From 24224e4f9d4dc2d16e1d4b1f1de6b2863d1f3962 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 20 Mar 2021 13:06:33 -0400 Subject: [PATCH 001/340] Add CurseForge release workflow --- .github/workflows/ci.yml | 21 +++------- .github/workflows/release.yml | 34 ++++++++++++++++ pom.xml | 2 +- scripts/generate_changelog.sh | 26 +++++++++--- scripts/get_spigot_versions.sh | 56 ++++++++++++++++++++++++++ scripts/install_spigot_dependencies.sh | 35 ++-------------- scripts/set_curseforge_env.sh | 42 +++++++++++++++++++ scripts/set_release_env.sh | 32 +++++++++++++++ 8 files changed, 195 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/get_spigot_versions.sh create mode 100644 scripts/set_curseforge_env.sh create mode 100644 scripts/set_release_env.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a73e107f..41ddcce3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,28 +60,17 @@ jobs: - name: Download Artifacts uses: actions/download-artifact@v2 - - name: Generate changelog - run: . scripts/generate_changelog.sh + - name: Set Release Variables + run: . scripts/set_release_env.sh - name: Create Release id: create-release - uses: actions/create-release@v1 + uses: softprops/action-gh-release@v0.1.5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} + name: ${{ env.VERSIONED_NAME }} body: ${{ env.GENERATED_CHANGELOG }} draft: true prerelease: false - - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1.0.2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create-release.outputs.upload_url }} - asset_path: ./OpenInv.jar - asset_name: OpenInv.jar - asset_content_type: application/java-archive \ No newline at end of file + files: ./OpenInv.jar diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..8901e47c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,34 @@ +name: Release to CurseForge + +on: + release: + types: [ released ] + +jobs: + curseforge_release: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Set CurseForge Variables + run: . scripts/set_curseforge_env.sh + + - name: Fetch Github Release Asset + uses: dsaltares/fetch-gh-release-asset@0.0.5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ github.event.release.id }} + file: OpenInv.jar + + - name: Create CurseForge Release + uses: itsmeow/curseforge-upload@v3 + with: + token: ${{ secrets.CURSEFORGE_TOKEN }} + project_id: 31432 + game_endpoint: minecraft + file_path: ./OpenInv.jar + changelog: ${{ github.event.release.body }} + display_name: ${{ github.event.release.name }} + game_versions: ${{ env.CURSEFORGE_MINECRAFT_VERSIONS }} + release-type: release diff --git a/pom.xml b/pom.xml index 6882a320..6362a211 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ com.lishid openinvparent - OpenInvParent + OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ 4.1.6-SNAPSHOT diff --git a/scripts/generate_changelog.sh b/scripts/generate_changelog.sh index 72c823d9..8aa0ec77 100644 --- a/scripts/generate_changelog.sh +++ b/scripts/generate_changelog.sh @@ -15,14 +15,15 @@ # along with this program. If not, see . # -# A script for generating a changelog from Git. -# # Note that this script is designed for use in GitHub Actions, and is not # particularly robust nor configurable. Run from project parent directory. # Query GitHub for the username of the given email address. # Falls through to the given author name. -lookup_email_username() { +function lookup_email_username() { + # Ensure fallthrough is set + ${2:?Must provide email and username as parameters, i.e. 'lookup_email_username admin@example.com Admin'} + lookup=$(curl -G --data-urlencode "q=$1 in:email" https://api.github.com/search/users -H 'Accept: application/vnd.github.v3+json' | grep '"login":' | sed -e 's/^.*": "//g' -e 's/",.*$//g') if [[ $lookup ]]; then @@ -32,10 +33,23 @@ lookup_email_username() { fi } +# Get a pretty list of supported Minecraft versions +function get_minecraft_versions() { + versions=$(. ./scripts/get_spigot_versions.sh) + + for version in "${versions[@]}"; do + # Append comma if variable is set, then append version + minecraft_versions="${minecraft_versions:+${minecraft_versions},}${version%%-R*}" + done + + echo "${minecraft_versions}" +} + # Use formatted log to pull authors list authors_raw=$(git log --pretty=format:"%ae|%an" "$(git describe --tags --abbrev=0 @^)"..@) readarray -t authors <<<"$authors_raw" +# Use associative array to map email to author name declare -A author_data for author in "${authors[@]}"; do @@ -55,7 +69,7 @@ for author in "${authors[@]}"; do done # Fetch actual formatted changelog -changelog=$(git log --pretty=format:"%s (%h) - %ae" "$(git describe --tags --abbrev=0 @^)"..@) +changelog=$(git log --pretty=format:"* %s (%h) - %ae" "$(git describe --tags --abbrev=0 @^)"..@) for author_email in "${!author_data[@]}"; do # Ignore case when matching @@ -64,4 +78,6 @@ for author_email in "${!author_data[@]}"; do changelog=${changelog//$author_email/${author_data[$author_email]}} done -echo "GENERATED_CHANGELOG<> "$GITHUB_ENV" +minecraft_versions=$(get_minecraft_versions) + +printf "## Supported Minecraft versions\n%s\n\n##Changelog\n%s" "${minecraft_versions}" "${changelog}" diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh new file mode 100644 index 00000000..252291d4 --- /dev/null +++ b/scripts/get_spigot_versions.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Copyright (C) 2011-2021 lishid. All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# Note that this script is designed for use in GitHub Actions, and is not +# particularly robust nor configurable. Run from project parent directory. + +# Use a nameref as a cache - maven evaluation is pretty slow. +# Re-calling the script and relying on it to handle caching is way easier than passing around info. +declare -a spigot_versions + +# We don't care about concatenation - either it's not null and we return or it's null and we instantiate. +# shellcheck disable=SC2199 +if [[ ${spigot_versions[@]} ]]; then + for spigot_version in "${spigot_versions[@]}"; do + echo "$spigot_version" + done + return +fi + +# Pull Spigot dependency information from Maven. +modules=$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all -pl internal | grep -oP '(?<=)(.*)(?=<\/string>)') + +declare -n versions="spigot_versions" + +for module in "${modules[@]}"; do + # Get number of dependencies declared in pom of specified internal module. + max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl internal/"$module" | grep -c "") + + for ((i=0; i < max_index; i++)); do + # Get artifactId of dependency. + artifact_id=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].artifactId -q -DforceStdout -P all -pl internal/"$module") + + # Ensure dependency is Spigot. + if [[ "$artifact_id" == spigot ]]; then + # Get Spigot version. + spigot_version=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].version -q -DforceStdout -P all -pl internal/"$module") + versions+=("$spigot_version") + echo "$spigot_version" + break + fi + done +done diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot_dependencies.sh index 09e93e16..02256693 100644 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot_dependencies.sh @@ -15,39 +15,12 @@ # along with this program. If not, see . # -# A script for installing required Spigot versions. -# -# Note that this script is designed for use in GitHub Actions, and is -# not particularly robust nor configurable. -# In its current state, the script must be run from OpenInv's parent -# project directory and will always install BuildTools to ~/buildtools. +# Note that this script is designed for use in GitHub Actions, and is not +# particularly robust nor configurable. Run from project parent directory. buildtools_dir=~/buildtools buildtools=$buildtools_dir/BuildTools.jar -get_spigot_versions () { - # Get all submodules of internal module - modules=$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all -pl internal | grep -oP '(?<=)(.*)(?=<\/string>)') - for module in "${modules[@]}"; do - - # Get number of dependencies declared in pom of specified internal module - max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl internal/"$module" | grep -c "") - - for ((i=0; i < max_index; i++)); do - # Get artifactId of dependency - artifact_id=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].artifactId -q -DforceStdout -P all -pl internal/"$module") - - # Ensure dependency is spigot - if [[ "$artifact_id" == spigot ]]; then - # Get spigot version - spigot_version=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].version -q -DforceStdout -P all -pl internal/"$module") - echo "$spigot_version" - break - fi - done - done -} - get_buildtools () { if [[ -d $buildtools_dir && -f $buildtools ]]; then return @@ -57,7 +30,7 @@ get_buildtools () { wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools } -versions=$(get_spigot_versions) +versions=$(. ./scripts/get_spigot_versions.sh) echo Found Spigot dependencies: "$versions" for version in "${versions[@]}"; do @@ -66,7 +39,7 @@ for version in "${versions[@]}"; do mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version" -q -o || exit_code=$? if [ $exit_code -ne 0 ]; then echo Installing missing Spigot version "$version" - revision=$(echo "$version" | grep -oP '(\d+\.\d+(\.\d+)?)(?=-R[0-9\.]+-SNAPSHOT)') + revision=${version//-R.*/} get_buildtools java -jar $buildtools -rev "$revision" else diff --git a/scripts/set_curseforge_env.sh b/scripts/set_curseforge_env.sh new file mode 100644 index 00000000..08940fa1 --- /dev/null +++ b/scripts/set_curseforge_env.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Copyright (C) 2011-2021 lishid. All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# Note that this script is designed for use in GitHub Actions, and is not +# particularly robust nor configurable. Run from project parent directory. + +# Parse Spigot dependency information into major Minecraft versions +function get_curseforge_minecraft_versions() { + versions=$(. ./scripts/get_spigot_versions.sh) + + for version in "${versions[@]}"; do + # Parse Minecraft major version + version="${version%[.-]"${version#*.*[.-]}"}" + + # Skip already listed versions + if [[ "$minecraft_versions" =~ "$version"($|,) ]]; then + continue + fi + + # Append comma if variable is set, then append version + minecraft_versions="${minecraft_versions:+${minecraft_versions},}Minecraft ${version}" + done + + echo "${minecraft_versions}" +} + +minecraft_versions=$(get_curseforge_minecraft_versions) +echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" diff --git a/scripts/set_release_env.sh b/scripts/set_release_env.sh new file mode 100644 index 00000000..3d850dcc --- /dev/null +++ b/scripts/set_release_env.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright (C) 2011-2021 lishid. All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# Note that this script is designed for use in GitHub Actions, and is not +# particularly robust nor configurable. Run from project parent directory. + +# Get a pretty string of the project's name and version +# Disable SC warning about variable expansion for this function - those are Maven variables. +# shellcheck disable=SC2016 +function get_versioned_name() { + mvn -q -Dexec.executable=echo -Dexec.args='${project.name} ${project.version}' --non-recursive exec:exec +} + +# Set GitHub environmental variables +echo "VERSIONED_NAME=$(get_versioned_name)" >> "$GITHUB_ENV" + +changelog="$(. ./scripts/generate_changelog.sh)" +printf "GENERATED_CHANGELOG<> "$GITHUB_ENV" From 15ee6ef9a5b6f42c721d893edeb6e161086ab54e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 20 Mar 2021 13:19:33 -0400 Subject: [PATCH 002/340] Fix revision number --- scripts/install_spigot_dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot_dependencies.sh index 02256693..84fce3f9 100644 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot_dependencies.sh @@ -39,7 +39,7 @@ for version in "${versions[@]}"; do mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version" -q -o || exit_code=$? if [ $exit_code -ne 0 ]; then echo Installing missing Spigot version "$version" - revision=${version//-R.*/} + revision=${version%%-R*} get_buildtools java -jar $buildtools -rev "$revision" else From 18c7916d796fd0a3a5ea8de7c5023766e7ab4314 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 21 Mar 2021 07:56:51 -0400 Subject: [PATCH 003/340] Include release script --- scripts/tag_release.sh | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 scripts/tag_release.sh diff --git a/scripts/tag_release.sh b/scripts/tag_release.sh new file mode 100644 index 00000000..2e4d022a --- /dev/null +++ b/scripts/tag_release.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Copyright (C) 2011-2021 lishid. All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if [[ ! $1 ]]; then + echo "Please provide a version string." + return +fi + +version="$1" +snapshot="${version%.*}.$((${version##*.} + 1))-SNAPSHOT" + +mvn versions:set -DnewVersion="$version" + +git add . +git commit -S -m "Bump version to $version for release" +git tag -s "$version" -m "Release $version" + +mvn clean package -am -P all + +mvn versions:set -DnewVersion="$snapshot" + +git add . +git commit -S -m "Bump version to $snapshot for development" From e09e7c59c7457980b8e2f164aa7bf907190f0bbf Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 09:42:11 -0400 Subject: [PATCH 004/340] Fix release action --- .github/workflows/ci.yml | 14 +++++++------- .github/workflows/release.yml | 2 ++ scripts/generate_changelog.sh | 11 +++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41ddcce3..72f4c31a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,6 @@ name: OpenInv CI on: push: - create: - types: [tag] pull_request_target: jobs: @@ -51,18 +49,20 @@ jobs: release: name: Create Github Release needs: [ build ] - if: github.event_name == 'create' && github.event.ref_type == 'tag' + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set Release Variables + run: bash ./scripts/set_release_env.sh - name: Download Artifacts uses: actions/download-artifact@v2 - - name: Set Release Variables - run: . scripts/set_release_env.sh - - name: Create Release id: create-release uses: softprops/action-gh-release@v0.1.5 @@ -73,4 +73,4 @@ jobs: body: ${{ env.GENERATED_CHANGELOG }} draft: true prerelease: false - files: ./OpenInv.jar + files: ./dist/OpenInv.jar diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8901e47c..76eb6d9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Set CurseForge Variables run: . scripts/set_curseforge_env.sh diff --git a/scripts/generate_changelog.sh b/scripts/generate_changelog.sh index 8aa0ec77..81cddfb4 100644 --- a/scripts/generate_changelog.sh +++ b/scripts/generate_changelog.sh @@ -21,9 +21,6 @@ # Query GitHub for the username of the given email address. # Falls through to the given author name. function lookup_email_username() { - # Ensure fallthrough is set - ${2:?Must provide email and username as parameters, i.e. 'lookup_email_username admin@example.com Admin'} - lookup=$(curl -G --data-urlencode "q=$1 in:email" https://api.github.com/search/users -H 'Accept: application/vnd.github.v3+json' | grep '"login":' | sed -e 's/^.*": "//g' -e 's/",.*$//g') if [[ $lookup ]]; then @@ -45,8 +42,10 @@ function get_minecraft_versions() { echo "${minecraft_versions}" } +previous_tag=$(git describe --tags --abbrev=0 @^) + # Use formatted log to pull authors list -authors_raw=$(git log --pretty=format:"%ae|%an" "$(git describe --tags --abbrev=0 @^)"..@) +authors_raw=$(git log --pretty=format:"%ae|%an" "$previous_tag"..@) readarray -t authors <<<"$authors_raw" # Use associative array to map email to author name @@ -69,7 +68,7 @@ for author in "${authors[@]}"; do done # Fetch actual formatted changelog -changelog=$(git log --pretty=format:"* %s (%h) - %ae" "$(git describe --tags --abbrev=0 @^)"..@) +changelog=$(git log --pretty=format:"* %s (%h) - %ae" "$previous_tag"..@) for author_email in "${!author_data[@]}"; do # Ignore case when matching @@ -80,4 +79,4 @@ done minecraft_versions=$(get_minecraft_versions) -printf "## Supported Minecraft versions\n%s\n\n##Changelog\n%s" "${minecraft_versions}" "${changelog}" +printf "## Supported Minecraft versions\n%s\n\n## Changelog\n%s" "${minecraft_versions}" "${changelog}" From 0e3bdb8741caba032ea826f7bbd109d37016fc5b Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 10:21:09 -0400 Subject: [PATCH 005/340] Convert to HTML for CF --- .github/workflows/release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76eb6d9f..b607b905 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,12 @@ jobs: version: ${{ github.event.release.id }} file: OpenInv.jar + - name: Convert to HTML + id: convert_to_html + uses: lifepal/markdown-to-html@v1.2 + with: + text: ${{ github.event.release.body }} + - name: Create CurseForge Release uses: itsmeow/curseforge-upload@v3 with: @@ -30,7 +36,7 @@ jobs: project_id: 31432 game_endpoint: minecraft file_path: ./OpenInv.jar - changelog: ${{ github.event.release.body }} + changelog: ${{ steps.convert_to_html.outputs.html }} display_name: ${{ github.event.release.name }} game_versions: ${{ env.CURSEFORGE_MINECRAFT_VERSIONS }} release-type: release From ae6c3bd292acd9946d86518d11d16e677ccf7689 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 10:24:48 -0400 Subject: [PATCH 006/340] Move release location --- .../src/main/java/com/lishid/openinv/util/InternalAccessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index fb245521..236d0cf3 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -84,7 +84,7 @@ public String getReleasesLink() { return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; case "v1_16_R3": default: - return "https://github.com/lishid/OpenInv/releases"; + return "https://github.com/Jikoo/OpenInv/releases"; } } From ccc6f4b4a6bb531b9e93bf070e30ab34cc59c355 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 11:50:56 -0400 Subject: [PATCH 007/340] Fix CF release --- .github/workflows/release.yml | 2 +- scripts/set_curseforge_env.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b607b905..a850dc33 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,4 +39,4 @@ jobs: changelog: ${{ steps.convert_to_html.outputs.html }} display_name: ${{ github.event.release.name }} game_versions: ${{ env.CURSEFORGE_MINECRAFT_VERSIONS }} - release-type: release + release_type: release diff --git a/scripts/set_curseforge_env.sh b/scripts/set_curseforge_env.sh index 08940fa1..dcd8b85c 100644 --- a/scripts/set_curseforge_env.sh +++ b/scripts/set_curseforge_env.sh @@ -39,4 +39,4 @@ function get_curseforge_minecraft_versions() { } minecraft_versions=$(get_curseforge_minecraft_versions) -echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" +echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV" From c440f618c90a67f8a90f57fc509a1f67db3702c5 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 11:52:04 -0400 Subject: [PATCH 008/340] Bump version to 4.1.6 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/pom.xml | 2 +- internal/v1_16_R3/pom.xml | 4 ++-- plugin/pom.xml | 4 ++-- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index cc34012f..e8bf585d 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.6-SNAPSHOT + 4.1.6 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 421f77ec..41283f95 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.6-SNAPSHOT + 4.1.6 openinvassembly diff --git a/internal/pom.xml b/internal/pom.xml index fd28dce6..fbacebbe 100644 --- a/internal/pom.xml +++ b/internal/pom.xml @@ -20,7 +20,7 @@ com.lishid openinvparent - 4.1.6-SNAPSHOT + 4.1.6 openinvinternal diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index 74905edd..37df5362 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -22,7 +22,7 @@ com.lishid openinvinternal - 4.1.6-SNAPSHOT + 4.1.6 openinvadapter1_16_R3 @@ -38,7 +38,7 @@ com.lishid openinvplugincore - 4.1.6-SNAPSHOT + 4.1.6 diff --git a/plugin/pom.xml b/plugin/pom.xml index 358b3962..827bcc57 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.6-SNAPSHOT + 4.1.6 openinvplugincore @@ -31,7 +31,7 @@ com.lishid openinvapi - 4.1.6-SNAPSHOT + 4.1.6 org.spigotmc diff --git a/pom.xml b/pom.xml index 6362a211..03077150 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.6-SNAPSHOT + 4.1.6 pom From 889c2ffdabac3059677350f2736ea631fc42c19f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 11:52:14 -0400 Subject: [PATCH 009/340] Bump version to 4.1.7-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/pom.xml | 2 +- internal/v1_16_R3/pom.xml | 4 ++-- plugin/pom.xml | 4 ++-- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index e8bf585d..5957eebb 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.6 + 4.1.7-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 41283f95..d982b92f 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.6 + 4.1.7-SNAPSHOT openinvassembly diff --git a/internal/pom.xml b/internal/pom.xml index fbacebbe..e5c73544 100644 --- a/internal/pom.xml +++ b/internal/pom.xml @@ -20,7 +20,7 @@ com.lishid openinvparent - 4.1.6 + 4.1.7-SNAPSHOT openinvinternal diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index 37df5362..3446dcdc 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -22,7 +22,7 @@ com.lishid openinvinternal - 4.1.6 + 4.1.7-SNAPSHOT openinvadapter1_16_R3 @@ -38,7 +38,7 @@ com.lishid openinvplugincore - 4.1.6 + 4.1.7-SNAPSHOT diff --git a/plugin/pom.xml b/plugin/pom.xml index 827bcc57..25ea5cea 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.6 + 4.1.7-SNAPSHOT openinvplugincore @@ -31,7 +31,7 @@ com.lishid openinvapi - 4.1.6 + 4.1.7-SNAPSHOT org.spigotmc diff --git a/pom.xml b/pom.xml index 03077150..b7b97b6d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.6 + 4.1.7-SNAPSHOT pom From 2a0e0d33d85d2f7559000d663346369076eb732b Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 11:56:39 -0400 Subject: [PATCH 010/340] Don't shade annotations --- api/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/api/pom.xml b/api/pom.xml index 5957eebb..9b5d9ba3 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -32,6 +32,7 @@ org.jetbrains annotations 17.0.0 + provided org.spigotmc From 2b563e0e1b6c3527419ed5d43bbcc6fbd50c26bd Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 12:07:53 -0400 Subject: [PATCH 011/340] Don't convert release notes to HTML We'll try this in another few months when there're enough changes to be worth it. --- .github/workflows/release.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a850dc33..b08f2801 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,12 +23,6 @@ jobs: version: ${{ github.event.release.id }} file: OpenInv.jar - - name: Convert to HTML - id: convert_to_html - uses: lifepal/markdown-to-html@v1.2 - with: - text: ${{ github.event.release.body }} - - name: Create CurseForge Release uses: itsmeow/curseforge-upload@v3 with: @@ -36,7 +30,7 @@ jobs: project_id: 31432 game_endpoint: minecraft file_path: ./OpenInv.jar - changelog: ${{ steps.convert_to_html.outputs.html }} + changelog: ${{ github.event.release.body }} display_name: ${{ github.event.release.name }} game_versions: ${{ env.CURSEFORGE_MINECRAFT_VERSIONS }} release_type: release From 7ab86f2af978e35f75625f9126a774f496c2d094 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 22 Mar 2021 12:20:58 -0400 Subject: [PATCH 012/340] Fix build --- api/pom.xml | 15 --------------- plugin/pom.xml | 6 ------ pom.xml | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 9b5d9ba3..dfba6c4a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -27,21 +27,6 @@ openinvapi OpenInvAPI - - - org.jetbrains - annotations - 17.0.0 - provided - - - org.spigotmc - spigot-api - 1.16.5-R0.1-SNAPSHOT - provided - - - diff --git a/plugin/pom.xml b/plugin/pom.xml index 25ea5cea..e926663d 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -33,12 +33,6 @@ openinvapi 4.1.7-SNAPSHOT - - org.spigotmc - spigot-api - 1.15.2-R0.1-SNAPSHOT - provided - diff --git a/pom.xml b/pom.xml index b7b97b6d..0101a38e 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,21 @@ + + + org.jetbrains + annotations + 17.0.0 + provided + + + org.spigotmc + spigot-api + 1.16.5-R0.1-SNAPSHOT + provided + + + From 5b186564850b248c2ca11e162f35a6d1f0967b8c Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 28 Mar 2021 11:10:39 -0400 Subject: [PATCH 013/340] Fix incorrect player name in title Closes #13 --- .../java/com/lishid/openinv/internal/OpenInventoryView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index 8fc6f09d..dd3fd20d 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -17,6 +17,7 @@ package com.lishid.openinv.internal; import com.lishid.openinv.OpenInv; +import java.util.Objects; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -62,7 +63,7 @@ public OpenInventoryView(Player player, ISpecialInventory inventory, String titl @Override public @NotNull String getTitle() { if (title == null) { - HumanEntity owner = getPlayer(); + HumanEntity owner = (HumanEntity) Objects.requireNonNull(inventory.getBukkitInventory().getHolder()); String localTitle = OpenInv.getPlugin(OpenInv.class) .getLocalizedMessage( From 6c4818dfd954e97ee594af0588d65408abc94a32 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 28 Mar 2021 11:12:49 -0400 Subject: [PATCH 014/340] Add method to access owner Makes handling ISpecialInventory ownership much more consistent with a lot less spaghetti. --- .../com/lishid/openinv/internal/ISpecialInventory.java | 8 ++++++++ .../openinv/internal/v1_16_R3/SpecialEnderChest.java | 5 +++++ .../openinv/internal/v1_16_R3/SpecialPlayerInventory.java | 5 +++++ .../com/lishid/openinv/internal/OpenInventoryView.java | 3 +-- .../com/lishid/openinv/listeners/InventoryListener.java | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java index e6929c96..d53ae32b 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java @@ -16,6 +16,7 @@ package com.lishid.openinv.internal; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.NotNull; @@ -48,4 +49,11 @@ public interface ISpecialInventory { */ boolean isInUse(); + /** + * Gets the Player associated with this ISpecialInventory. + * + * @return the HumanEntity + */ + @NotNull HumanEntity getPlayer(); + } diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java index 7fe8beea..f5717ef4 100644 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java +++ b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java @@ -80,6 +80,11 @@ public void setPlayerOnline(@NotNull final Player player) { } } + @Override + public @NotNull Player getPlayer() { + return owner.getBukkitEntity(); + } + @Override public void update() { this.owner.getEnderChest().update(); diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java index ada345c1..cffc59e1 100644 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java +++ b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java @@ -212,6 +212,11 @@ public void setPlayerOffline() { this.playerOnline = false; } + @Override + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } + @Override public ItemStack splitStack(int i, final int j) { List list = this.items; diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index dd3fd20d..6a3fe841 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -17,7 +17,6 @@ package com.lishid.openinv.internal; import com.lishid.openinv.OpenInv; -import java.util.Objects; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -63,7 +62,7 @@ public OpenInventoryView(Player player, ISpecialInventory inventory, String titl @Override public @NotNull String getTitle() { if (title == null) { - HumanEntity owner = (HumanEntity) Objects.requireNonNull(inventory.getBukkitInventory().getHolder()); + HumanEntity owner = inventory.getPlayer(); String localTitle = OpenInv.getPlugin(OpenInv.class) .getLocalizedMessage( diff --git a/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java index eee66861..2b87c50e 100644 --- a/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java @@ -199,7 +199,7 @@ private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent ev } // Only specially handle actions in the player's own inventory. - return !event.getWhoClicked().equals(event.getView().getTopInventory().getHolder()); + return !event.getWhoClicked().equals(playerInventory.getPlayer()); } } From e3acb5384a5871ed986fb38fedd4b7de80f0a7c0 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 5 Apr 2021 19:12:45 -0400 Subject: [PATCH 015/340] Deprecate API for removed notification configuration --- api/src/main/java/com/lishid/openinv/IOpenInv.java | 12 ++++++++++-- plugin/src/main/java/com/lishid/openinv/OpenInv.java | 10 ---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index b4f4e8f5..2d18f653 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -232,16 +232,24 @@ public interface IOpenInv { * when a container is activated with AnyChest. * * @return true unless configured otherwise + * @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings. */ - boolean notifyAnyChest(); + @Deprecated + default boolean notifyAnyChest() { + return true; + } /** * Check the configuration value for whether or not OpenInv displays a notification to the user * when a container is activated with SilentChest. * * @return true unless configured otherwise + * @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings. */ - boolean notifySilentChest(); + @Deprecated + default boolean notifySilentChest() { + return true; + } /** * Mark a Player as no longer in use by a Plugin to allow OpenInv to remove it from the cache diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 81375843..a8ac3a1f 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -334,16 +334,6 @@ public void sendSystemMessage(@NotNull Player player, @NotNull String key) { } } - @Override - public boolean notifyAnyChest() { - return this.getConfig().getBoolean("notify.any-chest", true); - } - - @Override - public boolean notifySilentChest() { - return this.getConfig().getBoolean("notify.silent-chest", true); - } - @Override public void onDisable() { From 30425d2baa5c141705da21d8d83488fdf5bfa6ee Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 21 Apr 2021 14:35:22 -0400 Subject: [PATCH 016/340] Don't bother validating names Fixes problems with players from Geyser not being obtainable when using FloodGate. --- api/src/main/java/com/lishid/openinv/IOpenInv.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index 2d18f653..ae5c41ed 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -24,6 +24,7 @@ import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.StringMetric; import java.util.UUID; +import java.util.logging.Level; import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -153,10 +154,7 @@ public interface IOpenInv { if (Bukkit.getServer().isPrimaryThread()) { this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!"); this.getLogger().warning("This can cause the server to hang, potentially severely."); - this.getLogger().warning("Trace:"); - for (StackTraceElement element : new Throwable().fillInStackTrace().getStackTrace()) { - this.getLogger().warning(element.toString()); - } + this.getLogger().log(Level.WARNING, new Throwable("Current stack trace"), () -> "Current stack trace"); } OfflinePlayer player; @@ -172,11 +170,6 @@ public interface IOpenInv { // Not a UUID } - // Ensure name is valid if server is in online mode to avoid unnecessary searching - if (Bukkit.getServer().getOnlineMode() && !name.matches("[a-zA-Z0-9_]{3,16}")) { - return null; - } - player = Bukkit.getServer().getPlayerExact(name); if (player != null) { From bd207e948a353a3435961e4cd93f5ccfc27a870a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 21 Apr 2021 11:07:15 -0400 Subject: [PATCH 017/340] Simplify dependency management Use dependencyManagement for versioning shared dependencies Use pluginManagement for versioning and configuring plugins --- README.MD | 95 +++++-------- api/pom.xml | 18 ++- assembly/pom.xml | 2 +- assembly/src/assembly/reactor-uberjar.xml | 20 ++- internal/pom.xml | 42 ------ internal/v1_16_R3/pom.xml | 35 ++--- plugin/pom.xml | 25 +--- pom.xml | 160 +++++++++++++--------- scripts/get_spigot_versions.sh | 9 +- 9 files changed, 175 insertions(+), 231 deletions(-) delete mode 100644 internal/pom.xml diff --git a/README.MD b/README.MD index 66c183ff..3cf7cd92 100644 --- a/README.MD +++ b/README.MD @@ -15,48 +15,7 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi - **AnyContainer**: Open containers, even if blocked by ocelots or blocks. ## Commands - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CommandAliasesDescription
/openinv [player]oi, inv, openOpen a player's inventory. If unspecified, will select last player opened or own if none opened previously.
/openender [player]oeOpen a player's ender chest. If unspecified, will select last player opened or own if none opened previously.
/searchinv <item> [minAmount]siLists all online players that have a certain item in their inventory.
/searchender <item> [minAmount]seLists all online players that have a certain item in their ender chest.
/searchenchant <[enchantment] [MinLevel]>searchenchantsLists all online players with a specific enchantment.
/anycontainer [check]ac, anychestCheck or toggle the AnyContainer function, allowing opening blocked containers.
/silentcontainer [check]sc, silentchestCheck or toggle the SilentContainer function, allowing opening containers silently.
+See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands). ## Permissions @@ -143,8 +102,39 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi
## For Developers -To compile, the relevant Craftbukkit/Spigot jars must be installed in your local repository using the install plugin. -Ex: `mvn install:install-file -Dpackaging=jar -Dfile=spigot-1.8-R0.1-SNAPSHOT.jar -DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.8-R0.1-SNAPSHOT` + +### As a Dependency +The OpenInv API is available via [JitPack](https://jitpack.io/). +```xml + + + jitpack.io + https://jitpack.io + + +``` +```xml + + + com.github.jikoo.OpenInv + openinvapi + ${openinv.version} + + +``` + +### Compilation +To compile, the relevant Spigot jars must be installed in your local repository using the `install` plugin: +```shell +mvn install:install-file -Dpackaging=jar -Dfile=spigot-1.8-R0.1-SNAPSHOT.jar \ + -DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.8-R0.1-SNAPSHOT +``` +Note that BuildTools automatically installs produced files. If you use BuildTools to compile Spigot locally, you don't need to install it manually. +If you want to use Paper as a dependency, you can install it by executing PaperClip with the property `paperclip.install` set to true: +```shell +wget -O paperclip.jar https://papermc.io/api/v1/paper/1.16.5/latest/download +java -jar -Dpaperclip.install=true paperclip.jar +``` To compile for a single version, specify the NMS revision you are targeting: `mvn -pl -am clean install` @@ -153,20 +143,3 @@ To compile for a set of versions, you'll need to use a profile. The only provide For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html). The final file is `target/OpenInv.jar` - -## License -``` -Copyright (C) 2011-2020 lishid. All rights reserved. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -``` diff --git a/api/pom.xml b/api/pom.xml index dfba6c4a..a04210b9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -19,23 +19,29 @@ 4.0.0 - com.lishid openinvparent + com.lishid 4.1.7-SNAPSHOT openinvapi OpenInvAPI + + + annotations + org.jetbrains + + + spigot-api + org.spigotmc + + + maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - diff --git a/assembly/pom.xml b/assembly/pom.xml index d982b92f..51126a47 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -26,6 +26,7 @@ openinvassembly OpenInvAssembly + pom ../target @@ -34,7 +35,6 @@ maven-assembly-plugin - 3.2.0 reactor-uberjar diff --git a/assembly/src/assembly/reactor-uberjar.xml b/assembly/src/assembly/reactor-uberjar.xml index 36beb33e..afe5bfa6 100644 --- a/assembly/src/assembly/reactor-uberjar.xml +++ b/assembly/src/assembly/reactor-uberjar.xml @@ -14,9 +14,9 @@ ~ along with this program. If not, see . --> - + reactor-uberjar @@ -34,8 +34,18 @@ / true - - + + + + META-INF/** + + + + false diff --git a/internal/pom.xml b/internal/pom.xml deleted file mode 100644 index e5c73544..00000000 --- a/internal/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - 4.0.0 - - - com.lishid - openinvparent - 4.1.7-SNAPSHOT - - - openinvinternal - OpenInvInternal - - pom - - - - - all - - v1_16_R3 - - - - - - diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index 3446dcdc..69e95029 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -20,8 +20,9 @@ 4.0.0 + openinvparent com.lishid - openinvinternal + ../../pom.xml 4.1.7-SNAPSHOT @@ -30,44 +31,32 @@ - org.spigotmc spigot - 1.16.5-R0.1-SNAPSHOT + org.spigotmc provided + 1.16.5-R0.1-SNAPSHOT + openinvapi com.lishid + + openinvplugincore - 4.1.7-SNAPSHOT + com.lishid + + + annotations + org.jetbrains - org.apache.maven.plugins maven-shade-plugin - 3.2.2 - - true - - - - package - - shade - - - - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - diff --git a/plugin/pom.xml b/plugin/pom.xml index e926663d..1ae03f7e 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -19,8 +19,8 @@ 4.0.0 - com.lishid openinvparent + com.lishid 4.1.7-SNAPSHOT @@ -29,9 +29,8 @@ - com.lishid openinvapi - 4.1.7-SNAPSHOT + com.lishid @@ -42,31 +41,13 @@ true + - org.apache.maven.plugins maven-shade-plugin - 3.2.2 - - true - - - - package - - shade - - - - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - diff --git a/pom.xml b/pom.xml index 0101a38e..a166257b 100644 --- a/pom.xml +++ b/pom.xml @@ -27,33 +27,32 @@ UTF-8 + 1.8 + 1.8 - - api - plugin - internal - assembly - - - - - all + + api + plugin + internal/v1_16_R3 + assembly + + + + + default - - all - true - + true + + api + plugin + assembly + - @@ -63,57 +62,84 @@ - - - org.jetbrains - annotations - 17.0.0 - provided - - - org.spigotmc - spigot-api - 1.16.5-R0.1-SNAPSHOT - provided - - + + + + annotations + org.jetbrains + provided + 20.1.0 + + + spigot-api + org.spigotmc + provided + 1.16.5-R0.1-SNAPSHOT + + + openinvapi + com.lishid + compile + 4.1.7-SNAPSHOT + + + openinvplugincore + com.lishid + compile + 4.1.7-SNAPSHOT + + + - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.2 - - - - *:* - - - META-INF/maven/** - - - - - - - package - - shade - - - - + + + + maven-shade-plugin + + + + + com.lishid:openinv* + + ** + + + + *:* + + + META-INF/MANIFEST.MF + + + + true + + + + package + + shade + + + + org.apache.maven.plugins + 3.2.4 + + + + maven-compiler-plugin + org.apache.maven.plugins + 3.8.1 + - - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - + + maven-assembly-plugin + org.apache.maven.plugins + 3.3.0 + + + diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh index 252291d4..64c2fea0 100644 --- a/scripts/get_spigot_versions.sh +++ b/scripts/get_spigot_versions.sh @@ -32,22 +32,23 @@ if [[ ${spigot_versions[@]} ]]; then fi # Pull Spigot dependency information from Maven. -modules=$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all -pl internal | grep -oP '(?<=)(.*)(?=<\/string>)') +# Since we only care about Spigot versions, only check modules in the folder internal. +readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all | grep -oP '(?<=)(internal/.*)(?=)')" declare -n versions="spigot_versions" for module in "${modules[@]}"; do # Get number of dependencies declared in pom of specified internal module. - max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl internal/"$module" | grep -c "") + max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl "$module" | grep -c "") for ((i=0; i < max_index; i++)); do # Get artifactId of dependency. - artifact_id=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].artifactId -q -DforceStdout -P all -pl internal/"$module") + artifact_id=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].artifactId -q -DforceStdout -P all -pl "$module") # Ensure dependency is Spigot. if [[ "$artifact_id" == spigot ]]; then # Get Spigot version. - spigot_version=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].version -q -DforceStdout -P all -pl internal/"$module") + spigot_version=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].version -q -DforceStdout -P all -pl "$module") versions+=("$spigot_version") echo "$spigot_version" break From 9e0ca479a5d69014b48d5ea714b22195d1fa3f6f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 26 May 2021 08:16:25 -0400 Subject: [PATCH 018/340] Fix Bukkit data not being loaded while offline Closes #16 --- .../lishid/openinv/internal/v1_16_R3/OpenPlayer.java | 11 ++++++++++- .../openinv/internal/v1_16_R3/PlayerDataManager.java | 6 ++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java index def3f960..ec87e3be 100644 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java +++ b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java @@ -32,15 +32,24 @@ public OpenPlayer(CraftServer server, EntityPlayer entity) { super(server, entity); } + @Override + public void loadData() { + // See CraftPlayer#loadData + NBTTagCompound loaded = this.server.getHandle().playerFileData.load(this.getHandle()); + if (loaded != null) { + readExtraData(loaded); + } + } + @Override public void saveData() { - super.saveData(); EntityPlayer player = this.getHandle(); // See net.minecraft.server.WorldNBTStorage#save(EntityPlayer) try { WorldNBTStorage worldNBTStorage = player.server.getPlayerList().playerFileData; NBTTagCompound playerData = player.save(new NBTTagCompound()); + setExtraData(playerData); if (!isOnline()) { // Special case: save old vehicle data diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java index 5a29ceb5..35cae336 100644 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java +++ b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java @@ -57,8 +57,7 @@ public PlayerDataManager() { } } - @NotNull - public static EntityPlayer getHandle(final Player player) { + public static @NotNull EntityPlayer getHandle(final Player player) { if (player instanceof CraftPlayer) { return ((CraftPlayer) player).getHandle(); } @@ -78,9 +77,8 @@ public static EntityPlayer getHandle(final Player player) { return nmsPlayer; } - @Nullable @Override - public Player loadPlayer(@NotNull final OfflinePlayer offline) { + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { // Ensure player has data if (!offline.hasPlayedBefore()) { return null; From 0f417014290c3d686a242a9d04909ca6ccfe8849 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 26 May 2021 08:17:11 -0400 Subject: [PATCH 019/340] Update annotations --- plugin/pom.xml | 8 ++++++++ pom.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/plugin/pom.xml b/plugin/pom.xml index 1ae03f7e..6a164c5f 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -32,6 +32,14 @@ openinvapi com.lishid + + annotations + org.jetbrains + + + spigot-api + org.spigotmc + diff --git a/pom.xml b/pom.xml index a166257b..d5633302 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ annotations org.jetbrains provided - 20.1.0 + 21.0.1 spigot-api From 2cfc55813b5b45183028f1dbd18982ed567966d9 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 29 May 2021 09:51:25 -0400 Subject: [PATCH 020/340] Bump version to 4.1.7 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_16_R3/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index a04210b9..bd10fb74 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.7-SNAPSHOT + 4.1.7 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 51126a47..5dfc7454 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.7-SNAPSHOT + 4.1.7 openinvassembly diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index 69e95029..d2e8f39a 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.7-SNAPSHOT + 4.1.7 openinvadapter1_16_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index 6a164c5f..e48aa2f3 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.7-SNAPSHOT + 4.1.7 openinvplugincore diff --git a/pom.xml b/pom.xml index d5633302..7c102a62 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.7-SNAPSHOT + 4.1.7 pom @@ -80,13 +80,13 @@ openinvapi com.lishid compile - 4.1.7-SNAPSHOT + 4.1.7 openinvplugincore com.lishid compile - 4.1.7-SNAPSHOT + 4.1.7 From 4d800361d8a4f4dbd5332ec3680a5c51cbbab685 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 29 May 2021 09:51:39 -0400 Subject: [PATCH 021/340] Bump version to 4.1.8-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_16_R3/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index bd10fb74..8182f602 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.7 + 4.1.8-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 5dfc7454..03907104 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.7 + 4.1.8-SNAPSHOT openinvassembly diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index d2e8f39a..673afbb9 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.7 + 4.1.8-SNAPSHOT openinvadapter1_16_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index e48aa2f3..3bf33932 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.7 + 4.1.8-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 7c102a62..8940b5fe 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.7 + 4.1.8-SNAPSHOT pom @@ -80,13 +80,13 @@ openinvapi com.lishid compile - 4.1.7 + 4.1.8-SNAPSHOT
openinvplugincore com.lishid compile - 4.1.7 + 4.1.8-SNAPSHOT From 9a2b379a649fd22fe5e84f7d3e138effc167a648 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 8 Jun 2021 11:38:26 -0400 Subject: [PATCH 022/340] Escape changelog for YAML With 1.17 releasing soon, we'll get to see if this was the issue very shortly. Worst case scenario the yaml is still messed up, but since the changelog is now the last element, unless it's so badly malformed that the entire action can't run the file should still upload even if the changelog does not. --- .github/workflows/release.yml | 13 +++++++------ scripts/set_curseforge_env.sh | 13 +++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b08f2801..5b78047e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,9 +13,6 @@ jobs: with: fetch-depth: 0 - - name: Set CurseForge Variables - run: . scripts/set_curseforge_env.sh - - name: Fetch Github Release Asset uses: dsaltares/fetch-gh-release-asset@0.0.5 with: @@ -23,6 +20,9 @@ jobs: version: ${{ github.event.release.id }} file: OpenInv.jar + - name: Set CurseForge Variables + run: . scripts/set_curseforge_env.sh "${{ github.event.release.body }}" + - name: Create CurseForge Release uses: itsmeow/curseforge-upload@v3 with: @@ -30,7 +30,8 @@ jobs: project_id: 31432 game_endpoint: minecraft file_path: ./OpenInv.jar - changelog: ${{ github.event.release.body }} - display_name: ${{ github.event.release.name }} - game_versions: ${{ env.CURSEFORGE_MINECRAFT_VERSIONS }} + display_name: "${{ github.event.release.name }}" + game_versions: "${{ env.CURSEFORGE_MINECRAFT_VERSIONS }}" release_type: release + changelog_type: markdown + changelog: "${{ env.CURSEFORGE_CHANGELOG }}" diff --git a/scripts/set_curseforge_env.sh b/scripts/set_curseforge_env.sh index dcd8b85c..f21dd406 100644 --- a/scripts/set_curseforge_env.sh +++ b/scripts/set_curseforge_env.sh @@ -38,5 +38,18 @@ function get_curseforge_minecraft_versions() { echo "${minecraft_versions}" } +# Modify provided changelog to not break when inserted into yaml file. +function get_yaml_safe_changelog() { + changelog=$1 + # Since we're using a flow scalar, newlines need to be doubled. + echo "${changelog// +/ + +}" +} + minecraft_versions=$(get_curseforge_minecraft_versions) echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV" + +changelog=$(get_yaml_safe_changelog "$1") +printf "CURSEFORGE_CHANGELOG<> "$GITHUB_ENV" \ No newline at end of file From 9fccea60f765cff6c929ac64bcf194483f4ca689 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Jun 2021 10:50:36 -0400 Subject: [PATCH 023/340] Update copyright --- api/pom.xml | 2 +- api/src/main/java/com/lishid/openinv/IOpenInv.java | 2 +- .../java/com/lishid/openinv/internal/IAnySilentContainer.java | 2 +- .../main/java/com/lishid/openinv/internal/IInventoryAccess.java | 2 +- .../java/com/lishid/openinv/internal/ISpecialEnderChest.java | 2 +- .../java/com/lishid/openinv/internal/ISpecialInventory.java | 2 +- .../com/lishid/openinv/internal/ISpecialPlayerInventory.java | 2 +- api/src/main/java/com/lishid/openinv/util/InventoryAccess.java | 2 +- api/src/main/java/com/lishid/openinv/util/StringMetric.java | 2 +- assembly/pom.xml | 2 +- assembly/src/assembly/reactor-uberjar.xml | 2 +- internal/v1_16_R3/pom.xml | 2 +- .../lishid/openinv/internal/v1_16_R3/AnySilentContainer.java | 2 +- .../com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java | 2 +- .../com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java | 2 +- .../openinv/internal/v1_16_R3/SpecialPlayerInventory.java | 2 +- plugin/pom.xml | 2 +- plugin/src/main/java/com/lishid/openinv/OpenInv.java | 2 +- .../com/lishid/openinv/commands/ContainerSettingCommand.java | 2 +- .../main/java/com/lishid/openinv/commands/OpenInvCommand.java | 2 +- .../com/lishid/openinv/commands/SearchContainerCommand.java | 2 +- .../java/com/lishid/openinv/commands/SearchEnchantCommand.java | 2 +- .../main/java/com/lishid/openinv/commands/SearchInvCommand.java | 2 +- .../java/com/lishid/openinv/internal/IPlayerDataManager.java | 2 +- .../java/com/lishid/openinv/listeners/InventoryListener.java | 2 +- .../main/java/com/lishid/openinv/listeners/PlayerListener.java | 2 +- .../main/java/com/lishid/openinv/listeners/PluginListener.java | 2 +- plugin/src/main/java/com/lishid/openinv/util/Cache.java | 2 +- plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java | 2 +- .../src/main/java/com/lishid/openinv/util/InternalAccessor.java | 2 +- .../src/main/java/com/lishid/openinv/util/LanguageManager.java | 2 +- plugin/src/main/java/com/lishid/openinv/util/Permissions.java | 2 +- plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java | 2 +- pom.xml | 2 +- 34 files changed, 34 insertions(+), 34 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 8182f602..7adc2daf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -1,5 +1,5 @@ + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 4.1.8-SNAPSHOT + + + openinvadapter1_17_R1 + OpenInvAdapter1_17_R1 + + + 16 + 16 + + + + + spigot + org.spigotmc + provided + 1.17-R0.1-SNAPSHOT + + + openinvapi + com.lishid + + + openinvplugincore + com.lishid + + + annotations + org.jetbrains + + + + + + + maven-shade-plugin + + + false + + + + maven-compiler-plugin + + + + + diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java new file mode 100644 index 00000000..3f7085e3 --- /dev/null +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_17_R1; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IAnySilentContainer; +import java.lang.reflect.Field; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.core.BlockPosition; +import net.minecraft.network.chat.ChatMessage; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.server.level.PlayerInteractManager; +import net.minecraft.world.ITileInventory; +import net.minecraft.world.InventoryLargeChest; +import net.minecraft.world.TileInventory; +import net.minecraft.world.entity.player.EntityHuman; +import net.minecraft.world.entity.player.PlayerInventory; +import net.minecraft.world.inventory.Container; +import net.minecraft.world.inventory.ContainerChest; +import net.minecraft.world.inventory.Containers; +import net.minecraft.world.inventory.InventoryEnderChest; +import net.minecraft.world.level.EnumGamemode; +import net.minecraft.world.level.World; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.BlockBarrel; +import net.minecraft.world.level.block.BlockChest; +import net.minecraft.world.level.block.BlockChestTrapped; +import net.minecraft.world.level.block.BlockShulkerBox; +import net.minecraft.world.level.block.entity.TileEntity; +import net.minecraft.world.level.block.entity.TileEntityChest; +import net.minecraft.world.level.block.entity.TileEntityEnderChest; +import net.minecraft.world.level.block.entity.TileEntityLootable; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.block.state.properties.BlockPropertyChestType; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.Barrel; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.EnderChest; +import org.bukkit.block.ShulkerBox; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.type.Chest; +import org.bukkit.entity.Cat; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.bukkit.util.BoundingBox; +import org.jetbrains.annotations.NotNull; + +public class AnySilentContainer implements IAnySilentContainer { + + private Field playerInteractManagerGamemode; + + public AnySilentContainer() { + try { + this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("b"); + this.playerInteractManagerGamemode.setAccessible(true); + } catch (NoSuchFieldException | SecurityException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to directly write player gamemode! SilentChest will fail."); + logger.log(Level.WARNING, "Error obtaining gamemode field", e); + } + } + + @Override + public boolean isAnySilentContainer(@NotNull final org.bukkit.block.Block bukkitBlock) { + if (bukkitBlock.getType() == Material.ENDER_CHEST) { + return true; + } + BlockState state = bukkitBlock.getState(); + return state instanceof org.bukkit.block.Chest + || state instanceof org.bukkit.block.ShulkerBox + || state instanceof org.bukkit.block.Barrel; + } + + @Override + public boolean isAnyContainerNeeded(@NotNull final Player p, @NotNull final org.bukkit.block.Block block) { + BlockState blockState = block.getState(); + + // Barrels do not require AnyContainer. + if (blockState instanceof Barrel) { + return false; + } + + // Enderchests require a non-occluding block on top to open. + if (blockState instanceof EnderChest) { + return block.getRelative(0, 1, 0).getType().isOccluding(); + } + + // Shulker boxes require 1/2 a block clear in the direction they open. + if (blockState instanceof ShulkerBox) { + BoundingBox boundingBox = block.getBoundingBox(); + if (boundingBox.getVolume() > 1) { + // Shulker box is already open. + return false; + } + + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Directional directional)) { + // Shouldn't be possible. Just in case, demand AnyChest. + return true; + } + + BlockFace face = directional.getFacing(); + boundingBox.shift(face.getDirection()); + // Return whether or not bounding boxes overlap. + return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox); + } + + if (!(blockState instanceof org.bukkit.block.Chest)) { + return false; + } + + if (isBlockedChest(block)) { + return true; + } + + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Chest chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) { + return false; + } + + int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4; + BlockFace relativeFace = BlockFace.values()[ordinal]; + org.bukkit.block.Block relative = block.getRelative(relativeFace); + + if (relative.getType() != block.getType()) { + return false; + } + + BlockData relativeData = relative.getBlockData(); + if (!(relativeData instanceof Chest relativeChest)) { + return false; + } + + if (relativeChest.getFacing() != chest.getFacing() + || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { + return false; + } + + return isBlockedChest(relative); + } + + private boolean isBlockedChest(org.bukkit.block.Block block) { + org.bukkit.block.Block relative = block.getRelative(0, 1, 0); + return relative.getType().isOccluding() + || block.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0; + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + final World world = player.getWorld(); + final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final TileEntity tile = world.getTileEntity(blockPosition); + + if (tile == null) { + return false; + } + + if (tile instanceof TileEntityEnderChest) { + // Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest + InventoryEnderChest enderChest = player.getEnderChest(); + enderChest.a((TileEntityEnderChest) tile); + player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> { + Containers containers = PlayerDataManager.getContainers(enderChest.getSize()); + int rows = enderChest.getSize() / 9; + return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows); + }, new ChatMessage("container.enderchest"))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(tile instanceof ITileInventory tileInventory)) { + return false; + } + + IBlockData blockData = world.getType(blockPosition); + Block block = blockData.getBlock(); + + if (block instanceof BlockChest) { + + BlockPropertyChestType chestType = blockData.get(BlockChest.c); + + if (chestType != BlockPropertyChestType.a) { + + BlockPosition adjacentBlockPosition = blockPosition.shift(BlockChest.h(blockData)); + IBlockData adjacentBlockData = world.getType(adjacentBlockPosition); + + if (adjacentBlockData.getBlock() == block) { + + BlockPropertyChestType adjacentChestType = adjacentBlockData.get(BlockChest.c); + + if (adjacentChestType != BlockPropertyChestType.a && chestType != adjacentChestType + && adjacentBlockData.get(BlockChest.b) == blockData.get(BlockChest.b)) { + + TileEntity adjacentTile = world.getTileEntity(adjacentBlockPosition); + + if (adjacentTile instanceof TileEntityChest && tileInventory instanceof TileEntityChest) { + TileEntityChest rightChest = chestType == BlockPropertyChestType.c ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile; + TileEntityChest leftChest = chestType == BlockPropertyChestType.c ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory); + + if (silentchest && (rightChest.g != null || leftChest.g != null)) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + tileInventory = new ITileInventory() { + public Container createMenu(int containerCounter, PlayerInventory playerInventory, EntityHuman entityHuman) { + leftChest.d(playerInventory.l); + rightChest.d(playerInventory.l); + return ContainerChest.b(containerCounter, playerInventory, new InventoryLargeChest(rightChest, leftChest)); + } + + public IChatBaseComponent getScoreboardDisplayName() { + if (leftChest.hasCustomName()) { + return leftChest.getScoreboardDisplayName(); + } + if (rightChest.hasCustomName()) { + return rightChest.getScoreboardDisplayName(); + } + return new ChatMessage("container.chestDouble"); + } + }; + } + } + } + } + + if (block instanceof BlockChestTrapped) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof BlockShulkerBox) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BlockBarrel) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.d.getGameMode() == EnumGamemode.d) { + player.openContainer(tileInventory); + return true; + } + + // SilentChest requires access to setting players' gamemode directly. + if (this.playerInteractManagerGamemode == null) { + return false; + } + + if (tile instanceof TileEntityLootable lootable) { + if (lootable.g != null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + EnumGamemode gamemode = player.d.getGameMode(); + this.forceGameMode(player, EnumGamemode.d); + player.openContainer(tileInventory); + this.forceGameMode(player, gamemode); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.playerInteractManagerGamemode == null) { + return; + } + + InventoryView view = bukkitPlayer.getOpenInventory(); + switch (view.getType()) { + case CHEST: + case ENDER_CHEST: + case SHULKER_BOX: + case BARREL: + break; + default: + return; + } + + EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + EnumGamemode gamemode = player.d.getGameMode(); + this.forceGameMode(player, EnumGamemode.d); + + // See EntityPlayer#closeInventory - can't call or we'd recursively deactivate. + player.bV.b(player); + player.bU.a(player.bV); + player.bV = player.bU; + + // Revert forced game mode. + this.forceGameMode(player, gamemode); + } + + private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) { + if (this.playerInteractManagerGamemode == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.playerInteractManagerGamemode.setAccessible(true); + this.playerInteractManagerGamemode.set(player.d, gameMode); + // TODO: may need additional calls to update abilities to prevent container sound + animation + // gameMode.a(player.getAbilities()); + // player.updateAbilities(); + // should be the fix if it doesn't work as-is + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java new file mode 100644 index 00000000..15ca1b75 --- /dev/null +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_17_R1; + +import java.io.File; +import java.io.FileOutputStream; +import net.minecraft.nbt.NBTCompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.level.storage.WorldNBTStorage; +import org.apache.logging.log4j.LogManager; +import org.bukkit.craftbukkit.v1_17_R1.CraftServer; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; + +public class OpenPlayer extends CraftPlayer { + + public OpenPlayer(CraftServer server, EntityPlayer entity) { + super(server, entity); + } + + @Override + public void loadData() { + super.loadData(); + // See CraftPlayer#loadData + NBTTagCompound loaded = this.server.getHandle().r.load(this.getHandle()); + if (loaded != null) { + readExtraData(loaded); + } + } + + @Override + public void saveData() { + EntityPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.WorldNBTStorage#save(EntityHuman) + try { + WorldNBTStorage worldNBTStorage = player.c.getPlayerList().r; + + NBTTagCompound playerData = player.save(new NBTTagCompound()); + setExtraData(playerData); + + if (!isOnline()) { + // Special case: save old vehicle data + NBTTagCompound oldData = worldNBTStorage.load(player); + + if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) { + // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) + playerData.set("RootVehicle", oldData.getCompound("RootVehicle")); + } + } + + File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp"); + File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat"); + + NBTCompressedStreamTools.a(playerData, new FileOutputStream(file)); + + if (file1.exists() && !file1.delete() || !file.renameTo(file1)) { + LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString()); + } + + } catch (Exception e) { + LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString()); + } + } + +} diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java new file mode 100644 index 00000000..618e5bad --- /dev/null +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_17_R1; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IPlayerDataManager; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.OpenInventoryView; +import com.mojang.authlib.GameProfile; +import java.lang.reflect.Field; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.network.chat.ChatComponentText; +import net.minecraft.network.protocol.game.PacketPlayOutOpenWindow; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.server.level.WorldServer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.inventory.Container; +import net.minecraft.world.inventory.Containers; +import net.minecraft.world.level.World; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_17_R1.CraftServer; +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.CraftContainer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerDataManager implements IPlayerDataManager { + + private @Nullable Field bukkitEntity; + + public PlayerDataManager() { + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + logger.log(Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull EntityPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + EntityPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } + + // Create a profile and entity to load the player data + // See net.minecraft.server.PlayerList#attemptLogin + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + WorldServer worldServer = server.getWorldServer(World.f); + + if (worldServer == null) { + return null; + } + + EntityPlayer entity = new EntityPlayer(server, worldServer, profile); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + // Get the bukkit entity + Player target = entity.getBukkitEntity(); + if (target != null) { + // Load data + target.loadData(); + } + // Return the entity + return target; + } + + void injectPlayer(EntityPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.c.server, player)); + } + + @NotNull + @Override + public Player inject(@NotNull Player player) { + try { + EntityPlayer nmsPlayer = getHandle(player); + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return player; + } + } + + @Nullable + @Override + public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + + EntityPlayer nmsPlayer = getHandle(player); + + if (nmsPlayer.b == null) { + return null; + } + + InventoryView view = getView(player, inventory); + + if (view == null) { + return player.openInventory(inventory.getBukkitInventory()); + } + + Container container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + @Override + public Containers getType() { + return getContainers(inventory.getBukkitInventory().getSize()); + } + }; + + container.setTitle(new ChatComponentText(view.getTitle())); + container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + + if (container == null) { + return null; + } + + nmsPlayer.b.sendPacket(new PacketPlayOutOpenWindow(nmsPlayer.bV.j, container.getType(), + new ChatComponentText(container.getBukkitView().getTitle()))); + nmsPlayer.bV = container; + nmsPlayer.initMenu(container); + + return container.getBukkitView(); + + } + + private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { + if (inventory instanceof SpecialEnderChest) { + return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); + } else if (inventory instanceof SpecialPlayerInventory) { + return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); + } else { + return null; + } + } + + static @NotNull Containers getContainers(int inventorySize) { + + return switch (inventorySize) { + case 9 -> Containers.a; + case 18 -> Containers.b; + case 36 -> Containers.d; // PLAYER + case 41, 45 -> Containers.e; + case 54 -> Containers.f; + default -> Containers.c; // 9x3 + }; + } + + @Override + public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java new file mode 100644 index 00000000..6a6a5920 --- /dev/null +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_17_R1; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.ContainerUtil; +import net.minecraft.world.IInventoryListener; +import net.minecraft.world.entity.player.AutoRecipeStackManager; +import net.minecraft.world.entity.player.EntityHuman; +import net.minecraft.world.inventory.InventoryEnderChest; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.TileEntityEnderChest; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialEnderChest extends InventoryEnderChest implements ISpecialEnderChest { + + private final CraftInventory inventory; + private EntityPlayer owner; + private NonNullList c; + private boolean playerOnline; + + public SpecialEnderChest(final Player player, final Boolean online) { + super(PlayerDataManager.getHandle(player)); + this.inventory = new CraftInventory(this); + this.owner = PlayerDataManager.getHandle(player); + this.playerOnline = online; + this.c = this.owner.getEnderChest().c; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return inventory; + } + + @Override + public boolean isInUse() { + return !this.getViewers().isEmpty(); + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public void setPlayerOnline(@NotNull final Player player) { + if (!this.playerOnline) { + try { + this.owner = PlayerDataManager.getHandle(player); + InventoryEnderChest enderChest = owner.getEnderChest(); + for (int i = 0; i < enderChest.getSize(); ++i) { + enderChest.setItem(i, this.c.get(i)); + } + this.c = enderChest.c; + enderChest.transaction.addAll(this.transaction); + } catch (Exception ignored) {} + this.playerOnline = true; + } + } + + @Override + public @NotNull Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public void update() { + this.owner.getEnderChest().update(); + } + + @Override + public List getContents() { + return this.c; + } + + @Override + public void onOpen(CraftHumanEntity who) { + super.onOpen(who); + this.owner.getEnderChest().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + super.onClose(who); + this.owner.getEnderChest().onClose(who); + } + + @Override + public List getViewers() { + return this.owner.getEnderChest().getViewers(); + } + + @Override + public boolean a(EntityHuman entityhuman) { + return true; + } + + @Override + public void a(TileEntityEnderChest tileentityenderchest) { + this.owner.getEnderChest().a(tileentityenderchest); + } + + @Override + public boolean b(TileEntityEnderChest tileentityenderchest) { + return this.owner.getEnderChest().b(tileentityenderchest); + } + + @Override + public int getMaxStackSize() { + return this.owner.getEnderChest().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int i) { + this.owner.getEnderChest().setMaxStackSize(i); + } + + @Override + public InventoryHolder getOwner() { + return this.owner.getEnderChest().getOwner(); + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void a(IInventoryListener iinventorylistener) { + this.owner.getEnderChest().a(iinventorylistener); + } + + @Override + public void b(IInventoryListener iinventorylistener) { + this.owner.getEnderChest().b(iinventorylistener); + } + + @Override + public ItemStack getItem(int i) { + return i >= 0 && i < this.c.size() ? this.c.get(i) : ItemStack.b; + } + + @Override + public ItemStack splitStack(int i, int j) { + ItemStack itemstack = ContainerUtil.a(this.c, i, j); + if (!itemstack.isEmpty()) { + this.update(); + } + + return itemstack; + } + + @Override + public ItemStack a(ItemStack itemstack) { + ItemStack itemstack1 = itemstack.cloneItemStack(); + this.d(itemstack1); + if (itemstack1.isEmpty()) { + return ItemStack.b; + } else { + this.c(itemstack1); + return itemstack1.isEmpty() ? ItemStack.b : itemstack1; + } + } + + private void c(ItemStack itemstack) { + for(int i = 0; i < this.getSize(); ++i) { + ItemStack itemstack1 = this.getItem(i); + if (itemstack1.isEmpty()) { + this.setItem(i, itemstack.cloneItemStack()); + itemstack.setCount(0); + return; + } + } + } + + private void d(ItemStack itemstack) { + for(int i = 0; i < this.getSize(); ++i) { + ItemStack itemstack1 = this.getItem(i); + if (ItemStack.e(itemstack1, itemstack)) { + this.a(itemstack, itemstack1); + if (itemstack.isEmpty()) { + return; + } + } + } + } + + private void a(ItemStack itemstack, ItemStack itemstack1) { + int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); + int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); + if (j > 0) { + itemstack1.add(j); + itemstack.subtract(j); + this.update(); + } + } + + @Override + public ItemStack splitWithoutUpdate(int i) { + ItemStack itemstack = this.c.get(i); + if (itemstack.isEmpty()) { + return ItemStack.b; + } else { + this.c.set(i, ItemStack.b); + return itemstack; + } + } + + @Override + public void setItem(int i, ItemStack itemstack) { + this.c.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.update(); + } + + @Override + public int getSize() { + return this.owner.getEnderChest().getSize(); + } + + @Override + public boolean isEmpty() { + return this.c.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void startOpen(EntityHuman entityhuman) { + } + + @Override + public void closeContainer(EntityHuman entityhuman) { + } + + @Override + public boolean b(int i, ItemStack itemstack) { + return true; + } + + @Override + public void clear() { + this.c.clear(); + this.update(); + } + + @Override + public void a(AutoRecipeStackManager autorecipestackmanager) { + for (ItemStack itemstack : this.c) { + autorecipestackmanager.b(itemstack); + } + + } + + @Override + public List f() { + List list = this.c.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clear(); + return list; + } + + @Override + public ItemStack a(Item item, int i) { + ItemStack itemstack = new ItemStack(item, 0); + + for(int j = this.getSize() - 1; j >= 0; --j) { + ItemStack itemstack1 = this.getItem(j); + if (itemstack1.getItem().equals(item)) { + int k = i - itemstack.getCount(); + ItemStack itemstack2 = itemstack1.cloneAndSubtract(k); + itemstack.add(itemstack2.getCount()); + if (itemstack.getCount() == i) { + break; + } + } + } + + if (!itemstack.isEmpty()) { + this.update(); + } + + return itemstack; + } + + @Override + public boolean b(ItemStack itemStack) { + for (ItemStack itemStack1 : this.c) { + if (itemStack1.isEmpty() || ItemStack.e(itemStack1, itemStack) && itemStack1.getCount() < itemStack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + @Override + public String toString() { + return this.c.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); + } + + @Override + public void a(NBTTagList nbttaglist) { + for(int i = 0; i < nbttaglist.size(); ++i) { + ItemStack itemstack = ItemStack.a(nbttaglist.getCompound(i)); + if (!itemstack.isEmpty()) { + this.a(itemstack); + } + } + + } + +} diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java new file mode 100644 index 00000000..0cf69729 --- /dev/null +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java @@ -0,0 +1,777 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_17_R1; + +import com.google.common.collect.ImmutableList; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportSystemDetails; +import net.minecraft.ReportedException; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.network.chat.ChatMessage; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.tags.Tag; +import net.minecraft.world.ContainerUtil; +import net.minecraft.world.IInventory; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EnumItemSlot; +import net.minecraft.world.entity.player.AutoRecipeStackManager; +import net.minecraft.world.entity.player.EntityHuman; +import net.minecraft.world.entity.player.PlayerInventory; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemArmor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.IBlockData; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory { + + private final CraftInventory inventory; + private boolean playerOnline; + private EntityHuman l; + private NonNullList h; + private NonNullList i; + private NonNullList j; + private List> n; + + public SpecialPlayerInventory(final Player bukkitPlayer, final Boolean online) { + super(PlayerDataManager.getHandle(bukkitPlayer)); + this.inventory = new CraftInventory(this); + this.playerOnline = online; + this.l = super.l; + this.h = this.l.getInventory().h; + this.i = this.l.getInventory().i; + this.j = this.l.getInventory().j; + this.n = ImmutableList.of(this.h, this.i, this.j); + } + + @Override + public void setPlayerOnline(@NotNull final Player player) { + if (!this.playerOnline) { + EntityPlayer entityPlayer = PlayerDataManager.getHandle(player); + entityPlayer.getInventory().transaction.addAll(this.transaction); + this.l = entityPlayer; + for (int i = 0; i < getSize(); ++i) { + this.l.getInventory().setItem(i, getRawItem(i)); + } + this.l.getInventory().k = this.k; + this.h = this.l.getInventory().h; + this.i = this.l.getInventory().i; + this.j = this.l.getInventory().j; + this.n = ImmutableList.of(this.h, this.i, this.j); + this.playerOnline = true; + } + } + + @Override + public boolean a(final EntityHuman entityhuman) { + return true; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; + } + + @Override + public ItemStack getItem(int i) { + List list = this.h; + + if (i >= list.size()) { + i -= list.size(); + list = this.i; + } else { + i = this.getReversedItemSlotNum(i); + } + + if (i >= list.size()) { + i -= list.size(); + list = this.j; + } else if (list == this.i) { + i = this.getReversedArmorSlotNum(i); + } + + if (i >= list.size()) { + return ItemStack.b; + } + + return list.get(i); + } + + private ItemStack getRawItem(int i) { + NonNullList list = null; + for (NonNullList next : this.n) { + if (i < next.size()) { + list = next; + break; + } + i -= next.size(); + } + + return list == null ? ItemStack.b : list.get(i); + } + + @Override + public IChatBaseComponent getDisplayName() { + return new ChatMessage(this.l.getName()); + } + + @Override + public boolean hasCustomName() { + return false; + } + + private int getReversedArmorSlotNum(final int i) { + if (i == 0) { + return 3; + } + if (i == 1) { + return 2; + } + if (i == 2) { + return 1; + } + if (i == 3) { + return 0; + } + return i; + } + + private int getReversedItemSlotNum(final int i) { + if (i >= 27) { + return i - 27; + } + return i + 9; + } + + @Override + public int getSize() { + return 45; + } + + @Override + public boolean isInUse() { + return !this.getViewers().isEmpty(); + } + + @Override + public void setItem(int i, final ItemStack itemstack) { + List list = this.h; + + if (i >= list.size()) { + i -= list.size(); + list = this.i; + } else { + i = this.getReversedItemSlotNum(i); + } + + if (i >= list.size()) { + i -= list.size(); + list = this.j; + } else if (list == this.i) { + i = this.getReversedArmorSlotNum(i); + } + + if (i >= list.size()) { + this.l.drop(itemstack, true); + return; + } + + list.set(i, itemstack); + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public @NotNull HumanEntity getPlayer() { + return this.l.getBukkitEntity(); + } + + @Override + public ItemStack splitStack(int i, final int j) { + List list = this.h; + + if (i >= list.size()) { + i -= list.size(); + list = this.i; + } else { + i = this.getReversedItemSlotNum(i); + } + + if (i >= list.size()) { + i -= list.size(); + list = this.j; + } else if (list == this.i) { + i = this.getReversedArmorSlotNum(i); + } + + if (i >= list.size()) { + return ItemStack.b; + } + + return list.get(i).isEmpty() ? ItemStack.b : ContainerUtil.a(list, i, j); + } + + @Override + public ItemStack splitWithoutUpdate(int i) { + List list = this.h; + + if (i >= list.size()) { + i -= list.size(); + list = this.i; + } else { + i = this.getReversedItemSlotNum(i); + } + + if (i >= list.size()) { + i -= list.size(); + list = this.j; + } else if (list == this.i) { + i = this.getReversedArmorSlotNum(i); + } + + if (i >= list.size()) { + return ItemStack.b; + } + + if (!list.get(i).isEmpty()) { + ItemStack itemstack = list.get(i); + + list.set(i, ItemStack.b); + return itemstack; + } + + return ItemStack.b; + } + + @Override + public List getContents() { + return this.n.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + + @Override + public boolean isEmpty() { + return this.n.stream().flatMap(Collection::stream).allMatch(ItemStack::isEmpty); + } + + @Override + public List getArmorContents() { + return this.i; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.transaction.add(who); + this.l.getInventory().transaction.add(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.transaction.remove(who); + this.l.getInventory().transaction.remove(who); + } + + @Override + public List getViewers() { + return this.transaction; + } + + @Override + public InventoryHolder getOwner() { + return this.l.getBukkitEntity(); + } + + public Location getLocation() { + return this.l.getBukkitEntity().getLocation(); + } + + /* Below this point largely just copied out of NMS to redirect to our overridden variables. */ + + @Override + public ItemStack getItemInHand() { + return d(this.k) ? this.h.get(this.k) : ItemStack.b; + } + + private boolean isSimilarAndNotFull(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.e(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + @Override + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + + for(int i = 0; i < this.h.size(); ++i) { + ItemStack itemstack1 = this.getItem(i); + if (itemstack1.isEmpty()) { + return itemstack.getCount(); + } + + if (this.isSimilarAndNotFull(itemstack1, itemstack)) { + remains -= (Math.min(itemstack1.getMaxStackSize(), this.getMaxStackSize())) - itemstack1.getCount(); + } + + if (remains <= 0) { + return itemstack.getCount(); + } + } + + ItemStack offhandItemStack = this.getItem(this.h.size() + this.i.size()); + if (this.isSimilarAndNotFull(offhandItemStack, itemstack)) { + remains -= (Math.min(offhandItemStack.getMaxStackSize(), this.getMaxStackSize())) - offhandItemStack.getCount(); + } + + return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; + } + + @Override + public int getFirstEmptySlotIndex() { + for(int i = 0; i < this.h.size(); ++i) { + if (this.h.get(i).isEmpty()) { + return i; + } + } + + return -1; + } + + @Override + public void a(ItemStack itemstack) { + int i = this.b(itemstack); + if (d(i)) { + this.k = i; + } else if (i == -1) { + this.k = this.i(); + if (!this.h.get(this.k).isEmpty()) { + int j = this.getFirstEmptySlotIndex(); + if (j != -1) { + this.h.set(j, this.h.get(this.k)); + } + } + + this.h.set(this.k, itemstack); + } else { + this.c(i); + } + + } + + @Override + public void c(int i) { + this.k = this.i(); + ItemStack itemstack = this.h.get(this.k); + this.h.set(this.k, this.h.get(i)); + this.h.set(i, itemstack); + } + + @Override + public int b(ItemStack itemstack) { + for (int i = 0; i < this.h.size(); ++i) { + if (!this.h.get(i).isEmpty() && ItemStack.e(itemstack, this.h.get(i))) { + return i; + } + } + + return -1; + } + + @Override + public int c(ItemStack itemstack) { + for (int i = 0; i < this.h.size(); ++i) { + ItemStack itemstack1 = this.h.get(i); + if (!this.h.get(i).isEmpty() && ItemStack.e(itemstack, this.h.get(i)) && !this.h.get(i).g() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) { + return i; + } + } + + return -1; + } + + @Override + public int i() { + int i; + int j; + for (j = 0; j < 9; ++j) { + i = (this.k + j) % 9; + if (this.h.get(i).isEmpty()) { + return i; + } + } + + for (j = 0; j < 9; ++j) { + i = (this.k + j) % 9; + if (!this.h.get(i).hasEnchantments()) { + return i; + } + } + + return this.k; + } + + @Override + public void a(double d0) { + if (d0 > 0.0D) { + d0 = 1.0D; + } + + if (d0 < 0.0D) { + d0 = -1.0D; + } + + this.k = (int) (this.k - d0); + + while (this.k < 0) { + this.k += 9; + } + + while (this.k >= 9) { + this.k -= 9; + } + + } + + @Override + public int a(Predicate predicate, int i, IInventory iinventory) { + byte b0 = 0; + boolean flag = i == 0; + int j = b0 + ContainerUtil.a(this, predicate, i - b0, flag); + j += ContainerUtil.a(iinventory, predicate, i - j, flag); + ItemStack itemstack = this.l.bV.getCarried(); + j += ContainerUtil.a(itemstack, predicate, i - j, flag); + if (itemstack.isEmpty()) { + this.l.bV.setCarried(ItemStack.b); + } + + return j; + } + + private int i(ItemStack itemstack) { + int i = this.firstPartial(itemstack); + if (i == -1) { + i = this.getFirstEmptySlotIndex(); + } + + return i == -1 ? itemstack.getCount() : this.d(i, itemstack); + } + + private int d(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + int j = itemstack.getCount(); + ItemStack itemstack1 = this.getItem(i); + if (itemstack1.isEmpty()) { + itemstack1 = new ItemStack(item, 0); + if (itemstack.hasTag()) { + itemstack1.setTag(Objects.requireNonNull(itemstack.getTag()).clone()); + } + + this.setItem(i, itemstack1); + } + + k = Math.min(j, itemstack1.getMaxStackSize() - itemstack1.getCount()); + + if (k > this.getMaxStackSize() - itemstack1.getCount()) { + k = this.getMaxStackSize() - itemstack1.getCount(); + } + + if (k != 0) { + j -= k; + itemstack1.add(k); + itemstack1.d(5); + } + + return j; + } + + @Override + public int firstPartial(ItemStack itemstack) { + if (this.isSimilarAndNotFull(this.getItem(this.k), itemstack)) { + return this.k; + } else if (this.isSimilarAndNotFull(this.getItem(40), itemstack)) { + return 40; + } else { + for(int i = 0; i < this.h.size(); ++i) { + if (this.isSimilarAndNotFull(this.h.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + @Override + public void j() { + for (NonNullList nonNullList : this.n) { + for (int i = 0; i < nonNullList.size(); ++i) { + if (!nonNullList.get(i).isEmpty()) { + nonNullList.get(i).a(this.l.t, this.l, i, this.k == i); + } + } + } + + } + + @Override + public boolean pickup(ItemStack itemStack) { + return this.c(-1, itemStack); + } + + @Override + public boolean c(int i, ItemStack itemStack) { + if (itemStack.isEmpty()) { + return false; + } else { + try { + if (itemStack.g()) { + if (i == -1) { + i = this.getFirstEmptySlotIndex(); + } + + if (i >= 0) { + this.h.set(i, itemStack.cloneItemStack()); + this.h.get(i).d(5); + itemStack.setCount(0); + return true; + } else if (this.l.getAbilities().d) { + itemStack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + do { + j = itemStack.getCount(); + if (i == -1) { + itemStack.setCount(this.i(itemStack)); + } else { + itemStack.setCount(this.d(i, itemStack)); + } + } while(!itemStack.isEmpty() && itemStack.getCount() < j); + + if (itemStack.getCount() == j && this.l.getAbilities().d) { + itemStack.setCount(0); + return true; + } else { + return itemStack.getCount() < j; + } + } + } catch (Throwable var6) { + CrashReport crashreport = CrashReport.a(var6, "Adding item to inventory"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added"); + crashreportsystemdetails.a("Item ID", Item.getId(itemStack.getItem())); + crashreportsystemdetails.a("Item data", itemStack.getDamage()); + crashreportsystemdetails.a("Item name", () -> itemStack.getName().getString()); + throw new ReportedException(crashreport); + } + } + } + + @Override + public void f(ItemStack itemStack) { + this.a(itemStack, true); + } + + @Override + public void a(ItemStack itemStack, boolean flag) { + while(true) { + if (!itemStack.isEmpty()) { + int i = this.firstPartial(itemStack); + if (i == -1) { + i = this.getFirstEmptySlotIndex(); + } + + if (i != -1) { + int j = itemStack.getMaxStackSize() - this.getItem(i).getCount(); + if (this.c(i, itemStack.cloneAndSubtract(j)) && flag && this.l instanceof EntityPlayer) { + ((EntityPlayer)this.l).b.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i))); + } + continue; + } + + this.l.drop(itemStack, false); + } + + return; + } + } + + @Override + public void g(ItemStack itemStack) { + for (NonNullList nonNullList : this.n) { + for (int i = 0; i < nonNullList.size(); ++i) { + if (nonNullList.get(i) == itemStack) { + nonNullList.set(i, ItemStack.b); + break; + } + } + } + } + + @Override + public float a(IBlockData iBlockData) { + return this.h.get(this.k).a(iBlockData); + } + + @Override + public NBTTagList a(NBTTagList nbtTagList) { + NBTTagCompound nbttagcompound; + int i; + for(i = 0; i < this.h.size(); ++i) { + if (!this.h.get(i).isEmpty()) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte)i); + this.h.get(i).save(nbttagcompound); + nbtTagList.add(nbttagcompound); + } + } + + for(i = 0; i < this.i.size(); ++i) { + if (!this.i.get(i).isEmpty()) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte)(i + 100)); + this.i.get(i).save(nbttagcompound); + nbtTagList.add(nbttagcompound); + } + } + + for(i = 0; i < this.j.size(); ++i) { + if (!this.j.get(i).isEmpty()) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte)(i + 150)); + this.j.get(i).save(nbttagcompound); + nbtTagList.add(nbttagcompound); + } + } + + return nbtTagList; + } + + @Override + public void b(NBTTagList nbtTagList) { + this.h.clear(); + this.i.clear(); + this.j.clear(); + + for(int i = 0; i < nbtTagList.size(); ++i) { + NBTTagCompound nbttagcompound = nbtTagList.getCompound(i); + int j = nbttagcompound.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.a(nbttagcompound); + if (!itemstack.isEmpty()) { + if (j < this.h.size()) { + this.h.set(j, itemstack); + } else if (j >= 100 && j < this.i.size() + 100) { + this.i.set(j - 100, itemstack); + } else if (j >= 150 && j < this.j.size() + 150) { + this.j.set(j - 150, itemstack); + } + } + } + + } + + @Override + public ItemStack e(int i) { + return this.i.get(i); + } + + @Override + public void a(DamageSource damageSource, float f, int[] intArray) { + if (f > 0.0F) { + f /= 4.0F; + if (f < 1.0F) { + f = 1.0F; + } + + for (int index : intArray) { + ItemStack itemstack = this.i.get(index); + if ((!damageSource.isFire() || !itemstack.getItem().w()) && itemstack.getItem() instanceof ItemArmor) { + itemstack.damage((int) f, this.l, (entityHuman) -> entityHuman.broadcastItemBreak(EnumItemSlot.a(EnumItemSlot.Function.b, index))); + } + } + } + + } + + @Override + public void dropContents() { + for (List list : this.n) { + for (int i = 0; i < list.size(); ++i) { + ItemStack itemstack = list.get(i); + if (!itemstack.isEmpty()) { + list.set(i, ItemStack.b); + this.l.a(itemstack, true, false); + } + } + } + } + + @Override + public boolean h(ItemStack itemStack) { + return this.n.stream() + .flatMap(Collection::stream) + .anyMatch(itemStack1 -> !itemStack1.isEmpty() && itemStack1.doMaterialsMatch(itemStack)); + } + + @Override + public boolean a(Tag tag) { + return this.n.stream() + .flatMap(Collection::stream) + .anyMatch(itemStack -> !itemStack.isEmpty() && itemStack.a(tag)); + } + + @Override + public void a(PlayerInventory playerInventory) { + for(int i = 0; i < this.getSize(); ++i) { + this.setItem(i, playerInventory.getItem(i)); + } + + this.k = playerInventory.k; + } + + @Override + public void clear() { + for (List list : this.n) { + list.clear(); + } + } + + @Override + public void a(AutoRecipeStackManager autoRecipeStackManager) { + for (ItemStack itemstack : this.h) { + autoRecipeStackManager.a(itemstack); + } + } + +} diff --git a/pom.xml b/pom.xml index 80144436..fa34fdcf 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ api plugin internal/v1_16_R3 + internal/v1_17_R1 assembly From ff7243db6a69e9b8fc2097907b813bc76a745517 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Jun 2021 12:05:38 -0400 Subject: [PATCH 025/340] Fix script issues Temporarily explicitly declare Spigot dependencies I don't have the time to deal with whatever the difference between remote and local bash/maven versions is right now. --- scripts/generate_changelog.sh | 2 +- scripts/get_spigot_versions.sh | 23 ++++++++--------------- scripts/install_spigot_dependencies.sh | 4 ++-- scripts/set_curseforge_env.sh | 2 +- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/scripts/generate_changelog.sh b/scripts/generate_changelog.sh index 81cddfb4..d2261f18 100644 --- a/scripts/generate_changelog.sh +++ b/scripts/generate_changelog.sh @@ -32,7 +32,7 @@ function lookup_email_username() { # Get a pretty list of supported Minecraft versions function get_minecraft_versions() { - versions=$(. ./scripts/get_spigot_versions.sh) + readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" for version in "${versions[@]}"; do # Append comma if variable is set, then append version diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh index 64c2fea0..e5249121 100644 --- a/scripts/get_spigot_versions.sh +++ b/scripts/get_spigot_versions.sh @@ -15,28 +15,21 @@ # along with this program. If not, see . # -# Note that this script is designed for use in GitHub Actions, and is not -# particularly robust nor configurable. Run from project parent directory. +# TODO FIGURE OUT AND REMOVE WHEN LESS STRESS +hacky_versions=("1.16.5-R0.1-SNAPSHOT" "1.17-R0.1-SNAPSHOT") +for hacky_version in "${hacky_versions[@]}"; do + echo "$hacky_version" +done -# Use a nameref as a cache - maven evaluation is pretty slow. -# Re-calling the script and relying on it to handle caching is way easier than passing around info. -declare -a spigot_versions +exit 0 -# We don't care about concatenation - either it's not null and we return or it's null and we instantiate. -# shellcheck disable=SC2199 -if [[ ${spigot_versions[@]} ]]; then - for spigot_version in "${spigot_versions[@]}"; do - echo "$spigot_version" - done - return -fi +# Note that this script is designed for use in GitHub Actions, and is not +# particularly robust nor configurable. Run from project parent directory. # Pull Spigot dependency information from Maven. # Since we only care about Spigot versions, only check modules in the folder internal. readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all | grep -oP '(?<=)(internal/.*)(?=)')" -declare -n versions="spigot_versions" - for module in "${modules[@]}"; do # Get number of dependencies declared in pom of specified internal module. max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl "$module" | grep -c "") diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot_dependencies.sh index 84fce3f9..0beac8f3 100644 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot_dependencies.sh @@ -30,8 +30,8 @@ get_buildtools () { wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools } -versions=$(. ./scripts/get_spigot_versions.sh) -echo Found Spigot dependencies: "$versions" +readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" +echo Found Spigot dependencies: "${versions[@]}" for version in "${versions[@]}"; do set -e diff --git a/scripts/set_curseforge_env.sh b/scripts/set_curseforge_env.sh index f21dd406..09f08b45 100644 --- a/scripts/set_curseforge_env.sh +++ b/scripts/set_curseforge_env.sh @@ -20,7 +20,7 @@ # Parse Spigot dependency information into major Minecraft versions function get_curseforge_minecraft_versions() { - versions=$(. ./scripts/get_spigot_versions.sh) + readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" for version in "${versions[@]}"; do # Parse Minecraft major version From 1cae4c7a8e4337e2115d160a5a4b1f5a81c830d4 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Jun 2021 18:39:07 -0400 Subject: [PATCH 026/340] Correct window id --- .../com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java index 618e5bad..4071eec7 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java @@ -169,7 +169,7 @@ public Containers getType() { return null; } - nmsPlayer.b.sendPacket(new PacketPlayOutOpenWindow(nmsPlayer.bV.j, container.getType(), + nmsPlayer.b.sendPacket(new PacketPlayOutOpenWindow(container.j, container.getType(), new ChatComponentText(container.getBukkitView().getTitle()))); nmsPlayer.bV = container; nmsPlayer.initMenu(container); From f613c0a522601f6bf2c0b097da5412f210b49981 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Jun 2021 18:39:19 -0400 Subject: [PATCH 027/340] Remove redundant load --- .../java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java index 15ca1b75..cf420eaa 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java @@ -34,7 +34,6 @@ public OpenPlayer(CraftServer server, EntityPlayer entity) { @Override public void loadData() { - super.loadData(); // See CraftPlayer#loadData NBTTagCompound loaded = this.server.getHandle().r.load(this.getHandle()); if (loaded != null) { From 64af4dddb096086a2e86979d6e80c50f3c2def48 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Jun 2021 18:40:39 -0400 Subject: [PATCH 028/340] Bump version to 4.1.8 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_16_R3/pom.xml | 2 +- internal/v1_17_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 7adc2daf..98df50dc 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.8-SNAPSHOT + 4.1.8 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 76aa28a1..ab03109f 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.8-SNAPSHOT + 4.1.8 openinvassembly diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index 13f5dc50..572b2694 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.8-SNAPSHOT + 4.1.8 openinvadapter1_16_R3 diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index e69e24a4..5de997bb 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.8-SNAPSHOT + 4.1.8 openinvadapter1_17_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index fa7537d8..b4c2c24b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.8-SNAPSHOT + 4.1.8 openinvplugincore diff --git a/pom.xml b/pom.xml index fa34fdcf..4f00289f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.8-SNAPSHOT + 4.1.8 pom @@ -81,13 +81,13 @@ openinvapi com.lishid compile - 4.1.8-SNAPSHOT + 4.1.8 openinvplugincore com.lishid compile - 4.1.8-SNAPSHOT + 4.1.8 From b198931f3063940403d979bdce0471642ee8f7fb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Jun 2021 18:40:48 -0400 Subject: [PATCH 029/340] Bump version to 4.1.9-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_16_R3/pom.xml | 2 +- internal/v1_17_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 98df50dc..b8f62591 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.8 + 4.1.9-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index ab03109f..53bd6f54 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.8 + 4.1.9-SNAPSHOT openinvassembly diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml index 572b2694..90b9458f 100644 --- a/internal/v1_16_R3/pom.xml +++ b/internal/v1_16_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.8 + 4.1.9-SNAPSHOT openinvadapter1_16_R3 diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 5de997bb..2cd90433 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.8 + 4.1.9-SNAPSHOT openinvadapter1_17_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index b4c2c24b..9a8a5387 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.8 + 4.1.9-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 4f00289f..c26be77a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.8 + 4.1.9-SNAPSHOT pom @@ -81,13 +81,13 @@ openinvapi com.lishid compile - 4.1.8 + 4.1.9-SNAPSHOT
openinvplugincore com.lishid compile - 4.1.8 + 4.1.9-SNAPSHOT From 4336b454b7a2edca0901fb570eb0e5bd5dade006 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 24 Jun 2021 08:21:26 -0400 Subject: [PATCH 030/340] Fix potential CME closing viewers without access Closes #33 --- .../internal/v1_17_R1/AnySilentContainer.java | 4 -- .../main/java/com/lishid/openinv/OpenInv.java | 40 +++++++++---------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java index 3f7085e3..4e8091d7 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java @@ -336,10 +336,6 @@ private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMod try { this.playerInteractManagerGamemode.setAccessible(true); this.playerInteractManagerGamemode.set(player.d, gameMode); - // TODO: may need additional calls to update abilities to prevent container sound + animation - // gameMode.a(player.getAbilities()); - // player.updateAbilities(); - // should be the fix if it doesn't work as-is } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 3d344724..680683d3 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -35,10 +35,12 @@ import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.LanguageManager; import com.lishid.openinv.util.Permissions; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.Consumer; @@ -88,7 +90,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { // Check if inventory is stored, and if it is, remove it and eject all viewers if (OpenInv.this.inventories.containsKey(key)) { Inventory inv = OpenInv.this.inventories.remove(key).getBukkitInventory(); - List viewers = inv.getViewers(); + List viewers = new ArrayList<>(inv.getViewers()); for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) { entity.closeInventory(); } @@ -97,7 +99,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { // Check if ender chest is stored, and if it is, remove it and eject all viewers if (OpenInv.this.enderChests.containsKey(key)) { Inventory inv = OpenInv.this.enderChests.remove(key).getBukkitInventory(); - List viewers = inv.getViewers(); + List viewers = new ArrayList<>(inv.getViewers()); for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) { entity.closeInventory(); } @@ -126,29 +128,23 @@ public void changeWorld(final Player player) { } if (this.inventories.containsKey(key)) { - Iterator iterator = this.inventories.get(key).getBukkitInventory().getViewers().iterator(); - //noinspection WhileLoopReplaceableByForEach - while (iterator.hasNext()) { - HumanEntity human = iterator.next(); - // If player has permission or is in the same world, allow continued access - // Just in case, also allow null worlds. - if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) { - continue; - } - human.closeInventory(); - } + kickCrossWorldViewers(player, this.inventories.get(key)); } if (this.enderChests.containsKey(key)) { - Iterator iterator = this.enderChests.get(key).getBukkitInventory().getViewers().iterator(); - //noinspection WhileLoopReplaceableByForEach - while (iterator.hasNext()) { - HumanEntity human = iterator.next(); - if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) { - continue; - } - human.closeInventory(); + kickCrossWorldViewers(player, this.enderChests.get(key)); + } + } + + private void kickCrossWorldViewers(Player player, ISpecialInventory inventory) { + List viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers()); + for (HumanEntity human : viewers) { + // If player has permission or is in the same world, allow continued access + // Just in case, also allow null worlds. + if (Permissions.CROSSWORLD.hasPermission(human) || Objects.equals(human.getWorld(), player.getWorld())) { + continue; } + human.closeInventory(); } } @@ -326,7 +322,7 @@ public void sendSystemMessage(@NotNull Player player, @NotNull String key) { return this.languageManager.getValue(key, getLocale(sender), replacements); } - private @Nullable String getLocale(@NotNull CommandSender sender) { + private @NotNull String getLocale(@NotNull CommandSender sender) { if (sender instanceof Player) { return ((Player) sender).getLocale(); } else { From e5ebf11d3298329f380639f42bcb0289f2e96a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=98=AD=E8=98=AD=E9=9C=B2=20Flandre=5Ftw?= <51469621+gregman98@users.noreply.github.com> Date: Tue, 29 Jun 2021 08:05:15 +0800 Subject: [PATCH 031/340] Add Chinese Traditional translation (#34) --- plugin/src/main/resources/zh_tw.yml | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 plugin/src/main/resources/zh_tw.yml diff --git a/plugin/src/main/resources/zh_tw.yml b/plugin/src/main/resources/zh_tw.yml new file mode 100644 index 00000000..c5e7399d --- /dev/null +++ b/plugin/src/main/resources/zh_tw.yml @@ -0,0 +1,31 @@ +# Translated into Chinese Traditional by Flandre_tw +messages: + error: + consoleUnsupported: 該指令無法在控制台執行。 + lootNotGenerated: '&c獎勵箱尚未生成 ! 請關閉 &b/silentcontainer&c。' + invalidMaterial: '&c無效的物品 : "%target%"' + invalidNumber: '&c無效的數字 : "%target%"' + invalidPlayer: '&c玩家不存在 !' + permissionOpenSelf: '&c你無法開啟自己的物品欄。' + permissionEnderAll: '&c你無法開啟其他玩家的終界箱。' + permissionExempt: '&c%target% 的物品欄受到保護。' + permissionCrossWorld: '&c%target% 不在你所在的世界。' + permissionPlayerOnline: '&c你無法開啟線上玩家的物品欄。' + permissionPlayerOffline: '&c你無法開啟離線玩家的物品欄。' + commandException: '&c發生錯誤,請查看控制台。' + info: + containerBlocked: 你正在開啟受阻擋的儲物箱。 + containerBlockedSilent: 你正在悄悄開啟受阻擋的儲物箱。 + containerSilent: 你正在悄悄開啟儲物箱。 + settingState: '%setting% : %state%' + player: + noMatches: 找不到持有 %target% 的玩家。 + matches: '找到持有 %target% 的玩家 : %detail%' + container: + noMatches: 找不到放有 %target% 的儲物箱。 + matches: '找到放有 %target% 的儲物箱 : %detail%' + 'true': '開啟' + 'false': '關閉' +container: + player: '%player% 的物品欄' + enderchest: '%player% 的終界箱' From 2f36a4d4dca7966e1cc704d91647a2c91bace132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=98=AD=E8=98=AD=E9=9C=B2=20Flandre=5Ftw?= <51469621+gregman98@users.noreply.github.com> Date: Tue, 29 Jun 2021 08:05:55 +0800 Subject: [PATCH 032/340] Add Chinese Simplified translation (#35) --- plugin/src/main/resources/zh_cn.yml | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 plugin/src/main/resources/zh_cn.yml diff --git a/plugin/src/main/resources/zh_cn.yml b/plugin/src/main/resources/zh_cn.yml new file mode 100644 index 00000000..bec0185c --- /dev/null +++ b/plugin/src/main/resources/zh_cn.yml @@ -0,0 +1,31 @@ +# Translated into Chinese Simplified by Flandre_tw +messages: + error: + consoleUnsupported: 该命令无法在后台执行。 + lootNotGenerated: '&c奖励箱尚未生成 ! 请关闭 &b/silentcontainer&c。' + invalidMaterial: '&c无效的物品 : "%target%"' + invalidNumber: '&c无效的数字 : "%target%"' + invalidPlayer: '&c玩家不存在 !' + permissionOpenSelf: '&c你无法开启自己的物品栏。' + permissionEnderAll: '&c你无法开启其他玩家的末影箱。' + permissionExempt: '&c%target% 的物品栏受到保护。' + permissionCrossWorld: '&c%target% 不在你所在的世界。' + permissionPlayerOnline: '&c你无法开启线上玩家的物品栏。' + permissionPlayerOffline: '&c你无法开启离线玩家的物品栏。' + commandException: '&c发生错误,请查看后台。' + info: + containerBlocked: 你正在开启受阻挡的储物箱。 + containerBlockedSilent: 你正在悄悄开启受阻挡的储物箱。 + containerSilent: 你正在悄悄开启储物箱。 + settingState: '%setting% : %state%' + player: + noMatches: 找不到持有 %target% 的玩家。 + matches: '找到持有 %target% 的玩家 : %detail%' + container: + noMatches: 找不到放有 %target% 的储物箱。 + matches: '找到放有 %target% 的储物箱 : %detail%' + 'true': '开启' + 'false': '关闭' +container: + player: '%player% 的物品栏' + enderchest: '%player% 的末影箱' From ea99bd5fd454246db201a50afa2ac48cd0fa5ecb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 28 Jul 2021 11:28:08 -0400 Subject: [PATCH 033/340] Extract duplicate API-only code --- .../openinv/internal/IAnySilentContainer.java | 110 +++++++++++++++++- .../internal/v1_16_R3/AnySilentContainer.java | 98 ---------------- .../internal/v1_17_R1/AnySilentContainer.java | 95 --------------- 3 files changed, 107 insertions(+), 196 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index d5e586b3..c0427772 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -16,8 +16,19 @@ package com.lishid.openinv.internal; +import org.bukkit.Material; +import org.bukkit.block.Barrel; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.EnderChest; +import org.bukkit.block.ShulkerBox; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.type.Chest; +import org.bukkit.entity.Cat; import org.bukkit.entity.Player; +import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; public interface IAnySilentContainer { @@ -41,14 +52,99 @@ public interface IAnySilentContainer { */ void deactivateContainer(@NotNull Player player); + /** + * @deprecated use {@link #isAnyContainerNeeded(Block)} + * @param player the player opening the container + * @param block the block + * @return true if the container is blocked + */ + @Deprecated + default boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block) { + return isAnyContainerNeeded(block); + } + /** * Checks if the container at the given coordinates is blocked. * - * @param player the Player opening the container * @param block the Block * @return true if the container is blocked */ - boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block); + default boolean isAnyContainerNeeded(@NotNull Block block) { + BlockState blockState = block.getState(); + + // Barrels do not require AnyContainer. + if (blockState instanceof Barrel) { + return false; + } + + // Enderchests require a non-occluding block on top to open. + if (blockState instanceof EnderChest) { + return block.getRelative(0, 1, 0).getType().isOccluding(); + } + + // Shulker boxes require 1/2 a block clear in the direction they open. + if (blockState instanceof ShulkerBox) { + BoundingBox boundingBox = block.getBoundingBox(); + if (boundingBox.getVolume() > 1) { + // Shulker box is already open. + return false; + } + + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Directional)) { + // Shouldn't be possible. Just in case, demand AnyChest. + return true; + } + + Directional directional = (Directional) blockData; + BlockFace face = directional.getFacing(); + boundingBox.shift(face.getDirection()); + // Return whether or not bounding boxes overlap. + return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox); + } + + + if (!(blockState instanceof org.bukkit.block.Chest)) { + return false; + } + + if (isChestBlocked(block)) { + return true; + } + + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) { + return false; + } + + Chest chest = (Chest) blockData; + int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4; + BlockFace relativeFace = BlockFace.values()[ordinal]; + org.bukkit.block.Block relative = block.getRelative(relativeFace); + + if (relative.getType() != block.getType()) { + return false; + } + + BlockData relativeData = relative.getBlockData(); + if (!(relativeData instanceof Chest)) { + return false; + } + + Chest relativeChest = (Chest) relativeData; + if (relativeChest.getFacing() != chest.getFacing() + || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { + return false; + } + + return isChestBlocked(relative); + } + + default boolean isChestBlocked(Block chest) { + org.bukkit.block.Block relative = chest.getRelative(0, 1, 0); + return relative.getType().isOccluding() + || chest.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0; + } /** * Checks if the given block is a container which can be unblocked or silenced. @@ -56,6 +152,14 @@ public interface IAnySilentContainer { * @param block the BlockState * @return true if the Block is a supported container */ - boolean isAnySilentContainer(@NotNull Block block); + default boolean isAnySilentContainer(@NotNull Block block) { + if (block.getType() == Material.ENDER_CHEST) { + return true; + } + BlockState state = block.getState(); + return state instanceof org.bukkit.block.Chest + || state instanceof org.bukkit.block.ShulkerBox + || state instanceof org.bukkit.block.Barrel; + } } diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java index 36281c25..3e099c0a 100644 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java +++ b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java @@ -48,18 +48,8 @@ import net.minecraft.server.v1_16_R3.World; import org.bukkit.Material; import org.bukkit.Statistic; -import org.bukkit.block.Barrel; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.EnderChest; -import org.bukkit.block.ShulkerBox; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.type.Chest; -import org.bukkit.entity.Cat; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; -import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; public class AnySilentContainer implements IAnySilentContainer { @@ -76,94 +66,6 @@ public AnySilentContainer() { } } - @Override - public boolean isAnySilentContainer(@NotNull final org.bukkit.block.Block bukkitBlock) { - if (bukkitBlock.getType() == Material.ENDER_CHEST) { - return true; - } - BlockState state = bukkitBlock.getState(); - return state instanceof org.bukkit.block.Chest - || state instanceof org.bukkit.block.ShulkerBox - || state instanceof org.bukkit.block.Barrel; - } - - @Override - public boolean isAnyContainerNeeded(@NotNull final Player p, @NotNull final org.bukkit.block.Block block) { - BlockState blockState = block.getState(); - - // Barrels do not require AnyContainer. - if (blockState instanceof Barrel) { - return false; - } - - // Enderchests require a non-occluding block on top to open. - if (blockState instanceof EnderChest) { - return block.getRelative(0, 1, 0).getType().isOccluding(); - } - - // Shulker boxes require 1/2 a block clear in the direction they open. - if (blockState instanceof ShulkerBox) { - BoundingBox boundingBox = block.getBoundingBox(); - if (boundingBox.getVolume() > 1) { - // Shulker box is already open. - return false; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Directional)) { - // Shouldn't be possible. Just in case, demand AnyChest. - return true; - } - - Directional directional = (Directional) blockData; - BlockFace face = directional.getFacing(); - boundingBox.shift(face.getDirection()); - // Return whether or not bounding boxes overlap. - return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox); - } - - if (!(blockState instanceof org.bukkit.block.Chest)) { - return false; - } - - if (isBlockedChest(block)) { - return true; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) { - return false; - } - - Chest chest = (Chest) blockData; - int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4; - BlockFace relativeFace = BlockFace.values()[ordinal]; - org.bukkit.block.Block relative = block.getRelative(relativeFace); - - if (relative.getType() != block.getType()) { - return false; - } - - BlockData relativeData = relative.getBlockData(); - if (!(relativeData instanceof Chest)) { - return false; - } - - Chest relativeChest = (Chest) relativeData; - if (relativeChest.getFacing() != chest.getFacing() - || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { - return false; - } - - return isBlockedChest(relative); - } - - private boolean isBlockedChest(org.bukkit.block.Block block) { - org.bukkit.block.Block relative = block.getRelative(0, 1, 0); - return relative.getType().isOccluding() - || block.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0; - } - @Override public boolean activateContainer(@NotNull final Player bukkitPlayer, final boolean silentchest, @NotNull final org.bukkit.block.Block bukkitBlock) { diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java index 4e8091d7..26a6266c 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java @@ -50,18 +50,8 @@ import net.minecraft.world.level.block.state.properties.BlockPropertyChestType; import org.bukkit.Material; import org.bukkit.Statistic; -import org.bukkit.block.Barrel; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.EnderChest; -import org.bukkit.block.ShulkerBox; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.type.Chest; -import org.bukkit.entity.Cat; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; -import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; public class AnySilentContainer implements IAnySilentContainer { @@ -79,91 +69,6 @@ public AnySilentContainer() { } } - @Override - public boolean isAnySilentContainer(@NotNull final org.bukkit.block.Block bukkitBlock) { - if (bukkitBlock.getType() == Material.ENDER_CHEST) { - return true; - } - BlockState state = bukkitBlock.getState(); - return state instanceof org.bukkit.block.Chest - || state instanceof org.bukkit.block.ShulkerBox - || state instanceof org.bukkit.block.Barrel; - } - - @Override - public boolean isAnyContainerNeeded(@NotNull final Player p, @NotNull final org.bukkit.block.Block block) { - BlockState blockState = block.getState(); - - // Barrels do not require AnyContainer. - if (blockState instanceof Barrel) { - return false; - } - - // Enderchests require a non-occluding block on top to open. - if (blockState instanceof EnderChest) { - return block.getRelative(0, 1, 0).getType().isOccluding(); - } - - // Shulker boxes require 1/2 a block clear in the direction they open. - if (blockState instanceof ShulkerBox) { - BoundingBox boundingBox = block.getBoundingBox(); - if (boundingBox.getVolume() > 1) { - // Shulker box is already open. - return false; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Directional directional)) { - // Shouldn't be possible. Just in case, demand AnyChest. - return true; - } - - BlockFace face = directional.getFacing(); - boundingBox.shift(face.getDirection()); - // Return whether or not bounding boxes overlap. - return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox); - } - - if (!(blockState instanceof org.bukkit.block.Chest)) { - return false; - } - - if (isBlockedChest(block)) { - return true; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Chest chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) { - return false; - } - - int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4; - BlockFace relativeFace = BlockFace.values()[ordinal]; - org.bukkit.block.Block relative = block.getRelative(relativeFace); - - if (relative.getType() != block.getType()) { - return false; - } - - BlockData relativeData = relative.getBlockData(); - if (!(relativeData instanceof Chest relativeChest)) { - return false; - } - - if (relativeChest.getFacing() != chest.getFacing() - || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { - return false; - } - - return isBlockedChest(relative); - } - - private boolean isBlockedChest(org.bukkit.block.Block block) { - org.bukkit.block.Block relative = block.getRelative(0, 1, 0); - return relative.getType().isOccluding() - || block.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0; - } - @Override public boolean activateContainer( @NotNull final Player bukkitPlayer, From 8599997e03ebc0665f981bba9b63f3bcafd4a650 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 28 Jul 2021 13:38:21 -0400 Subject: [PATCH 034/340] Work towards fixing shulkers --- .../openinv/internal/IAnySilentContainer.java | 20 ++++++++++++++++--- .../internal/v1_16_R3/AnySilentContainer.java | 20 +++++++++++++++++++ internal/v1_17_R1/pom.xml | 2 +- .../internal/v1_17_R1/AnySilentContainer.java | 20 +++++++++++++++++++ scripts/get_spigot_versions.sh | 2 +- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index c0427772..ef78c76a 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -98,12 +98,18 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { Directional directional = (Directional) blockData; BlockFace face = directional.getFacing(); - boundingBox.shift(face.getDirection()); + Block relative = block.getRelative(face, 1); + + if (isShulkerIgnoreBoundingBox(relative)) { + // Certain special cases are ignored. Signs, simple redstone, etc. + return false; + } + + boundingBox.expand(face.getDirection(), 0.5); // Return whether or not bounding boxes overlap. - return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox); + return relative.getBoundingBox().overlaps(boundingBox); } - if (!(blockState instanceof org.bukkit.block.Chest)) { return false; } @@ -140,6 +146,14 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { return isChestBlocked(relative); } + boolean isShulkerIgnoreBoundingBox(Block block); + + /** + * Determine whether or not a chest is blocked. + * + * @param chest the chest block + * @return true if the chest block cannot be opened under ordinary circumstances + */ default boolean isChestBlocked(Block chest) { org.bukkit.block.Block relative = chest.getRelative(0, 1, 0); return relative.getType().isOccluding() diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java index 3e099c0a..03a91730 100644 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java +++ b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java @@ -19,6 +19,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import java.lang.reflect.Field; +import java.util.logging.Level; import net.minecraft.server.v1_16_R3.Block; import net.minecraft.server.v1_16_R3.BlockBarrel; import net.minecraft.server.v1_16_R3.BlockChest; @@ -46,8 +47,10 @@ import net.minecraft.server.v1_16_R3.TileEntityLootable; import net.minecraft.server.v1_16_R3.TileInventory; import net.minecraft.server.v1_16_R3.World; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Statistic; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -66,6 +69,23 @@ public AnySilentContainer() { } } + @Override + public boolean isShulkerIgnoreBoundingBox(org.bukkit.block.Block bukkitBlock) { + org.bukkit.World bukkitWorld = bukkitBlock.getWorld(); + if (!(bukkitWorld instanceof CraftWorld)) { + bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); + } + if (!(bukkitWorld instanceof CraftWorld)) { + Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); + OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); + return false; + } + + final World world = ((CraftWorld) bukkitWorld).getHandle(); + final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + return world.getType(blockPosition).d(); + } + @Override public boolean activateContainer(@NotNull final Player bukkitPlayer, final boolean silentchest, @NotNull final org.bukkit.block.Block bukkitBlock) { diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 2cd90433..2c06c72e 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -39,7 +39,7 @@ spigot org.spigotmc provided - 1.17-R0.1-SNAPSHOT + 1.17.1-R0.1-SNAPSHOT
openinvapi diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java index 26a6266c..672e8906 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java @@ -48,8 +48,10 @@ import net.minecraft.world.level.block.entity.TileEntityLootable; import net.minecraft.world.level.block.state.IBlockData; import net.minecraft.world.level.block.state.properties.BlockPropertyChestType; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Statistic; +import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -69,6 +71,24 @@ public AnySilentContainer() { } } + @Override + public boolean isShulkerIgnoreBoundingBox(org.bukkit.block.Block bukkitBlock) { + org.bukkit.World bukkitWorld = bukkitBlock.getWorld(); + if (!(bukkitWorld instanceof CraftWorld)) { + bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); + } + if (!(bukkitWorld instanceof CraftWorld)) { + Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); + OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); + return false; + } + + final World world = ((CraftWorld) bukkitWorld).getHandle(); + final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + // isLargeVoxelShape + return world.getType(blockPosition).d(); + } + @Override public boolean activateContainer( @NotNull final Player bukkitPlayer, diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh index e5249121..29376e0f 100644 --- a/scripts/get_spigot_versions.sh +++ b/scripts/get_spigot_versions.sh @@ -16,7 +16,7 @@ # # TODO FIGURE OUT AND REMOVE WHEN LESS STRESS -hacky_versions=("1.16.5-R0.1-SNAPSHOT" "1.17-R0.1-SNAPSHOT") +hacky_versions=("1.16.5-R0.1-SNAPSHOT" "1.17.1-R0.1-SNAPSHOT") for hacky_version in "${hacky_versions[@]}"; do echo "$hacky_version" done From 556a8bcfce58217686ec10dd1afaf94296b048e8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 16 Oct 2021 16:23:26 -0400 Subject: [PATCH 035/340] Drop 1.16.5 support to use Mojang mappings Because there is no option to create a `remapped-mojang` artifact for 1.16.5 and the whole point of the scripts is to save me time, 1.16.5 support is being removed earlier than usual. Also fixes issues with NMS-based shulker collision check. Closes #36 --- .../openinv/internal/IAnySilentContainer.java | 26 +- .../lishid/openinv/util/InventoryAccess.java | 21 +- .../lishid/openinv/util/ReflectionHelper.java | 75 ++ internal/v1_16_R3/pom.xml | 64 -- .../internal/v1_16_R3/AnySilentContainer.java | 265 ------ .../openinv/internal/v1_16_R3/OpenPlayer.java | 78 -- .../internal/v1_16_R3/PlayerDataManager.java | 235 ------ .../internal/v1_16_R3/SpecialEnderChest.java | 261 ------ .../v1_16_R3/SpecialPlayerInventory.java | 738 ----------------- internal/v1_17_R1/pom.xml | 33 + .../internal/v1_17_R1/AnySilentContainer.java | 256 +++--- .../openinv/internal/v1_17_R1/OpenPlayer.java | 44 +- .../internal/v1_17_R1/PlayerDataManager.java | 63 +- .../internal/v1_17_R1/SpecialEnderChest.java | 223 +++--- .../v1_17_R1/SpecialPlayerInventory.java | 752 +++++++++--------- .../lishid/openinv/util/InternalAccessor.java | 2 + pom.xml | 7 +- scripts/get_spigot_versions.sh | 4 +- scripts/install_spigot_dependencies.sh | 4 +- 19 files changed, 792 insertions(+), 2359 deletions(-) create mode 100644 api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java delete mode 100644 internal/v1_16_R3/pom.xml delete mode 100644 internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java delete mode 100644 internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java delete mode 100644 internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java delete mode 100644 internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java delete mode 100644 internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index ef78c76a..62b95e48 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -24,7 +24,6 @@ import org.bukkit.block.EnderChest; import org.bukkit.block.ShulkerBox; import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; import org.bukkit.block.data.type.Chest; import org.bukkit.entity.Cat; import org.bukkit.entity.Player; @@ -84,30 +83,9 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { // Shulker boxes require 1/2 a block clear in the direction they open. if (blockState instanceof ShulkerBox) { - BoundingBox boundingBox = block.getBoundingBox(); - if (boundingBox.getVolume() > 1) { - // Shulker box is already open. - return false; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Directional)) { - // Shouldn't be possible. Just in case, demand AnyChest. + if (isShulkerBlocked((ShulkerBox) blockState)) { return true; } - - Directional directional = (Directional) blockData; - BlockFace face = directional.getFacing(); - Block relative = block.getRelative(face, 1); - - if (isShulkerIgnoreBoundingBox(relative)) { - // Certain special cases are ignored. Signs, simple redstone, etc. - return false; - } - - boundingBox.expand(face.getDirection(), 0.5); - // Return whether or not bounding boxes overlap. - return relative.getBoundingBox().overlaps(boundingBox); } if (!(blockState instanceof org.bukkit.block.Chest)) { @@ -146,7 +124,7 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { return isChestBlocked(relative); } - boolean isShulkerIgnoreBoundingBox(Block block); + boolean isShulkerBlocked(ShulkerBox block); /** * Determine whether or not a chest is blocked. diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index 90885da1..1de0d857 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -20,7 +20,6 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.lang.reflect.Field; import java.lang.reflect.Method; import org.bukkit.Bukkit; import org.bukkit.inventory.Inventory; @@ -81,7 +80,8 @@ public static boolean isEnderChest(@NotNull Inventory inventory) { } catch (ReflectiveOperationException ignored) {} } - inv = grabFieldOfTypeFromObject(expected, inventory); + // Use reflection to find the IInventory + inv = ReflectionHelper.grabObjectByType(inventory, expected); if (expected.isInstance(inv)) { return expected.cast(inv); @@ -90,23 +90,6 @@ public static boolean isEnderChest(@NotNull Inventory inventory) { return null; } - private static @Nullable T grabFieldOfTypeFromObject(final Class type, final Object object) { - // Use reflection to find the IInventory - Class clazz = object.getClass(); - T result = null; - for (Field f : clazz.getDeclaredFields()) { - f.setAccessible(true); - if (type.isAssignableFrom(f.getDeclaringClass())) { - try { - result = type.cast(f.get(object)); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - return result; - } - @Deprecated @Override public @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) { diff --git a/api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java b/api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java new file mode 100644 index 00000000..7c283749 --- /dev/null +++ b/api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.util; + +import java.lang.reflect.Field; +import org.jetbrains.annotations.Nullable; + +/** + * A utility for making reflection easier. + */ +public final class ReflectionHelper { + + private ReflectionHelper() {} + + /** + * Grab an {@link Object} stored in a {@link Field} of another {@code Object}. + * + *

This casts the field to the correct class. Any issues will result in a {@code null} return value. + * + * @param fieldType the {@link Class} of {@code Object} stored in the {@code Field} + * @param holder the containing {@code Object} + * @param the type of stored {@code Object} + * @return the first matching {@code Object} or {@code null} if none match + */ + public static @Nullable T grabObjectByType(final Object holder, final Class fieldType) { + Field field = grabFieldByType(holder.getClass(), fieldType); + + if (field != null) { + try { + return fieldType.cast(field.get(holder)); + } catch (IllegalAccessException ignored) { + // Ignore issues obtaining field + } + } + + return null; + } + + /** + * Grab a {@link Field} of an {@link Object} + * + * @param fieldType the {@link Class} of the object + * @param holderType the containing {@code Class} + * @return the first matching object or {@code null} if none match + */ + public static @Nullable Field grabFieldByType(Class holderType, Class fieldType) { + for (Field field : holderType.getDeclaredFields()) { + field.setAccessible(true); + if (fieldType.isAssignableFrom(field.getType())) { + return field; + } + } + + if (holderType.getSuperclass() != null) { + return grabFieldByType(fieldType, holderType.getSuperclass()); + } + + return null; + } + +} diff --git a/internal/v1_16_R3/pom.xml b/internal/v1_16_R3/pom.xml deleted file mode 100644 index 90b9458f..00000000 --- a/internal/v1_16_R3/pom.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 4.1.9-SNAPSHOT - - - openinvadapter1_16_R3 - OpenInvAdapter1_16_R3 - - - - spigot - org.spigotmc - provided - 1.16.5-R0.1-SNAPSHOT - - - openinvapi - com.lishid - - - openinvplugincore - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-shade-plugin - - - maven-compiler-plugin - - - - - diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java deleted file mode 100644 index 03a91730..00000000 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/AnySilentContainer.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_16_R3; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IAnySilentContainer; -import java.lang.reflect.Field; -import java.util.logging.Level; -import net.minecraft.server.v1_16_R3.Block; -import net.minecraft.server.v1_16_R3.BlockBarrel; -import net.minecraft.server.v1_16_R3.BlockChest; -import net.minecraft.server.v1_16_R3.BlockChestTrapped; -import net.minecraft.server.v1_16_R3.BlockPosition; -import net.minecraft.server.v1_16_R3.BlockPropertyChestType; -import net.minecraft.server.v1_16_R3.BlockShulkerBox; -import net.minecraft.server.v1_16_R3.ChatMessage; -import net.minecraft.server.v1_16_R3.Container; -import net.minecraft.server.v1_16_R3.ContainerChest; -import net.minecraft.server.v1_16_R3.Containers; -import net.minecraft.server.v1_16_R3.EntityHuman; -import net.minecraft.server.v1_16_R3.EntityPlayer; -import net.minecraft.server.v1_16_R3.EnumGamemode; -import net.minecraft.server.v1_16_R3.IBlockData; -import net.minecraft.server.v1_16_R3.IChatBaseComponent; -import net.minecraft.server.v1_16_R3.ITileInventory; -import net.minecraft.server.v1_16_R3.InventoryEnderChest; -import net.minecraft.server.v1_16_R3.InventoryLargeChest; -import net.minecraft.server.v1_16_R3.PlayerInteractManager; -import net.minecraft.server.v1_16_R3.PlayerInventory; -import net.minecraft.server.v1_16_R3.TileEntity; -import net.minecraft.server.v1_16_R3.TileEntityChest; -import net.minecraft.server.v1_16_R3.TileEntityEnderChest; -import net.minecraft.server.v1_16_R3.TileEntityLootable; -import net.minecraft.server.v1_16_R3.TileInventory; -import net.minecraft.server.v1_16_R3.World; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; - -public class AnySilentContainer implements IAnySilentContainer { - - private Field playerInteractManagerGamemode; - - public AnySilentContainer() { - try { - this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("gamemode"); - this.playerInteractManagerGamemode.setAccessible(true); - } catch (NoSuchFieldException | SecurityException e) { - System.err.println("[OpenInv] Unable to directly write player gamemode! SilentChest will fail."); - e.printStackTrace(); - } - } - - @Override - public boolean isShulkerIgnoreBoundingBox(org.bukkit.block.Block bukkitBlock) { - org.bukkit.World bukkitWorld = bukkitBlock.getWorld(); - if (!(bukkitWorld instanceof CraftWorld)) { - bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); - } - if (!(bukkitWorld instanceof CraftWorld)) { - Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); - OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); - return false; - } - - final World world = ((CraftWorld) bukkitWorld).getHandle(); - final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - return world.getType(blockPosition).d(); - } - - @Override - public boolean activateContainer(@NotNull final Player bukkitPlayer, final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - final World world = player.world; - final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final TileEntity tile = world.getTileEntity(blockPosition); - - if (tile == null) { - return false; - } - - if (tile instanceof TileEntityEnderChest) { - // Anychest ender chest. See net.minecraft.server.BlockEnderChest - InventoryEnderChest enderChest = player.getEnderChest(); - enderChest.a((TileEntityEnderChest) tile); - player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> { - Containers containers = PlayerDataManager.getContainers(enderChest.getSize()); - int rows = enderChest.getSize() / 9; - return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows); - }, new ChatMessage("container.enderchest"))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(tile instanceof ITileInventory)) { - return false; - } - - ITileInventory tileInventory = (ITileInventory) tile; - IBlockData blockData = world.getType(blockPosition); - Block block = blockData.getBlock(); - - if (block instanceof BlockChest) { - - BlockPropertyChestType chestType = blockData.get(BlockChest.c); - - if (chestType != BlockPropertyChestType.SINGLE) { - - BlockPosition adjacentBlockPosition = blockPosition.shift(BlockChest.h(blockData)); - IBlockData adjacentBlockData = world.getType(adjacentBlockPosition); - - if (adjacentBlockData.getBlock() == block) { - - BlockPropertyChestType adjacentChestType = adjacentBlockData.get(BlockChest.c); - - if (adjacentChestType != BlockPropertyChestType.SINGLE && chestType != adjacentChestType - && adjacentBlockData.get(BlockChest.FACING) == blockData.get(BlockChest.FACING)) { - - TileEntity adjacentTile = world.getTileEntity(adjacentBlockPosition); - - if (adjacentTile instanceof TileEntityChest && tileInventory instanceof TileEntityChest) { - TileEntityChest rightChest = chestType == BlockPropertyChestType.RIGHT ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile; - TileEntityChest leftChest = chestType == BlockPropertyChestType.RIGHT ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory); - - if (silentchest && (rightChest.lootTable != null || leftChest.lootTable != null)) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - tileInventory = new ITileInventory() { - public Container createMenu(int containerCounter, PlayerInventory playerInventory, EntityHuman entityHuman) { - leftChest.d(playerInventory.player); - rightChest.d(playerInventory.player); - return ContainerChest.b(containerCounter, playerInventory, new InventoryLargeChest(rightChest, leftChest)); - } - - public IChatBaseComponent getScoreboardDisplayName() { - if (leftChest.hasCustomName()) { - return leftChest.getScoreboardDisplayName(); - } - if (rightChest.hasCustomName()) { - return rightChest.getScoreboardDisplayName(); - } - return new ChatMessage("container.chestDouble"); - } - }; - } - } - } - } - - if (block instanceof BlockChestTrapped) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof BlockShulkerBox) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BlockBarrel) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) { - player.openContainer(tileInventory); - return true; - } - - // SilentChest requires access to setting players' gamemode directly. - if (this.playerInteractManagerGamemode == null) { - return false; - } - - if (tile instanceof TileEntityLootable) { - TileEntityLootable lootable = (TileEntityLootable) tile; - if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - EnumGamemode gamemode = player.playerInteractManager.getGameMode(); - this.forceGameMode(player, EnumGamemode.SPECTATOR); - player.openContainer(tileInventory); - this.forceGameMode(player, gamemode); - return true; - } - - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.playerInteractManagerGamemode == null) { - return; - } - - InventoryView view = bukkitPlayer.getOpenInventory(); - switch (view.getType()) { - case CHEST: - case ENDER_CHEST: - case SHULKER_BOX: - case BARREL: - break; - default: - return; - } - - EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - EnumGamemode gamemode = player.playerInteractManager.getGameMode(); - this.forceGameMode(player, EnumGamemode.SPECTATOR); - player.activeContainer.b(player); - player.activeContainer.a(player, false); - player.activeContainer.transferTo(player.defaultContainer, player.getBukkitEntity()); - player.activeContainer = player.defaultContainer; - this.forceGameMode(player, gamemode); - } - - private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) { - if (this.playerInteractManagerGamemode == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - if (!this.playerInteractManagerGamemode.isAccessible()) { - // Just in case, ensure accessible. - this.playerInteractManagerGamemode.setAccessible(true); - } - this.playerInteractManagerGamemode.set(player.playerInteractManager, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } - -} diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java deleted file mode 100644 index ec87e3be..00000000 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/OpenPlayer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_16_R3; - -import java.io.File; -import java.io.FileOutputStream; -import net.minecraft.server.v1_16_R3.EntityPlayer; -import net.minecraft.server.v1_16_R3.NBTCompressedStreamTools; -import net.minecraft.server.v1_16_R3.NBTTagCompound; -import net.minecraft.server.v1_16_R3.WorldNBTStorage; -import org.apache.logging.log4j.LogManager; -import org.bukkit.craftbukkit.v1_16_R3.CraftServer; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; - -public class OpenPlayer extends CraftPlayer { - - public OpenPlayer(CraftServer server, EntityPlayer entity) { - super(server, entity); - } - - @Override - public void loadData() { - // See CraftPlayer#loadData - NBTTagCompound loaded = this.server.getHandle().playerFileData.load(this.getHandle()); - if (loaded != null) { - readExtraData(loaded); - } - } - - @Override - public void saveData() { - EntityPlayer player = this.getHandle(); - // See net.minecraft.server.WorldNBTStorage#save(EntityPlayer) - try { - WorldNBTStorage worldNBTStorage = player.server.getPlayerList().playerFileData; - - NBTTagCompound playerData = player.save(new NBTTagCompound()); - setExtraData(playerData); - - if (!isOnline()) { - // Special case: save old vehicle data - NBTTagCompound oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.set("RootVehicle", oldData.getCompound("RootVehicle")); - } - } - - File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp"); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat"); - - NBTCompressedStreamTools.a(playerData, new FileOutputStream(file)); - - if (file1.exists() && !file1.delete() || !file.renameTo(file1)) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString()); - } - - } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString()); - } - } - -} diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java deleted file mode 100644 index 56490ec5..00000000 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/PlayerDataManager.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_16_R3; - -import com.lishid.openinv.internal.IPlayerDataManager; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.OpenInventoryView; -import com.mojang.authlib.GameProfile; -import java.lang.reflect.Field; -import net.minecraft.server.v1_16_R3.ChatComponentText; -import net.minecraft.server.v1_16_R3.Container; -import net.minecraft.server.v1_16_R3.Containers; -import net.minecraft.server.v1_16_R3.Entity; -import net.minecraft.server.v1_16_R3.EntityPlayer; -import net.minecraft.server.v1_16_R3.MinecraftServer; -import net.minecraft.server.v1_16_R3.PacketPlayOutOpenWindow; -import net.minecraft.server.v1_16_R3.PlayerInteractManager; -import net.minecraft.server.v1_16_R3.World; -import net.minecraft.server.v1_16_R3.WorldServer; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_16_R3.CraftServer; -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.CraftContainer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PlayerDataManager implements IPlayerDataManager { - - private @Nullable Field bukkitEntity; - - public PlayerDataManager() { - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); - e.printStackTrace(); - bukkitEntity = null; - } - } - - public static @NotNull EntityPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - EntityPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); - } - - return nmsPlayer; - } - - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - - // Create a profile and entity to load the player data - // See net.minecraft.server.PlayerList#attemptLogin - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - WorldServer worldServer = server.getWorldServer(World.OVERWORLD); - - if (worldServer == null) { - return null; - } - - EntityPlayer entity = new EntityPlayer(server, worldServer, profile, new PlayerInteractManager(worldServer)); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - // Get the bukkit entity - Player target = entity.getBukkitEntity(); - if (target != null) { - // Load data - target.loadData(); - } - // Return the entity - return target; - } - - void injectPlayer(EntityPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } - - bukkitEntity.setAccessible(true); - - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); - } - - @NotNull - @Override - public Player inject(@NotNull Player player) { - try { - EntityPlayer nmsPlayer = getHandle(player); - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - return player; - } - } - - @Nullable - @Override - public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - - EntityPlayer nmsPlayer = getHandle(player); - - if (nmsPlayer.playerConnection == null) { - return null; - } - - InventoryView view = getView(player, inventory); - - if (view == null) { - return player.openInventory(inventory.getBukkitInventory()); - } - - Container container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { - @Override - public Containers getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - - container.setTitle(new ChatComponentText(view.getTitle())); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); - - if (container == null) { - return null; - } - - nmsPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, container.getType(), - new ChatComponentText(container.getBukkitView().getTitle()))); - nmsPlayer.activeContainer = container; - container.addSlotListener(nmsPlayer); - - return container.getBukkitView(); - - } - - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - - static @NotNull Containers getContainers(int inventorySize) { - switch (inventorySize) { - case 9: - return Containers.GENERIC_9X1; - case 18: - return Containers.GENERIC_9X2; - case 36: - return Containers.GENERIC_9X4; - case 41: // PLAYER - case 45: - return Containers.GENERIC_9X5; - case 54: - return Containers.GENERIC_9X6; - case 27: - default: - return Containers.GENERIC_9X3; - } - } - - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - -} diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java deleted file mode 100644 index c2bf0610..00000000 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialEnderChest.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_16_R3; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import java.util.List; -import net.minecraft.server.v1_16_R3.AutoRecipeStackManager; -import net.minecraft.server.v1_16_R3.ContainerUtil; -import net.minecraft.server.v1_16_R3.EntityHuman; -import net.minecraft.server.v1_16_R3.EntityPlayer; -import net.minecraft.server.v1_16_R3.IInventoryListener; -import net.minecraft.server.v1_16_R3.InventoryEnderChest; -import net.minecraft.server.v1_16_R3.ItemStack; -import net.minecraft.server.v1_16_R3.NonNullList; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialEnderChest extends InventoryEnderChest implements ISpecialEnderChest { - - private final CraftInventory inventory; - private EntityPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChest().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public boolean isInUse() { - return !this.getViewers().isEmpty(); - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final Player player) { - if (!this.playerOnline) { - try { - this.owner = PlayerDataManager.getHandle(player); - InventoryEnderChest enderChest = owner.getEnderChest(); - for (int i = 0; i < enderChest.getSize(); ++i) { - enderChest.setItem(i, this.items.get(i)); - } - this.items = enderChest.items; - } catch (Exception ignored) {} - this.playerOnline = true; - } - } - - @Override - public @NotNull Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void update() { - this.owner.getEnderChest().update(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChest().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChest().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChest().getViewers(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChest().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChest().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void a(IInventoryListener iinventorylistener) { - this.owner.getEnderChest().a(iinventorylistener); - } - - @Override - public void b(IInventoryListener iinventorylistener) { - this.owner.getEnderChest().b(iinventorylistener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.b; - } - - @Override - public ItemStack splitStack(int i, int j) { - ItemStack itemstack = ContainerUtil.a(this.items, i, j); - if (!itemstack.isEmpty()) { - this.update(); - } - - return itemstack; - } - - @Override - public ItemStack a(ItemStack itemstack) { - ItemStack itemstack1 = itemstack.cloneItemStack(); - - for (int i = 0; i < this.getSize(); ++i) { - ItemStack itemstack2 = this.getItem(i); - if (itemstack2.isEmpty()) { - this.setItem(i, itemstack1); - this.update(); - return ItemStack.b; - } - - if (ItemStack.c(itemstack2, itemstack1)) { - int j = Math.min(this.getMaxStackSize(), itemstack2.getMaxStackSize()); - int k = Math.min(itemstack1.getCount(), j - itemstack2.getCount()); - if (k > 0) { - itemstack2.add(k); - itemstack1.subtract(k); - if (itemstack1.isEmpty()) { - this.update(); - return ItemStack.b; - } - } - } - } - - if (itemstack1.getCount() != itemstack.getCount()) { - this.update(); - } - - return itemstack1; - } - - @Override - public ItemStack splitWithoutUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.b; - } else { - this.items.set(i, ItemStack.b); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.update(); - } - - @Override - public int getSize() { - return this.owner.getEnderChest().getSize(); - } - - @Override - public boolean isEmpty() { - - for (ItemStack itemstack : this.items) { - if (!itemstack.isEmpty()) { - return false; - } - } - - return true; - } - - @Override - public int getMaxStackSize() { - return 64; - } - - @Override - public boolean a(EntityHuman entityhuman) { - return true; - } - - @Override - public void startOpen(EntityHuman entityhuman) { - } - - @Override - public void closeContainer(EntityHuman entityhuman) { - } - - @Override - public boolean b(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clear() { - this.items.clear(); - } - - @Override - public void a(AutoRecipeStackManager autorecipestackmanager) { - - for (ItemStack itemstack : this.items) { - autorecipestackmanager.b(itemstack); - } - - } - -} diff --git a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java b/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java deleted file mode 100644 index df9325c1..00000000 --- a/internal/v1_16_R3/src/main/java/com/lishid/openinv/internal/v1_16_R3/SpecialPlayerInventory.java +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_16_R3; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.util.Iterator; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.server.v1_16_R3.AutoRecipeStackManager; -import net.minecraft.server.v1_16_R3.ChatMessage; -import net.minecraft.server.v1_16_R3.ContainerUtil; -import net.minecraft.server.v1_16_R3.CrashReport; -import net.minecraft.server.v1_16_R3.CrashReportSystemDetails; -import net.minecraft.server.v1_16_R3.DamageSource; -import net.minecraft.server.v1_16_R3.EntityHuman; -import net.minecraft.server.v1_16_R3.EntityPlayer; -import net.minecraft.server.v1_16_R3.EnumItemSlot; -import net.minecraft.server.v1_16_R3.IBlockData; -import net.minecraft.server.v1_16_R3.IChatBaseComponent; -import net.minecraft.server.v1_16_R3.IInventory; -import net.minecraft.server.v1_16_R3.Item; -import net.minecraft.server.v1_16_R3.ItemArmor; -import net.minecraft.server.v1_16_R3.ItemStack; -import net.minecraft.server.v1_16_R3.NBTTagCompound; -import net.minecraft.server.v1_16_R3.NBTTagList; -import net.minecraft.server.v1_16_R3.NonNullList; -import net.minecraft.server.v1_16_R3.PacketPlayOutSetSlot; -import net.minecraft.server.v1_16_R3.PlayerInventory; -import net.minecraft.server.v1_16_R3.ReportedException; -import net.minecraft.server.v1_16_R3.World; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_16_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private EntityHuman player; - private NonNullList items, armor, extraSlots; - private List> f; - - public SpecialPlayerInventory(final Player bukkitPlayer, final Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.items = this.player.inventory.items; - this.armor = this.player.inventory.armor; - this.extraSlots = this.player.inventory.extraSlots; - this.f = ImmutableList.of(this.items, this.armor, this.extraSlots); - } - - @Override - public void setPlayerOnline(@NotNull final Player player) { - if (!this.playerOnline) { - EntityPlayer entityPlayer = PlayerDataManager.getHandle(player); - entityPlayer.inventory.transaction.addAll(this.transaction); - this.player = entityPlayer; - for (int i = 0; i < getSize(); ++i) { - this.player.inventory.setItem(i, getRawItem(i)); - } - this.player.inventory.itemInHandIndex = this.itemInHandIndex; - this.items = this.player.inventory.items; - this.armor = this.player.inventory.armor; - this.extraSlots = this.player.inventory.extraSlots; - this.f = ImmutableList.of(this.items, this.armor, this.extraSlots); - this.playerOnline = true; - } - } - - @Override - public boolean a(final EntityHuman entityhuman) { - return true; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public ItemStack getItem(int i) { - List list = this.items; - - if (i >= list.size()) { - i -= list.size(); - list = this.armor; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.extraSlots; - } else if (list == this.armor) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - return ItemStack.b; - } - - return list.get(i); - } - - private ItemStack getRawItem(int i) { - NonNullList list = null; - for (NonNullList next : this.f) { - if (i < next.size()) { - list = next; - break; - } - i -= next.size(); - } - - return list == null ? ItemStack.b : list.get(i); - } - - @Override - public IChatBaseComponent getDisplayName() { - return new ChatMessage(this.player.getName()); - } - - @Override - public boolean hasCustomName() { - return false; - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - @Override - public int getSize() { - return 45; - } - - @Override - public boolean isInUse() { - return !this.getViewers().isEmpty(); - } - - @Override - public void setItem(int i, final ItemStack itemstack) { - List list = this.items; - - if (i >= list.size()) { - i -= list.size(); - list = this.armor; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.extraSlots; - } else if (list == this.armor) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - this.player.drop(itemstack, true); - return; - } - - list.set(i, itemstack); - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - @Override - public ItemStack splitStack(int i, final int j) { - List list = this.items; - - if (i >= list.size()) { - i -= list.size(); - list = this.armor; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.extraSlots; - } else if (list == this.armor) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - return ItemStack.b; - } - - return list.get(i).isEmpty() ? ItemStack.b : ContainerUtil.a(list, i, j); - } - - @Override - public ItemStack splitWithoutUpdate(int i) { - List list = this.items; - - if (i >= list.size()) { - i -= list.size(); - list = this.armor; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.extraSlots; - } else if (list == this.armor) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - return ItemStack.b; - } - - if (!list.get(i).isEmpty()) { - ItemStack itemstack = list.get(i); - - list.set(i, ItemStack.b); - return itemstack; - } - - return ItemStack.b; - } - - @Override - public List getContents() { - return this.f.stream().flatMap(List::stream).collect(Collectors.toList()); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.transaction.add(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.transaction.remove(who); - } - - @Override - public List getViewers() { - return this.transaction; - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public ItemStack getItemInHand() { - return d(this.itemInHandIndex) ? this.items.get(this.itemInHandIndex) : ItemStack.b; - } - - private boolean isSimilarAndNotFull(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && this.b(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - private boolean b(ItemStack itemstack, ItemStack itemstack1) { - return itemstack.getItem() == itemstack1.getItem() && ItemStack.equals(itemstack, itemstack1); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (!this.isSimilarAndNotFull(itemstack, itemstack1)) { - remains -= Math.min(itemstack1.getMaxStackSize(), this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getItem(this.items.size() + this.armor.size()); - if (this.isSimilarAndNotFull(offhandItemStack, itemstack)) { - remains -= Math.min(offhandItemStack.getMaxStackSize(), this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return itemstack.getCount() - remains; - } - - @Override - public int getFirstEmptySlotIndex() { - for (int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void c(int i) { - this.itemInHandIndex = this.i(); - ItemStack itemstack = this.items.get(this.itemInHandIndex); - this.items.set(this.itemInHandIndex, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int c(ItemStack itemstack) { - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.items.get(i); - if (!this.items.get(i).isEmpty() && this.b(itemstack, this.items.get(i)) && !this.items.get(i).f() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) { - return i; - } - } - - return -1; - } - - @Override - public int i() { - int i; - int j; - for (j = 0; j < 9; ++j) { - i = (this.itemInHandIndex + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for (j = 0; j < 9; ++j) { - i = (this.itemInHandIndex + j) % 9; - if (!this.items.get(i).hasEnchantments()) { - return i; - } - } - - return this.itemInHandIndex; - } - - @Override - public int a(Predicate predicate, int i, IInventory iinventory) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerUtil.a(this, predicate, i - b0, flag); - j += ContainerUtil.a(iinventory, predicate, i - j, flag); - j += ContainerUtil.a(this.getCarried(), predicate, i - j, flag); - if (this.getCarried().isEmpty()) { - this.setCarried(ItemStack.b); - } - - return j; - } - - private int i(ItemStack itemstack) { - int i = this.firstPartial(itemstack); - if (i == -1) { - i = this.getFirstEmptySlotIndex(); - } - - return i == -1 ? itemstack.getCount() : this.d(i, itemstack); - } - - private int d(int i, ItemStack itemstack) { - Item item = itemstack.getItem(); - int j = itemstack.getCount(); - ItemStack itemstack1 = this.getItem(i); - if (itemstack1.isEmpty()) { - itemstack1 = new ItemStack(item, 0); - NBTTagCompound tag = itemstack.getTag(); - if (tag != null) { - itemstack1.setTag(tag.clone()); - } - - this.setItem(i, itemstack1); - } - - int k = j; - if (j > itemstack1.getMaxStackSize() - itemstack1.getCount()) { - k = itemstack1.getMaxStackSize() - itemstack1.getCount(); - } - - if (k > this.getMaxStackSize() - itemstack1.getCount()) { - k = this.getMaxStackSize() - itemstack1.getCount(); - } - - if (k != 0) { - j -= k; - itemstack1.add(k); - itemstack1.d(5); - } - return j; - } - - @Override - public int firstPartial(ItemStack itemstack) { - if (this.isSimilarAndNotFull(this.getItem(this.itemInHandIndex), itemstack)) { - return this.itemInHandIndex; - } else if (this.isSimilarAndNotFull(this.getItem(40), itemstack)) { - return 40; - } else { - for (int i = 0; i < this.items.size(); ++i) { - if (this.isSimilarAndNotFull(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void j() { - - for (List itemStacks : this.f) { - for (int i = 0; i < itemStacks.size(); ++i) { - if (!itemStacks.get(i).isEmpty()) { - itemStacks.get(i).a(this.player.world, this.player, i, this.itemInHandIndex == i); - } - } - } - - } - - @Override - public boolean pickup(ItemStack itemstack) { - return this.c(-1, itemstack); - } - - @Override - public boolean c(int i, ItemStack itemstack) { - if (itemstack.isEmpty()) { - return false; - } else { - try { - if (itemstack.f()) { - if (i == -1) { - i = this.getFirstEmptySlotIndex(); - } - - if (i >= 0) { - this.items.set(i, itemstack.cloneItemStack()); - this.items.get(i).d(5); - itemstack.setCount(0); - return true; - } else if (this.player.abilities.canInstantlyBuild) { - itemstack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemstack.getCount(); - if (i == -1) { - itemstack.setCount(this.i(itemstack)); - } else { - itemstack.setCount(this.d(i, itemstack)); - } - } while(!itemstack.isEmpty() && itemstack.getCount() < j); - - if (itemstack.getCount() == j && this.player.abilities.canInstantlyBuild) { - itemstack.setCount(0); - return true; - } else { - return itemstack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashreport = CrashReport.a(var6, "Adding item to inventory"); - CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added"); - crashreportsystemdetails.a("Item ID", Item.getId(itemstack.getItem())); - crashreportsystemdetails.a("Item data", itemstack.getDamage()); - crashreportsystemdetails.a("Item name", () -> itemstack.getName().getString()); - throw new ReportedException(crashreport); - } - } - } - - @Override - public void a(World world, ItemStack itemstack) { - if (!world.isClientSide) { - while(!itemstack.isEmpty()) { - int i = this.firstPartial(itemstack); - if (i == -1) { - i = this.getFirstEmptySlotIndex(); - } - - if (i == -1) { - this.player.drop(itemstack, false); - break; - } - - int j = itemstack.getMaxStackSize() - this.getItem(i).getCount(); - if (this.c(i, itemstack.cloneAndSubtract(j))) { - ((EntityPlayer)this.player).playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i))); - } - } - } - - } - - @Override - public void f(ItemStack itemstack) { - - for (List list : this.f) { - for (int i = 0; i < list.size(); ++i) { - if (list.get(i) == itemstack) { - list.set(i, ItemStack.b); - break; - } - } - } - } - - @Override - public float a(IBlockData iblockdata) { - return this.items.get(this.itemInHandIndex).a(iblockdata); - } - - @Override - public NBTTagList a(NBTTagList nbttaglist) { - NBTTagCompound nbttagcompound; - int i; - for (i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - nbttagcompound = new NBTTagCompound(); - nbttagcompound.setByte("Slot", (byte) i); - this.items.get(i).save(nbttagcompound); - nbttaglist.add(nbttagcompound); - } - } - - for (i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - nbttagcompound = new NBTTagCompound(); - nbttagcompound.setByte("Slot", (byte) (i + 100)); - this.armor.get(i).save(nbttagcompound); - nbttaglist.add(nbttagcompound); - } - } - - for (i = 0; i < this.extraSlots.size(); ++i) { - if (!this.extraSlots.get(i).isEmpty()) { - nbttagcompound = new NBTTagCompound(); - nbttagcompound.setByte("Slot", (byte) (i + 150)); - this.extraSlots.get(i).save(nbttagcompound); - nbttaglist.add(nbttagcompound); - } - } - - return nbttaglist; - } - - @Override - public void b(NBTTagList nbttaglist) { - this.items.clear(); - this.armor.clear(); - this.extraSlots.clear(); - - for(int i = 0; i < nbttaglist.size(); ++i) { - NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); - int j = nbttagcompound.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.a(nbttagcompound); - if (!itemstack.isEmpty()) { - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.extraSlots.size() + 150) { - this.extraSlots.set(j - 150, itemstack); - } - } - } - - } - - @Override - public boolean isEmpty() { - Iterator iterator = this.items.iterator(); - - ItemStack itemstack; - while (iterator.hasNext()) { - itemstack = iterator.next(); - if (!itemstack.isEmpty()) { - return false; - } - } - - iterator = this.armor.iterator(); - - while (iterator.hasNext()) { - itemstack = iterator.next(); - if (!itemstack.isEmpty()) { - return false; - } - } - - iterator = this.extraSlots.iterator(); - - while (iterator.hasNext()) { - itemstack = iterator.next(); - if (!itemstack.isEmpty()) { - return false; - } - } - - return true; - } - - @Nullable - @Override - public IChatBaseComponent getCustomName() { - return null; - } - - @Override - public void a(DamageSource damagesource, float f) { - if (f > 0.0F) { - f /= 4.0F; - if (f < 1.0F) { - f = 1.0F; - } - - for (int i = 0; i < this.armor.size(); ++i) { - ItemStack itemstack = this.armor.get(0); - int index = i; - if ((!damagesource.isFire() || !itemstack.getItem().u()) && itemstack.getItem() instanceof ItemArmor) { - itemstack.damage((int) f, this.player, (entityHuman) -> entityHuman.broadcastItemBreak(EnumItemSlot.a(EnumItemSlot.Function.ARMOR, index))); - } - } - } - } - - @Override - public void dropContents() { - for (List itemStacks : this.f) { - for (int i = 0; i < itemStacks.size(); ++i) { - ItemStack itemstack = itemStacks.get(i); - if (!itemstack.isEmpty()) { - itemStacks.set(i, ItemStack.b); - this.player.a(itemstack, true, false); - } - } - } - } - - @Override - public boolean h(ItemStack itemstack) { - return this.f.stream().flatMap(List::stream).anyMatch(itemStack1 -> !itemStack1.isEmpty() && itemStack1.doMaterialsMatch(itemstack)); - } - - @Override - public void a(PlayerInventory playerinventory) { - for (int i = 0; i < playerinventory.getSize(); ++i) { - this.setItem(i, playerinventory.getItem(i)); - } - - this.itemInHandIndex = playerinventory.itemInHandIndex; - } - - @Override - public void clear() { - this.f.forEach(List::clear); - } - - @Override - public void a(AutoRecipeStackManager autorecipestackmanager) { - for (ItemStack itemstack : this.items) { - autorecipestackmanager.a(itemstack); - } - } - -} diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 2c06c72e..613aa381 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -40,6 +40,7 @@ org.spigotmc provided 1.17.1-R0.1-SNAPSHOT + remapped-mojang openinvapi @@ -67,6 +68,38 @@ maven-compiler-plugin + + net.md-5 + specialsource-maven-plugin + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.17.1-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.17.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.17.1-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT:jar:remapped-obf + + + + diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java index 672e8906..514aa924 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java @@ -18,75 +18,102 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.util.ReflectionHelper; import java.lang.reflect.Field; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; -import net.minecraft.core.BlockPosition; -import net.minecraft.network.chat.ChatMessage; -import net.minecraft.network.chat.IChatBaseComponent; -import net.minecraft.server.level.EntityPlayer; -import net.minecraft.server.level.PlayerInteractManager; -import net.minecraft.world.ITileInventory; -import net.minecraft.world.InventoryLargeChest; -import net.minecraft.world.TileInventory; -import net.minecraft.world.entity.player.EntityHuman; -import net.minecraft.world.entity.player.PlayerInventory; -import net.minecraft.world.inventory.Container; -import net.minecraft.world.inventory.ContainerChest; -import net.minecraft.world.inventory.Containers; -import net.minecraft.world.inventory.InventoryEnderChest; -import net.minecraft.world.level.EnumGamemode; -import net.minecraft.world.level.World; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.CompoundContainer; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.BlockBarrel; -import net.minecraft.world.level.block.BlockChest; -import net.minecraft.world.level.block.BlockChestTrapped; -import net.minecraft.world.level.block.BlockShulkerBox; -import net.minecraft.world.level.block.entity.TileEntity; -import net.minecraft.world.level.block.entity.TileEntityChest; -import net.minecraft.world.level.block.entity.TileEntityEnderChest; -import net.minecraft.world.level.block.entity.TileEntityLootable; -import net.minecraft.world.level.block.state.IBlockData; -import net.minecraft.world.level.block.state.properties.BlockPropertyChestType; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.DoubleBlockCombiner; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Statistic; +import org.bukkit.block.ShulkerBox; import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class AnySilentContainer implements IAnySilentContainer { - private Field playerInteractManagerGamemode; + private @Nullable Field serverPlayerGameModeGameType; public AnySilentContainer() { try { - this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("b"); - this.playerInteractManagerGamemode.setAccessible(true); - } catch (NoSuchFieldException | SecurityException e) { + try { + // IDE warns about field not existing, but SpecialSource does not remap strings used in reflection. + // The warning is not suppressed as a reminder that it must manually be checked on updates. + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to directly write player gamemode! SilentChest will fail."); - logger.log(Level.WARNING, "Error obtaining gamemode field", e); + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(Level.WARNING, "Error obtaining GameType field", e); } } @Override - public boolean isShulkerIgnoreBoundingBox(org.bukkit.block.Block bukkitBlock) { - org.bukkit.World bukkitWorld = bukkitBlock.getWorld(); + public boolean isShulkerBlocked(ShulkerBox box) { + org.bukkit.World bukkitWorld = box.getWorld(); if (!(bukkitWorld instanceof CraftWorld)) { bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); } - if (!(bukkitWorld instanceof CraftWorld)) { + + if (!(bukkitWorld instanceof CraftWorld craftWorld)) { Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); return false; } - final World world = ((CraftWorld) bukkitWorld).getHandle(); - final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - // isLargeVoxelShape - return world.getType(blockPosition).d(); + final ServerLevel world = craftWorld.getHandle(); + final BlockPos blockPosition = new BlockPos(box.getX(), box.getY(), box.getZ()); + final BlockEntity tile = world.getBlockEntity(blockPosition); + + if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) + || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { + return false; + } + + BlockState blockState = world.getBlockState(blockPosition); + + // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen + AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) + .move(blockPosition) + .deflate(1.0E-6D); + return !world.noCollision(boundingBox); } @Override @@ -102,128 +129,109 @@ public boolean activateContainer( return true; } - EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - final World world = player.getWorld(); - final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final TileEntity tile = world.getTileEntity(blockPosition); + final ServerLevel level = player.getLevel(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); - if (tile == null) { + if (blockEntity == null) { return false; } - if (tile instanceof TileEntityEnderChest) { + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { // Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest - InventoryEnderChest enderChest = player.getEnderChest(); - enderChest.a((TileEntityEnderChest) tile); - player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> { - Containers containers = PlayerDataManager.getContainers(enderChest.getSize()); - int rows = enderChest.getSize() / 9; - return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows); - }, new ChatMessage("container.enderchest"))); + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, new TextComponent("container.enderchest"))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } - if (!(tile instanceof ITileInventory tileInventory)) { + if (!(blockEntity instanceof MenuProvider menuProvider)) { return false; } - IBlockData blockData = world.getType(blockPosition); - Block block = blockData.getBlock(); - - if (block instanceof BlockChest) { - - BlockPropertyChestType chestType = blockData.get(BlockChest.c); - - if (chestType != BlockPropertyChestType.a) { + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); - BlockPosition adjacentBlockPosition = blockPosition.shift(BlockChest.h(blockData)); - IBlockData adjacentBlockData = world.getType(adjacentBlockPosition); + if (block instanceof ChestBlock chestBlock) { - if (adjacentBlockData.getBlock() == block) { - - BlockPropertyChestType adjacentChestType = adjacentBlockData.get(BlockChest.c); - - if (adjacentChestType != BlockPropertyChestType.a && chestType != adjacentChestType - && adjacentBlockData.get(BlockChest.b) == blockData.get(BlockChest.b)) { - - TileEntity adjacentTile = world.getTileEntity(adjacentBlockPosition); - - if (adjacentTile instanceof TileEntityChest && tileInventory instanceof TileEntityChest) { - TileEntityChest rightChest = chestType == BlockPropertyChestType.c ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile; - TileEntityChest leftChest = chestType == BlockPropertyChestType.c ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory); - - if (silentchest && (rightChest.g != null || leftChest.g != null)) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } + // boolean flag: check if chest is blocked + Optional menuOptional = chestBlock.combine(blockState, level, blockPos, false).apply( + // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER + new DoubleBlockCombiner.Combiner>() { + @Override + public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { + CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); + return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); + } - tileInventory = new ITileInventory() { - public Container createMenu(int containerCounter, PlayerInventory playerInventory, EntityHuman entityHuman) { - leftChest.d(playerInventory.l); - rightChest.d(playerInventory.l); - return ContainerChest.b(containerCounter, playerInventory, new InventoryLargeChest(rightChest, leftChest)); - } + @Override + public Optional acceptSingle(ChestBlockEntity localChest) { + return Optional.of(localChest); + } - public IChatBaseComponent getScoreboardDisplayName() { - if (leftChest.hasCustomName()) { - return leftChest.getScoreboardDisplayName(); - } - if (rightChest.hasCustomName()) { - return rightChest.getScoreboardDisplayName(); - } - return new ChatMessage("container.chestDouble"); - } - }; + @Override + public Optional acceptNone() { + return Optional.empty(); } - } - } + }); + + if (menuOptional.isEmpty()) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; } - if (block instanceof BlockChestTrapped) { + menuProvider = menuOptional.get(); + + if (block instanceof TrappedChestBlock) { bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); } else { bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); } } - if (block instanceof BlockShulkerBox) { + if (block instanceof ShulkerBoxBlock) { bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); } - if (block instanceof BlockBarrel) { + if (block instanceof BarrelBlock) { bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); } // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.d.getGameMode() == EnumGamemode.d) { - player.openContainer(tileInventory); + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); return true; } - // SilentChest requires access to setting players' gamemode directly. - if (this.playerInteractManagerGamemode == null) { + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { return false; } - if (tile instanceof TileEntityLootable lootable) { - if (lootable.g != null) { + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } } - EnumGamemode gamemode = player.d.getGameMode(); - this.forceGameMode(player, EnumGamemode.d); - player.openContainer(tileInventory); - this.forceGameMode(player, gamemode); + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); return true; } @Override public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.playerInteractManagerGamemode == null) { + if (this.serverPlayerGameModeGameType == null) { return; } @@ -238,29 +246,29 @@ public void deactivateContainer(@NotNull final Player bukkitPlayer) { return; } - EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. - EnumGamemode gamemode = player.d.getGameMode(); - this.forceGameMode(player, EnumGamemode.d); + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); - // See EntityPlayer#closeInventory - can't call or we'd recursively deactivate. - player.bV.b(player); - player.bU.a(player.bV); - player.bV = player.bU; + // Close container - note that this is very different from ServerPlayer#closeContainer! + // Triggering event must not be re-called or we'll enter an infinite loop. + player.doCloseContainer(); // Revert forced game mode. - this.forceGameMode(player, gamemode); + this.forceGameType(player, gameType); } - private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) { - if (this.playerInteractManagerGamemode == null) { + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { // No need to warn repeatedly, error on startup and lack of function should be enough. return; } try { - this.playerInteractManagerGamemode.setAccessible(true); - this.playerInteractManagerGamemode.set(player.d, gameMode); + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java index cf420eaa..c4c17e85 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java @@ -17,25 +17,25 @@ package com.lishid.openinv.internal.v1_17_R1; import java.io.File; -import java.io.FileOutputStream; -import net.minecraft.nbt.NBTCompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.server.level.EntityPlayer; -import net.minecraft.world.level.storage.WorldNBTStorage; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; import org.apache.logging.log4j.LogManager; import org.bukkit.craftbukkit.v1_17_R1.CraftServer; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; public class OpenPlayer extends CraftPlayer { - public OpenPlayer(CraftServer server, EntityPlayer entity) { + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @Override public void loadData() { // See CraftPlayer#loadData - NBTTagCompound loaded = this.server.getHandle().r.load(this.getHandle()); + CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); if (loaded != null) { readExtraData(loaded); } @@ -43,35 +43,31 @@ public void loadData() { @Override public void saveData() { - EntityPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.WorldNBTStorage#save(EntityHuman) + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try { - WorldNBTStorage worldNBTStorage = player.c.getPlayerList().r; + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - NBTTagCompound playerData = player.save(new NBTTagCompound()); + CompoundTag playerData = player.saveWithoutId(new CompoundTag()); setExtraData(playerData); if (!isOnline()) { // Special case: save old vehicle data - NBTTagCompound oldData = worldNBTStorage.load(player); + CompoundTag oldData = worldNBTStorage.load(player); - if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) { + if (oldData != null && oldData.contains("RootVehicle", 10)) { // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.set("RootVehicle", oldData.getCompound("RootVehicle")); + playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); } } - File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp"); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat"); - - NBTCompressedStreamTools.a(playerData, new FileOutputStream(file)); - - if (file1.exists() && !file1.delete() || !file.renameTo(file1)) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString()); - } - + File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + NbtIo.writeCompressed(playerData, file); + File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); + File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(file1, file, file2); } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString()); + LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString()); } } diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java index 4071eec7..df7697e7 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java @@ -22,17 +22,16 @@ import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; import java.lang.reflect.Field; -import java.util.logging.Level; import java.util.logging.Logger; -import net.minecraft.network.chat.ChatComponentText; -import net.minecraft.network.protocol.game.PacketPlayOutOpenWindow; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.EntityPlayer; -import net.minecraft.server.level.WorldServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; -import net.minecraft.world.inventory.Container; -import net.minecraft.world.inventory.Containers; -import net.minecraft.world.level.World; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; @@ -55,21 +54,21 @@ public PlayerDataManager() { } catch (NoSuchFieldException e) { Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); - logger.log(Level.WARNING, e.getMessage(), e); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); bukkitEntity = null; } } - public static @NotNull EntityPlayer getHandle(final Player player) { + public static @NotNull ServerPlayer getHandle(final Player player) { if (player instanceof CraftPlayer) { return ((CraftPlayer) player).getHandle(); } Server server = player.getServer(); - EntityPlayer nmsPlayer = null; + ServerPlayer nmsPlayer = null; if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName()); + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); } if (nmsPlayer == null) { @@ -92,13 +91,13 @@ public PlayerDataManager() { GameProfile profile = new GameProfile(offline.getUniqueId(), offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - WorldServer worldServer = server.getWorldServer(World.f); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); if (worldServer == null) { return null; } - EntityPlayer entity = new EntityPlayer(server, worldServer, profile); + ServerPlayer entity = new ServerPlayer(server, worldServer, profile); try { injectPlayer(entity); @@ -116,21 +115,21 @@ public PlayerDataManager() { return target; } - void injectPlayer(EntityPlayer player) throws IllegalAccessException { + void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; } bukkitEntity.setAccessible(true); - bukkitEntity.set(player, new OpenPlayer(player.c.server, player)); + bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); } @NotNull @Override public Player inject(@NotNull Player player) { try { - EntityPlayer nmsPlayer = getHandle(player); + ServerPlayer nmsPlayer = getHandle(player); injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { @@ -143,9 +142,9 @@ public Player inject(@NotNull Player player) { @Override public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - EntityPlayer nmsPlayer = getHandle(player); + ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.b == null) { + if (nmsPlayer.connection == null) { return null; } @@ -155,23 +154,23 @@ public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInve return player.openInventory(inventory.getBukkitInventory()); } - Container container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override - public Containers getType() { + public MenuType getType() { return getContainers(inventory.getBukkitInventory().getSize()); } }; - container.setTitle(new ChatComponentText(view.getTitle())); + container.setTitle(new TextComponent(view.getTitle())); container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); if (container == null) { return null; } - nmsPlayer.b.sendPacket(new PacketPlayOutOpenWindow(container.j, container.getType(), - new ChatComponentText(container.getBukkitView().getTitle()))); - nmsPlayer.bV = container; + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), + new TextComponent(container.getBukkitView().getTitle()))); + nmsPlayer.containerMenu = container; nmsPlayer.initMenu(container); return container.getBukkitView(); @@ -188,15 +187,15 @@ public Containers getType() { } } - static @NotNull Containers getContainers(int inventorySize) { + static @NotNull MenuType getContainers(int inventorySize) { return switch (inventorySize) { - case 9 -> Containers.a; - case 18 -> Containers.b; - case 36 -> Containers.d; // PLAYER - case 41, 45 -> Containers.e; - case 54 -> Containers.f; - default -> Containers.c; // 9x3 + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> MenuType.GENERIC_9x3; // Default 27-slot inventory }; } diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java index 6a6a5920..fed9b564 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java @@ -21,38 +21,38 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.core.NonNullList; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.server.level.EntityPlayer; -import net.minecraft.world.ContainerUtil; -import net.minecraft.world.IInventoryListener; -import net.minecraft.world.entity.player.AutoRecipeStackManager; -import net.minecraft.world.entity.player.EntityHuman; -import net.minecraft.world.inventory.InventoryEnderChest; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ContainerListener; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.PlayerEnderChestContainer; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.TileEntityEnderChest; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import org.bukkit.Location; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity; import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class SpecialEnderChest extends InventoryEnderChest implements ISpecialEnderChest { +public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { private final CraftInventory inventory; - private EntityPlayer owner; - private NonNullList c; + private ServerPlayer owner; + private NonNullList items; private boolean playerOnline; - public SpecialEnderChest(final Player player, final Boolean online) { + public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { super(PlayerDataManager.getHandle(player)); this.inventory = new CraftInventory(this); this.owner = PlayerDataManager.getHandle(player); this.playerOnline = online; - this.c = this.owner.getEnderChest().c; + this.items = this.owner.getEnderChestInventory().items; } @Override @@ -71,15 +71,15 @@ public void setPlayerOffline() { } @Override - public void setPlayerOnline(@NotNull final Player player) { + public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { if (!this.playerOnline) { try { this.owner = PlayerDataManager.getHandle(player); - InventoryEnderChest enderChest = owner.getEnderChest(); - for (int i = 0; i < enderChest.getSize(); ++i) { - enderChest.setItem(i, this.c.get(i)); + PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); + for (int i = 0; i < enderChest.getContainerSize(); ++i) { + enderChest.setItem(i, this.items.get(i)); } - this.c = enderChest.c; + this.items = enderChest.items; enderChest.transaction.addAll(this.transaction); } catch (Exception ignored) {} this.playerOnline = true; @@ -87,65 +87,63 @@ public void setPlayerOnline(@NotNull final Player player) { } @Override - public @NotNull Player getPlayer() { + public @NotNull org.bukkit.entity.Player getPlayer() { return owner.getBukkitEntity(); } @Override - public void update() { - this.owner.getEnderChest().update(); + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); } @Override public List getContents() { - return this.c; + return this.items; } @Override public void onOpen(CraftHumanEntity who) { - super.onOpen(who); - this.owner.getEnderChest().onOpen(who); + this.owner.getEnderChestInventory().onOpen(who); } @Override public void onClose(CraftHumanEntity who) { - super.onClose(who); - this.owner.getEnderChest().onClose(who); + this.owner.getEnderChestInventory().onClose(who); } @Override public List getViewers() { - return this.owner.getEnderChest().getViewers(); + return this.owner.getEnderChestInventory().getViewers(); } @Override - public boolean a(EntityHuman entityhuman) { + public boolean stillValid(Player player) { return true; } @Override - public void a(TileEntityEnderChest tileentityenderchest) { - this.owner.getEnderChest().a(tileentityenderchest); + public void setActiveChest(EnderChestBlockEntity enderChest) { + this.owner.getEnderChestInventory().setActiveChest(enderChest); } @Override - public boolean b(TileEntityEnderChest tileentityenderchest) { - return this.owner.getEnderChest().b(tileentityenderchest); + public boolean isActiveChest(EnderChestBlockEntity enderChest) { + return this.owner.getEnderChestInventory().isActiveChest(enderChest); } @Override public int getMaxStackSize() { - return this.owner.getEnderChest().getMaxStackSize(); + return this.owner.getEnderChestInventory().getMaxStackSize(); } @Override public void setMaxStackSize(int i) { - this.owner.getEnderChest().setMaxStackSize(i); + this.owner.getEnderChestInventory().setMaxStackSize(i); } @Override public InventoryHolder getOwner() { - return this.owner.getEnderChest().getOwner(); + return this.owner.getEnderChestInventory().getOwner(); } @Override @@ -154,58 +152,69 @@ public InventoryHolder getOwner() { } @Override - public void a(IInventoryListener iinventorylistener) { - this.owner.getEnderChest().a(iinventorylistener); + public void addListener(ContainerListener listener) { + this.owner.getEnderChestInventory().addListener(listener); } @Override - public void b(IInventoryListener iinventorylistener) { - this.owner.getEnderChest().b(iinventorylistener); + public void removeListener(ContainerListener listener) { + this.owner.getEnderChestInventory().removeListener(listener); } @Override public ItemStack getItem(int i) { - return i >= 0 && i < this.c.size() ? this.c.get(i) : ItemStack.b; + return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; } @Override - public ItemStack splitStack(int i, int j) { - ItemStack itemstack = ContainerUtil.a(this.c, i, j); + public ItemStack removeItem(int i, int j) { + ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); if (!itemstack.isEmpty()) { - this.update(); + this.setChanged(); } return itemstack; } @Override - public ItemStack a(ItemStack itemstack) { - ItemStack itemstack1 = itemstack.cloneItemStack(); - this.d(itemstack1); - if (itemstack1.isEmpty()) { - return ItemStack.b; + public ItemStack addItem(ItemStack itemstack) { + ItemStack localItem = itemstack.copy(); + this.moveItemToOccupiedSlotsWithSameType(localItem); + if (localItem.isEmpty()) { + return ItemStack.EMPTY; } else { - this.c(itemstack1); - return itemstack1.isEmpty() ? ItemStack.b : itemstack1; + this.moveItemToEmptySlots(localItem); + return localItem.isEmpty() ? ItemStack.EMPTY : localItem; } } - private void c(ItemStack itemstack) { - for(int i = 0; i < this.getSize(); ++i) { - ItemStack itemstack1 = this.getItem(i); - if (itemstack1.isEmpty()) { - this.setItem(i, itemstack.cloneItemStack()); + @Override + public boolean canAddItem(ItemStack itemstack) { + for (ItemStack itemstack1 : this.items) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + private void moveItemToEmptySlots(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (localItem.isEmpty()) { + this.setItem(i, itemstack.copy()); itemstack.setCount(0); return; } } } - private void d(ItemStack itemstack) { - for(int i = 0; i < this.getSize(); ++i) { - ItemStack itemstack1 = this.getItem(i); - if (ItemStack.e(itemstack1, itemstack)) { - this.a(itemstack, itemstack1); + private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (ItemStack.isSameItemSameTags(localItem, itemstack)) { + this.moveItemsBetweenStacks(itemstack, localItem); if (itemstack.isEmpty()) { return; } @@ -213,91 +222,91 @@ private void d(ItemStack itemstack) { } } - private void a(ItemStack itemstack, ItemStack itemstack1) { + private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); if (j > 0) { - itemstack1.add(j); - itemstack.subtract(j); - this.update(); + itemstack1.grow(j); + itemstack.shrink(j); + this.setChanged(); } } @Override - public ItemStack splitWithoutUpdate(int i) { - ItemStack itemstack = this.c.get(i); + public ItemStack removeItemNoUpdate(int i) { + ItemStack itemstack = this.items.get(i); if (itemstack.isEmpty()) { - return ItemStack.b; + return ItemStack.EMPTY; } else { - this.c.set(i, ItemStack.b); + this.items.set(i, ItemStack.EMPTY); return itemstack; } } @Override public void setItem(int i, ItemStack itemstack) { - this.c.set(i, itemstack); + this.items.set(i, itemstack); if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { itemstack.setCount(this.getMaxStackSize()); } - this.update(); + this.setChanged(); } @Override - public int getSize() { - return this.owner.getEnderChest().getSize(); + public int getContainerSize() { + return this.owner.getEnderChestInventory().getContainerSize(); } @Override public boolean isEmpty() { - return this.c.stream().allMatch(ItemStack::isEmpty); + return this.items.stream().allMatch(ItemStack::isEmpty); } @Override - public void startOpen(EntityHuman entityhuman) { + public void startOpen(Player player) { } @Override - public void closeContainer(EntityHuman entityhuman) { + public void stopOpen(Player player) { } @Override - public boolean b(int i, ItemStack itemstack) { + public boolean canPlaceItem(int i, ItemStack itemstack) { return true; } @Override - public void clear() { - this.c.clear(); - this.update(); + public void clearContent() { + this.items.clear(); + this.setChanged(); } @Override - public void a(AutoRecipeStackManager autorecipestackmanager) { - for (ItemStack itemstack : this.c) { - autorecipestackmanager.b(itemstack); + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountStack(itemstack); } } @Override - public List f() { - List list = this.c.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clear(); + public List removeAllItems() { + List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clearContent(); return list; } @Override - public ItemStack a(Item item, int i) { + public ItemStack removeItemType(Item item, int i) { ItemStack itemstack = new ItemStack(item, 0); - for(int j = this.getSize() - 1; j >= 0; --j) { - ItemStack itemstack1 = this.getItem(j); - if (itemstack1.getItem().equals(item)) { + for(int j = this.getContainerSize() - 1; j >= 0; --j) { + ItemStack localItem = this.getItem(j); + if (localItem.getItem().equals(item)) { int k = i - itemstack.getCount(); - ItemStack itemstack2 = itemstack1.cloneAndSubtract(k); - itemstack.add(itemstack2.getCount()); + ItemStack splitItem = localItem.split(k); + itemstack.grow(splitItem.getCount()); if (itemstack.getCount() == i) { break; } @@ -305,34 +314,28 @@ public ItemStack a(Item item, int i) { } if (!itemstack.isEmpty()) { - this.update(); + this.setChanged(); } return itemstack; } - @Override - public boolean b(ItemStack itemStack) { - for (ItemStack itemStack1 : this.c) { - if (itemStack1.isEmpty() || ItemStack.e(itemStack1, itemStack) && itemStack1.getCount() < itemStack1.getMaxStackSize()) { - return true; - } - } - - return false; - } - @Override public String toString() { - return this.c.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); } @Override - public void a(NBTTagList nbttaglist) { - for(int i = 0; i < nbttaglist.size(); ++i) { - ItemStack itemstack = ItemStack.a(nbttaglist.getCompound(i)); - if (!itemstack.isEmpty()) { - this.a(itemstack); + public void fromTag(ListTag listTag) { + for (int i = 0; i < this.getContainerSize(); ++i) { + this.setItem(i, ItemStack.EMPTY); + } + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + if (j < this.getContainerSize()) { + this.setItem(j, ItemStack.of(compoundTag)); } } diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java index 0cf69729..8b2f24a3 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java @@ -20,134 +20,128 @@ import com.lishid.openinv.internal.ISpecialPlayerInventory; import java.util.Collection; import java.util.List; -import java.util.Objects; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.CrashReport; -import net.minecraft.CrashReportSystemDetails; +import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.core.NonNullList; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.network.chat.ChatMessage; -import net.minecraft.network.chat.IChatBaseComponent; -import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; -import net.minecraft.server.level.EntityPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.Tag; -import net.minecraft.world.ContainerUtil; -import net.minecraft.world.IInventory; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.EnumItemSlot; -import net.minecraft.world.entity.player.AutoRecipeStackManager; -import net.minecraft.world.entity.player.EntityHuman; -import net.minecraft.world.entity.player.PlayerInventory; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemArmor; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Location; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity; import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory { +public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { private final CraftInventory inventory; private boolean playerOnline; - private EntityHuman l; - private NonNullList h; - private NonNullList i; - private NonNullList j; - private List> n; + private Player player; + private NonNullList items; + private NonNullList armor; + private NonNullList offhand; + private List> compartments; - public SpecialPlayerInventory(final Player bukkitPlayer, final Boolean online) { + public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { super(PlayerDataManager.getHandle(bukkitPlayer)); this.inventory = new CraftInventory(this); this.playerOnline = online; - this.l = super.l; - this.h = this.l.getInventory().h; - this.i = this.l.getInventory().i; - this.j = this.l.getInventory().j; - this.n = ImmutableList.of(this.h, this.i, this.j); + this.player = super.player; + this.selected = player.getInventory().selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); } @Override - public void setPlayerOnline(@NotNull final Player player) { + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { if (!this.playerOnline) { - EntityPlayer entityPlayer = PlayerDataManager.getHandle(player); + Player entityPlayer = PlayerDataManager.getHandle(player); entityPlayer.getInventory().transaction.addAll(this.transaction); - this.l = entityPlayer; - for (int i = 0; i < getSize(); ++i) { - this.l.getInventory().setItem(i, getRawItem(i)); + this.player = entityPlayer; + for (int i = 0; i < getContainerSize(); ++i) { + this.player.getInventory().setItem(i, getRawItem(i)); } - this.l.getInventory().k = this.k; - this.h = this.l.getInventory().h; - this.i = this.l.getInventory().i; - this.j = this.l.getInventory().j; - this.n = ImmutableList.of(this.h, this.i, this.j); + this.player.getInventory().selected = this.selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); this.playerOnline = true; } } @Override - public boolean a(final EntityHuman entityhuman) { - return true; + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; } @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public boolean isInUse() { + return !this.getViewers().isEmpty(); } @Override - public ItemStack getItem(int i) { - List list = this.h; + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } - if (i >= list.size()) { - i -= list.size(); - list = this.i; - } else { - i = this.getReversedItemSlotNum(i); - } + private @NotNull ItemStack getRawItem(int i) { + return super.getItem(i); + } - if (i >= list.size()) { - i -= list.size(); - list = this.j; - } else if (list == this.i) { - i = this.getReversedArmorSlotNum(i); - } + private void setRawItem(int i, @NotNull ItemStack itemStack) { + super.setItem(i, itemStack); + } + + private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - if (i >= list.size()) { - return ItemStack.b; + private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { + if (index < items.size()) { + return new IndexedCompartment(items, getReversedItemSlotNum(index)); } - return list.get(i); - } + index -= items.size(); - private ItemStack getRawItem(int i) { - NonNullList list = null; - for (NonNullList next : this.n) { - if (i < next.size()) { - list = next; - break; - } - i -= next.size(); + if (index < armor.size()) { + return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); } - return list == null ? ItemStack.b : list.get(i); - } + index -= armor.size(); - @Override - public IChatBaseComponent getDisplayName() { - return new ChatMessage(this.l.getName()); - } + if (index < offhand.size()) { + return new IndexedCompartment(offhand, index); + } - @Override - public boolean hasCustomName() { - return false; + index -= offhand.size(); + + return new IndexedCompartment(null, index); } private int getReversedArmorSlotNum(final int i) { @@ -173,173 +167,81 @@ private int getReversedItemSlotNum(final int i) { return i + 9; } - @Override - public int getSize() { - return 45; + private boolean contains(Predicate predicate) { + return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); } @Override - public boolean isInUse() { - return !this.getViewers().isEmpty(); - } - - @Override - public void setItem(int i, final ItemStack itemstack) { - List list = this.h; - - if (i >= list.size()) { - i -= list.size(); - list = this.i; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.j; - } else if (list == this.i) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - this.l.drop(itemstack, true); - return; - } - - list.set(i, itemstack); - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.l.getBukkitEntity(); + public List getArmorContents() { + return this.armor; } @Override - public ItemStack splitStack(int i, final int j) { - List list = this.h; - - if (i >= list.size()) { - i -= list.size(); - list = this.i; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.j; - } else if (list == this.i) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - return ItemStack.b; - } - - return list.get(i).isEmpty() ? ItemStack.b : ContainerUtil.a(list, i, j); + public void onOpen(CraftHumanEntity who) { + this.player.getInventory().onOpen(who); } @Override - public ItemStack splitWithoutUpdate(int i) { - List list = this.h; - - if (i >= list.size()) { - i -= list.size(); - list = this.i; - } else { - i = this.getReversedItemSlotNum(i); - } - - if (i >= list.size()) { - i -= list.size(); - list = this.j; - } else if (list == this.i) { - i = this.getReversedArmorSlotNum(i); - } - - if (i >= list.size()) { - return ItemStack.b; - } - - if (!list.get(i).isEmpty()) { - ItemStack itemstack = list.get(i); - - list.set(i, ItemStack.b); - return itemstack; - } - - return ItemStack.b; + public void onClose(CraftHumanEntity who) { + this.player.getInventory().onClose(who); } @Override - public List getContents() { - return this.n.stream().flatMap(Collection::stream).collect(Collectors.toList()); + public List getViewers() { + return this.player.getInventory().getViewers(); } @Override - public boolean isEmpty() { - return this.n.stream().flatMap(Collection::stream).allMatch(ItemStack::isEmpty); + public InventoryHolder getOwner() { + return this.player.getBukkitEntity(); } @Override - public List getArmorContents() { - return this.i; + public int getMaxStackSize() { + return this.player.getInventory().getMaxStackSize(); } @Override - public void onOpen(CraftHumanEntity who) { - this.transaction.add(who); - this.l.getInventory().transaction.add(who); + public void setMaxStackSize(int size) { + this.player.getInventory().setMaxStackSize(size); } @Override - public void onClose(CraftHumanEntity who) { - this.transaction.remove(who); - this.l.getInventory().transaction.remove(who); + public Location getLocation() { + return this.player.getBukkitEntity().getLocation(); } @Override - public List getViewers() { - return this.transaction; + public boolean hasCustomName() { + return false; } @Override - public InventoryHolder getOwner() { - return this.l.getBukkitEntity(); - } - - public Location getLocation() { - return this.l.getBukkitEntity().getLocation(); + public List getContents() { + return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); } - /* Below this point largely just copied out of NMS to redirect to our overridden variables. */ - @Override - public ItemStack getItemInHand() { - return d(this.k) ? this.h.get(this.k) : ItemStack.b; + public ItemStack getSelected() { + return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; } - private boolean isSimilarAndNotFull(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.e(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); } @Override public int canHold(ItemStack itemstack) { int remains = itemstack.getCount(); - for(int i = 0; i < this.h.size(); ++i) { - ItemStack itemstack1 = this.getItem(i); + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getRawItem(i); if (itemstack1.isEmpty()) { return itemstack.getCount(); } - if (this.isSimilarAndNotFull(itemstack1, itemstack)) { - remains -= (Math.min(itemstack1.getMaxStackSize(), this.getMaxStackSize())) - itemstack1.getCount(); + if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); } if (remains <= 0) { @@ -347,18 +249,18 @@ public int canHold(ItemStack itemstack) { } } - ItemStack offhandItemStack = this.getItem(this.h.size() + this.i.size()); - if (this.isSimilarAndNotFull(offhandItemStack, itemstack)) { - remains -= (Math.min(offhandItemStack.getMaxStackSize(), this.getMaxStackSize())) - offhandItemStack.getCount(); + ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); + if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { + remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); } return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; } @Override - public int getFirstEmptySlotIndex() { - for(int i = 0; i < this.h.size(); ++i) { - if (this.h.get(i).isEmpty()) { + public int getFreeSlot() { + for(int i = 0; i < this.items.size(); ++i) { + if (this.items.get(i).isEmpty()) { return i; } } @@ -367,38 +269,38 @@ public int getFirstEmptySlotIndex() { } @Override - public void a(ItemStack itemstack) { - int i = this.b(itemstack); - if (d(i)) { - this.k = i; + public void setPickedItem(ItemStack itemstack) { + int i = this.findSlotMatchingItem(itemstack); + if (isHotbarSlot(i)) { + this.selected = i; } else if (i == -1) { - this.k = this.i(); - if (!this.h.get(this.k).isEmpty()) { - int j = this.getFirstEmptySlotIndex(); + this.selected = this.getSuitableHotbarSlot(); + if (!this.items.get(this.selected).isEmpty()) { + int j = this.getFreeSlot(); if (j != -1) { - this.h.set(j, this.h.get(this.k)); + this.items.set(j, this.items.get(this.selected)); } } - this.h.set(this.k, itemstack); + this.items.set(this.selected, itemstack); } else { - this.c(i); + this.pickSlot(i); } } @Override - public void c(int i) { - this.k = this.i(); - ItemStack itemstack = this.h.get(this.k); - this.h.set(this.k, this.h.get(i)); - this.h.set(i, itemstack); + public void pickSlot(int i) { + this.selected = this.getSuitableHotbarSlot(); + ItemStack itemstack = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(i)); + this.items.set(i, itemstack); } @Override - public int b(ItemStack itemstack) { - for (int i = 0; i < this.h.size(); ++i) { - if (!this.h.get(i).isEmpty() && ItemStack.e(itemstack, this.h.get(i))) { + public int findSlotMatchingItem(ItemStack itemstack) { + for(int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { return i; } } @@ -407,10 +309,10 @@ public int b(ItemStack itemstack) { } @Override - public int c(ItemStack itemstack) { - for (int i = 0; i < this.h.size(); ++i) { - ItemStack itemstack1 = this.h.get(i); - if (!this.h.get(i).isEmpty() && ItemStack.e(itemstack, this.h.get(i)) && !this.h.get(i).g() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) { + public int findSlotMatchingUnusedItem(ItemStack itemStack) { + for(int i = 0; i < this.items.size(); ++i) { + ItemStack localItem = this.items.get(i); + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { return i; } } @@ -419,28 +321,28 @@ public int c(ItemStack itemstack) { } @Override - public int i() { + public int getSuitableHotbarSlot() { int i; int j; - for (j = 0; j < 9; ++j) { - i = (this.k + j) % 9; - if (this.h.get(i).isEmpty()) { + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (this.items.get(i).isEmpty()) { return i; } } - for (j = 0; j < 9; ++j) { - i = (this.k + j) % 9; - if (!this.h.get(i).hasEnchantments()) { + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (!this.items.get(i).isEnchanted()) { return i; } } - return this.k; + return this.selected; } @Override - public void a(double d0) { + public void swapPaint(double d0) { if (d0 > 0.0D) { d0 = 1.0D; } @@ -449,79 +351,80 @@ public void a(double d0) { d0 = -1.0D; } - this.k = (int) (this.k - d0); + this.selected = (int) (this.selected - d0); - while (this.k < 0) { - this.k += 9; + while (this.selected < 0) { + this.selected += 9; } - while (this.k >= 9) { - this.k -= 9; + while(this.selected >= 9) { + this.selected -= 9; } - } @Override - public int a(Predicate predicate, int i, IInventory iinventory) { + public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { byte b0 = 0; boolean flag = i == 0; - int j = b0 + ContainerUtil.a(this, predicate, i - b0, flag); - j += ContainerUtil.a(iinventory, predicate, i - j, flag); - ItemStack itemstack = this.l.bV.getCarried(); - j += ContainerUtil.a(itemstack, predicate, i - j, flag); + int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); + j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); + ItemStack itemstack = this.player.containerMenu.getCarried(); + j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); if (itemstack.isEmpty()) { - this.l.bV.setCarried(ItemStack.b); + this.player.containerMenu.setCarried(ItemStack.EMPTY); } return j; } - private int i(ItemStack itemstack) { - int i = this.firstPartial(itemstack); + private int addResource(ItemStack itemstack) { + int i = this.getSlotWithRemainingSpace(itemstack); if (i == -1) { - i = this.getFirstEmptySlotIndex(); + i = this.getFreeSlot(); } - return i == -1 ? itemstack.getCount() : this.d(i, itemstack); + return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); } - private int d(int i, ItemStack itemstack) { + private int addResource(int i, ItemStack itemstack) { Item item = itemstack.getItem(); int j = itemstack.getCount(); - ItemStack itemstack1 = this.getItem(i); - if (itemstack1.isEmpty()) { - itemstack1 = new ItemStack(item, 0); + ItemStack localItemStack = this.getRawItem(i); + if (localItemStack.isEmpty()) { + localItemStack = new ItemStack(item, 0); if (itemstack.hasTag()) { - itemstack1.setTag(Objects.requireNonNull(itemstack.getTag()).clone()); + // hasTag ensures tag not null + //noinspection ConstantConditions + localItemStack.setTag(itemstack.getTag().copy()); } - this.setItem(i, itemstack1); + this.setRawItem(i, localItemStack); } - k = Math.min(j, itemstack1.getMaxStackSize() - itemstack1.getCount()); + int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); - if (k > this.getMaxStackSize() - itemstack1.getCount()) { - k = this.getMaxStackSize() - itemstack1.getCount(); + if (k > this.getMaxStackSize() - localItemStack.getCount()) { + k = this.getMaxStackSize() - localItemStack.getCount(); } if (k != 0) { j -= k; - itemstack1.add(k); - itemstack1.d(5); + localItemStack.grow(k); + localItemStack.setPopTime(5); } return j; } @Override - public int firstPartial(ItemStack itemstack) { - if (this.isSimilarAndNotFull(this.getItem(this.k), itemstack)) { - return this.k; - } else if (this.isSimilarAndNotFull(this.getItem(40), itemstack)) { + public int getSlotWithRemainingSpace(ItemStack itemstack) { + if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { + return this.selected; + } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { return 40; } else { - for(int i = 0; i < this.h.size(); ++i) { - if (this.isSimilarAndNotFull(this.h.get(i), itemstack)) { + for(int i = 0; i < this.items.size(); ++i) { + if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { return i; } } @@ -531,11 +434,11 @@ public int firstPartial(ItemStack itemstack) { } @Override - public void j() { - for (NonNullList nonNullList : this.n) { - for (int i = 0; i < nonNullList.size(); ++i) { - if (!nonNullList.get(i).isEmpty()) { - nonNullList.get(i).a(this.l.t, this.l, i, this.k == i); + public void tick() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (!compartment.get(i).isEmpty()) { + compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); } } } @@ -543,27 +446,27 @@ public void j() { } @Override - public boolean pickup(ItemStack itemStack) { - return this.c(-1, itemStack); + public boolean add(ItemStack itemStack) { + return this.add(-1, itemStack); } @Override - public boolean c(int i, ItemStack itemStack) { + public boolean add(int i, ItemStack itemStack) { if (itemStack.isEmpty()) { return false; } else { try { - if (itemStack.g()) { + if (itemStack.isDamaged()) { if (i == -1) { - i = this.getFirstEmptySlotIndex(); + i = this.getFreeSlot(); } if (i >= 0) { - this.h.set(i, itemStack.cloneItemStack()); - this.h.get(i).d(5); + this.items.set(i, itemStack.copy()); + this.items.get(i).setPopTime(5); itemStack.setCount(0); return true; - } else if (this.l.getAbilities().d) { + } else if (this.player.getAbilities().instabuild) { itemStack.setCount(0); return true; } else { @@ -574,13 +477,13 @@ public boolean c(int i, ItemStack itemStack) { do { j = itemStack.getCount(); if (i == -1) { - itemStack.setCount(this.i(itemStack)); + itemStack.setCount(this.addResource(itemStack)); } else { - itemStack.setCount(this.d(i, itemStack)); + itemStack.setCount(this.addResource(i, itemStack)); } } while(!itemStack.isEmpty() && itemStack.getCount() < j); - if (itemStack.getCount() == j && this.l.getAbilities().d) { + if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { itemStack.setCount(0); return true; } else { @@ -588,39 +491,39 @@ public boolean c(int i, ItemStack itemStack) { } } } catch (Throwable var6) { - CrashReport crashreport = CrashReport.a(var6, "Adding item to inventory"); - CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added"); - crashreportsystemdetails.a("Item ID", Item.getId(itemStack.getItem())); - crashreportsystemdetails.a("Item data", itemStack.getDamage()); - crashreportsystemdetails.a("Item name", () -> itemStack.getName().getString()); - throw new ReportedException(crashreport); + CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); + crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); + crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); + crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); + throw new ReportedException(crashReport); } } } @Override - public void f(ItemStack itemStack) { - this.a(itemStack, true); + public void placeItemBackInInventory(ItemStack itemStack) { + this.placeItemBackInInventory(itemStack, true); } @Override - public void a(ItemStack itemStack, boolean flag) { + public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { while(true) { if (!itemStack.isEmpty()) { - int i = this.firstPartial(itemStack); + int i = this.getSlotWithRemainingSpace(itemStack); if (i == -1) { - i = this.getFirstEmptySlotIndex(); + i = this.getFreeSlot(); } if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getItem(i).getCount(); - if (this.c(i, itemStack.cloneAndSubtract(j)) && flag && this.l instanceof EntityPlayer) { - ((EntityPlayer)this.l).b.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i))); + int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); + if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { + ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); } continue; } - this.l.drop(itemStack, false); + this.player.drop(itemStack, false); } return; @@ -628,11 +531,23 @@ public void a(ItemStack itemStack, boolean flag) { } @Override - public void g(ItemStack itemStack) { - for (NonNullList nonNullList : this.n) { - for (int i = 0; i < nonNullList.size(); ++i) { - if (nonNullList.get(i) == itemStack) { - nonNullList.set(i, ItemStack.b); + public ItemStack removeItem(int rawIndex, final int j) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null + || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { + return ItemStack.EMPTY; + } + + return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); + } + + @Override + public void removeItem(ItemStack itemStack) { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (compartment.get(i) == itemStack) { + compartment.set(i, ItemStack.EMPTY); break; } } @@ -640,61 +555,88 @@ public void g(ItemStack itemStack) { } @Override - public float a(IBlockData iBlockData) { - return this.h.get(this.k).a(iBlockData); + public ItemStack removeItemNoUpdate(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); + + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + + return removed; } @Override - public NBTTagList a(NBTTagList nbtTagList) { - NBTTagCompound nbttagcompound; - int i; - for(i = 0; i < this.h.size(); ++i) { - if (!this.h.get(i).isEmpty()) { - nbttagcompound = new NBTTagCompound(); - nbttagcompound.setByte("Slot", (byte)i); - this.h.get(i).save(nbttagcompound); - nbtTagList.add(nbttagcompound); + public void setItem(int rawIndex, final ItemStack itemStack) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + this.player.drop(itemStack, true); + return; + } + + indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); + } + + @Override + public float getDestroySpeed(BlockState blockState) { + return this.items.get(this.selected).getDestroySpeed(blockState); + } + + @Override + public ListTag save(ListTag listTag) { + for (int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)i); + this.items.get(i).save(compoundTag); + listTag.add(compoundTag); } } - for(i = 0; i < this.i.size(); ++i) { - if (!this.i.get(i).isEmpty()) { - nbttagcompound = new NBTTagCompound(); - nbttagcompound.setByte("Slot", (byte)(i + 100)); - this.i.get(i).save(nbttagcompound); - nbtTagList.add(nbttagcompound); + for (int i = 0; i < this.armor.size(); ++i) { + if (!this.armor.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 100)); + this.armor.get(i).save(compoundTag); + listTag.add(compoundTag); } } - for(i = 0; i < this.j.size(); ++i) { - if (!this.j.get(i).isEmpty()) { - nbttagcompound = new NBTTagCompound(); - nbttagcompound.setByte("Slot", (byte)(i + 150)); - this.j.get(i).save(nbttagcompound); - nbtTagList.add(nbttagcompound); + for (int i = 0; i < this.offhand.size(); ++i) { + if (!this.offhand.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 150)); + this.offhand.get(i).save(compoundTag); + listTag.add(compoundTag); } } - return nbtTagList; + return listTag; } @Override - public void b(NBTTagList nbtTagList) { - this.h.clear(); - this.i.clear(); - this.j.clear(); + public void load(ListTag listTag) { + this.items.clear(); + this.armor.clear(); + this.offhand.clear(); - for(int i = 0; i < nbtTagList.size(); ++i) { - NBTTagCompound nbttagcompound = nbtTagList.getCompound(i); - int j = nbttagcompound.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.a(nbttagcompound); + for(int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.of(compoundTag); if (!itemstack.isEmpty()) { - if (j < this.h.size()) { - this.h.set(j, itemstack); - } else if (j >= 100 && j < this.i.size() + 100) { - this.i.set(j - 100, itemstack); - } else if (j >= 150 && j < this.j.size() + 150) { - this.j.set(j - 150, itemstack); + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); } } } @@ -702,76 +644,126 @@ public void b(NBTTagList nbtTagList) { } @Override - public ItemStack e(int i) { - return this.i.get(i); + public int getContainerSize() { + return 45; + } + + @Override + public boolean isEmpty() { + return !contains(itemStack -> !itemStack.isEmpty()); + } + + @Override + public ItemStack getItem(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + return indexedCompartment.compartment().get(indexedCompartment.index()); + } + + @Override + public Component getName() { + return this.player.getName(); + } + + @Override + public ItemStack getArmor(int index) { + return this.armor.get(index); } @Override - public void a(DamageSource damageSource, float f, int[] intArray) { - if (f > 0.0F) { - f /= 4.0F; - if (f < 1.0F) { - f = 1.0F; + public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { + if (damage > 0.0F) { + damage /= 4.0F; + if (damage < 1.0F) { + damage = 1.0F; } - for (int index : intArray) { - ItemStack itemstack = this.i.get(index); - if ((!damageSource.isFire() || !itemstack.getItem().w()) && itemstack.getItem() instanceof ItemArmor) { - itemstack.damage((int) f, this.l, (entityHuman) -> entityHuman.broadcastItemBreak(EnumItemSlot.a(EnumItemSlot.Function.b, index))); + for (int index : armorIndices) { + ItemStack itemstack = this.armor.get(index); + if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { + itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); } } } - } @Override - public void dropContents() { - for (List list : this.n) { - for (int i = 0; i < list.size(); ++i) { - ItemStack itemstack = list.get(i); + public void dropAll() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + ItemStack itemstack = compartment.get(i); if (!itemstack.isEmpty()) { - list.set(i, ItemStack.b); - this.l.a(itemstack, true, false); + this.player.drop(itemstack, true, false); + compartment.set(i, ItemStack.EMPTY); } } } } @Override - public boolean h(ItemStack itemStack) { - return this.n.stream() - .flatMap(Collection::stream) - .anyMatch(itemStack1 -> !itemStack1.isEmpty() && itemStack1.doMaterialsMatch(itemStack)); + public void setChanged() { + super.setChanged(); + } + + @Override + public int getTimesChanged() { + return super.getTimesChanged(); } @Override - public boolean a(Tag tag) { - return this.n.stream() - .flatMap(Collection::stream) - .anyMatch(itemStack -> !itemStack.isEmpty() && itemStack.a(tag)); + public boolean stillValid(Player player) { + return true; } @Override - public void a(PlayerInventory playerInventory) { - for(int i = 0; i < this.getSize(); ++i) { - this.setItem(i, playerInventory.getItem(i)); + public boolean contains(ItemStack itemstack) { + return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); + } + + @Override + public boolean contains(Tag tag) { + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag)); + } + + @Override + public void replaceWith(Inventory inventory) { + Function getter; + + if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { + getter = specialPlayerInventory::getRawItem; + } else { + getter = inventory::getItem; } - this.k = playerInventory.k; + for(int i = 0; i < this.getContainerSize(); ++i) { + this.setRawItem(i, getter.apply(i)); + } + + this.selected = inventory.selected; } @Override - public void clear() { - for (List list : this.n) { - list.clear(); + public void clearContent() { + for (NonNullList compartment : this.compartments) { + compartment.clear(); } } @Override - public void a(AutoRecipeStackManager autoRecipeStackManager) { - for (ItemStack itemstack : this.h) { - autoRecipeStackManager.a(itemstack); + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountSimpleStack(itemstack); } } + @Override + public ItemStack removeFromSelected(boolean dropWholeStack) { + ItemStack itemstack = this.getSelected(); + return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 4f054074..090f5cff 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -83,6 +83,8 @@ public String getReleasesLink() { case "v1_16_R2": return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; case "v1_16_R3": + return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; + case "v1_17_R1": default: return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/pom.xml b/pom.xml index c26be77a..ef04ab1b 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,6 @@ api plugin - internal/v1_16_R3 internal/v1_17_R1 assembly @@ -139,6 +138,12 @@ org.apache.maven.plugins 3.3.0 + + + net.md-5 + specialsource-maven-plugin + 1.2.2 + diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh index 29376e0f..4dca7171 100644 --- a/scripts/get_spigot_versions.sh +++ b/scripts/get_spigot_versions.sh @@ -15,8 +15,8 @@ # along with this program. If not, see . # -# TODO FIGURE OUT AND REMOVE WHEN LESS STRESS -hacky_versions=("1.16.5-R0.1-SNAPSHOT" "1.17.1-R0.1-SNAPSHOT") +# TODO Fix script for bash version used in Actions images +hacky_versions=("1.17.1-R0.1-SNAPSHOT") for hacky_version in "${hacky_versions[@]}"; do echo "$hacky_version" done diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot_dependencies.sh index 0beac8f3..336da7db 100644 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot_dependencies.sh @@ -36,12 +36,12 @@ echo Found Spigot dependencies: "${versions[@]}" for version in "${versions[@]}"; do set -e exit_code=0 - mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version" -q -o || exit_code=$? + mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version":remapped-mojang -q -o || exit_code=$? if [ $exit_code -ne 0 ]; then echo Installing missing Spigot version "$version" revision=${version%%-R*} get_buildtools - java -jar $buildtools -rev "$revision" + java -jar $buildtools -rev "$revision" --remapped else echo Spigot "$version" is already installed fi From e3d9cea0274a6b0ee2d883925913be9c165874c7 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 16 Oct 2021 17:54:30 -0400 Subject: [PATCH 036/340] Fix spigot dependency parsing * Re-add cache * Work around reflection issues with mvn help:evaluate on Java 9+ --- scripts/get_spigot_versions.sh | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh index 4dca7171..1e079f39 100644 --- a/scripts/get_spigot_versions.sh +++ b/scripts/get_spigot_versions.sh @@ -15,21 +15,32 @@ # along with this program. If not, see . # -# TODO Fix script for bash version used in Actions images -hacky_versions=("1.17.1-R0.1-SNAPSHOT") -for hacky_version in "${hacky_versions[@]}"; do - echo "$hacky_version" -done - -exit 0 - # Note that this script is designed for use in GitHub Actions, and is not # particularly robust nor configurable. Run from project parent directory. +# Use a nameref as a cache - maven evaluation is pretty slow. +# Re-calling the script and relying on it to handle caching is way easier than passing around info. +declare -a spigot_versions + +# We don't care about concatenation - either it's not null and we handle entries or it's null and we instantiate. +# shellcheck disable=SC2199 +if [[ ${spigot_versions[@]} ]]; then + for spigot_version in "${spigot_versions[@]}"; do + echo "$spigot_version" + done + return +fi + +old_maven_opts=$MAVEN_OPTS +# Add JVM parameters to allow help plugin access to packages it needs. +export MAVEN_OPTS="$old_maven_opts --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED" + # Pull Spigot dependency information from Maven. # Since we only care about Spigot versions, only check modules in the folder internal. readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all | grep -oP '(?<=)(internal/.*)(?=)')" +declare -n versions="spigot_versions" + for module in "${modules[@]}"; do # Get number of dependencies declared in pom of specified internal module. max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl "$module" | grep -c "") @@ -48,3 +59,6 @@ for module in "${modules[@]}"; do fi done done + +# Reset JVM parameters +export MAVEN_OPTS=$old_maven_opts \ No newline at end of file From b4601bb6c1e7f7e8fc53e448747477dd7b2e55f8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 16 Oct 2021 19:16:08 -0400 Subject: [PATCH 037/340] Fix cached Spigot dependencies never being used Since plugins have not been downloaded, get always fails when running in offline mode. --- scripts/install_spigot_dependencies.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot_dependencies.sh index 336da7db..dd3cbfc5 100644 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot_dependencies.sh @@ -33,6 +33,11 @@ get_buildtools () { readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" echo Found Spigot dependencies: "${versions[@]}" +# Install dependencies aside from Spigot prior to running in offline mode. +# Note that the default SuperPOM declares maven-dependency-plugin 2.8.0. +# Unfortunately, we run into MDEP-204 and require a version >= 3.1.2. +mvn org.apache.maven.plugins:maven-dependency-plugin:3.2.0:go-offline -DexcludeArtifactIds=spigot + for version in "${versions[@]}"; do set -e exit_code=0 From dd90b5281f6a966d99916dbf12d70e17755c9944 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 17 Nov 2021 10:41:53 -0500 Subject: [PATCH 038/340] Use Java 17 for CI --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49c156ba..b190db22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,9 +12,10 @@ jobs: uses: actions/checkout@v2 - name: Set Up Java - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: - java-version: 16 + distribution: 'adopt' + java-version: '17' # Use cache to speed up build - name: Cache Maven Repo From d236dd27b3760f4ac7d9a4c51b4b306577d03a80 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 23 Nov 2021 13:00:42 -0500 Subject: [PATCH 039/340] Update to 1.18-pre5 --- internal/v1_18_R1/pom.xml | 112 +++ .../internal/v1_18_R1/AnySilentContainer.java | 277 +++++++ .../openinv/internal/v1_18_R1/OpenPlayer.java | 74 ++ .../internal/v1_18_R1/PlayerDataManager.java | 230 ++++++ .../internal/v1_18_R1/SpecialEnderChest.java | 344 ++++++++ .../v1_18_R1/SpecialPlayerInventory.java | 769 ++++++++++++++++++ pom.xml | 1 + 7 files changed, 1807 insertions(+) create mode 100644 internal/v1_18_R1/pom.xml create mode 100644 internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java create mode 100644 internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java create mode 100644 internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java create mode 100644 internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java create mode 100644 internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml new file mode 100644 index 00000000..8629e1e9 --- /dev/null +++ b/internal/v1_18_R1/pom.xml @@ -0,0 +1,112 @@ + + + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 4.1.9-SNAPSHOT + + + openinvadapter1_18_R1 + OpenInvAdapter1_18_R1 + + + 17 + 17 + 1.18-pre5-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot-api + ${spigot.version} + + + spigot + org.spigotmc + provided + ${spigot.version} + remapped-mojang + + + openinvapi + com.lishid + + + openinvplugincore + com.lishid + + + annotations + org.jetbrains + + + + + + + maven-shade-plugin + + + false + + + + maven-compiler-plugin + + + net.md-5 + specialsource-maven-plugin + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang + true + org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot + org.spigotmc:spigot:${spigot.version}:jar:remapped-obf + + + + + + + + diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java new file mode 100644 index 00000000..a1d82727 --- /dev/null +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R1; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.util.ReflectionHelper; +import java.lang.reflect.Field; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.CompoundContainer; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.DoubleBlockCombiner; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.ShulkerBox; +import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AnySilentContainer implements IAnySilentContainer { + + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer() { + try { + try { + // IDE warns about field not existing, but SpecialSource does not remap strings used in reflection. + // The warning is not suppressed as a reminder that it must manually be checked on updates. + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(Level.WARNING, "Error obtaining GameType field", e); + } + } + + @Override + public boolean isShulkerBlocked(ShulkerBox box) { + org.bukkit.World bukkitWorld = box.getWorld(); + if (!(bukkitWorld instanceof CraftWorld)) { + bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); + } + + if (!(bukkitWorld instanceof CraftWorld craftWorld)) { + Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); + OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); + return false; + } + + final ServerLevel world = craftWorld.getHandle(); + final BlockPos blockPosition = new BlockPos(box.getX(), box.getY(), box.getZ()); + final BlockEntity tile = world.getBlockEntity(blockPosition); + + if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) + || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { + return false; + } + + BlockState blockState = world.getBlockState(blockPosition); + + // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen + AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) + .move(blockPosition) + .deflate(1.0E-6D); + return !world.noCollision(boundingBox); + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + final ServerLevel level = player.getLevel(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); + + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, new TextComponent("container.enderchest"))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: check if chest is blocked + Optional menuOptional = chestBlock.combine(blockState, level, blockPos, false).apply( + // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER + new DoubleBlockCombiner.Combiner>() { + @Override + public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { + CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); + return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); + } + + @Override + public Optional acceptSingle(ChestBlockEntity localChest) { + return Optional.of(localChest); + } + + @Override + public Optional acceptNone() { + return Optional.empty(); + } + }); + + if (menuOptional.isEmpty()) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + menuProvider = menuOptional.get(); + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; + } + + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null) { + return; + } + + InventoryView view = bukkitPlayer.getOpenInventory(); + switch (view.getType()) { + case CHEST: + case ENDER_CHEST: + case SHULKER_BOX: + case BARREL: + break; + default: + return; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // Close container - note that this is very different from ServerPlayer#closeContainer! + // Triggering event must not be re-called or we'll enter an infinite loop. + player.doCloseContainer(); + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java new file mode 100644 index 00000000..2a0a7793 --- /dev/null +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R1; + +import java.io.File; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.apache.logging.log4j.LogManager; +import org.bukkit.craftbukkit.v1_18_R1.CraftServer; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; + +public class OpenPlayer extends CraftPlayer { + + public OpenPlayer(CraftServer server, ServerPlayer entity) { + super(server, entity); + } + + @Override + public void loadData() { + // See CraftPlayer#loadData + CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); + if (loaded != null) { + readExtraData(loaded); + } + } + + @Override + public void saveData() { + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + setExtraData(playerData); + + if (!isOnline()) { + // Special case: save old vehicle data + CompoundTag oldData = worldNBTStorage.load(player); + + if (oldData != null && oldData.contains("RootVehicle", 10)) { + // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) + playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + } + + File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + NbtIo.writeCompressed(playerData, file); + File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); + File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(file1, file, file2); + } catch (Exception e) { + LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString()); + } + } + +} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java new file mode 100644 index 00000000..a982b998 --- /dev/null +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R1; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IPlayerDataManager; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.OpenInventoryView; +import com.mojang.authlib.GameProfile; +import java.lang.reflect.Field; +import java.util.logging.Logger; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_18_R1.CraftServer; +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.CraftContainer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerDataManager implements IPlayerDataManager { + + private @Nullable Field bukkitEntity; + + public PlayerDataManager() { + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } + + // Create a profile and entity to load the player data + // See net.minecraft.server.PlayerList#attemptLogin + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + + if (worldServer == null) { + return null; + } + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + // Get the bukkit entity + Player target = entity.getBukkitEntity(); + if (target != null) { + // Load data + target.loadData(); + } + // Return the entity + return target; + } + + void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + } + + @NotNull + @Override + public Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return player; + } + } + + @Nullable + @Override + public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + + ServerPlayer nmsPlayer = getHandle(player); + + if (nmsPlayer.connection == null) { + return null; + } + + InventoryView view = getView(player, inventory); + + if (view == null) { + return player.openInventory(inventory.getBukkitInventory()); + } + + AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + @Override + public MenuType getType() { + return getContainers(inventory.getBukkitInventory().getSize()); + } + }; + + container.setTitle(new TextComponent(view.getTitle())); + container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + + if (container == null) { + return null; + } + + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), + new TextComponent(container.getBukkitView().getTitle()))); + nmsPlayer.containerMenu = container; + nmsPlayer.initMenu(container); + + return container.getBukkitView(); + + } + + private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { + if (inventory instanceof SpecialEnderChest) { + return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); + } else if (inventory instanceof SpecialPlayerInventory) { + return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); + } else { + return null; + } + } + + static @NotNull MenuType getContainers(int inventorySize) { + + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> MenuType.GENERIC_9x3; // Default 27-slot inventory + }; + } + + @Override + public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java new file mode 100644 index 00000000..d10bfddd --- /dev/null +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R1; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ContainerListener; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { + + private final CraftInventory inventory; + private ServerPlayer owner; + private NonNullList items; + private boolean playerOnline; + + public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { + super(PlayerDataManager.getHandle(player)); + this.inventory = new CraftInventory(this); + this.owner = PlayerDataManager.getHandle(player); + this.playerOnline = online; + this.items = this.owner.getEnderChestInventory().items; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return inventory; + } + + @Override + public boolean isInUse() { + return !this.getViewers().isEmpty(); + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { + if (!this.playerOnline) { + try { + this.owner = PlayerDataManager.getHandle(player); + PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); + for (int i = 0; i < enderChest.getContainerSize(); ++i) { + enderChest.setItem(i, this.items.get(i)); + } + this.items = enderChest.items; + enderChest.transaction.addAll(this.transaction); + } catch (Exception ignored) {} + this.playerOnline = true; + } + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public List getContents() { + return this.items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.owner.getEnderChestInventory().getViewers(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void setActiveChest(EnderChestBlockEntity enderChest) { + this.owner.getEnderChestInventory().setActiveChest(enderChest); + } + + @Override + public boolean isActiveChest(EnderChestBlockEntity enderChest) { + return this.owner.getEnderChestInventory().isActiveChest(enderChest); + } + + @Override + public int getMaxStackSize() { + return this.owner.getEnderChestInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int i) { + this.owner.getEnderChestInventory().setMaxStackSize(i); + } + + @Override + public InventoryHolder getOwner() { + return this.owner.getEnderChestInventory().getOwner(); + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void addListener(ContainerListener listener) { + this.owner.getEnderChestInventory().addListener(listener); + } + + @Override + public void removeListener(ContainerListener listener) { + this.owner.getEnderChestInventory().removeListener(listener); + } + + @Override + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int i, int j) { + ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack addItem(ItemStack itemstack) { + ItemStack localItem = itemstack.copy(); + this.moveItemToOccupiedSlotsWithSameType(localItem); + if (localItem.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.moveItemToEmptySlots(localItem); + return localItem.isEmpty() ? ItemStack.EMPTY : localItem; + } + } + + @Override + public boolean canAddItem(ItemStack itemstack) { + for (ItemStack itemstack1 : this.items) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + private void moveItemToEmptySlots(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (localItem.isEmpty()) { + this.setItem(i, itemstack.copy()); + itemstack.setCount(0); + return; + } + } + } + + private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (ItemStack.isSameItemSameTags(localItem, itemstack)) { + this.moveItemsBetweenStacks(itemstack, localItem); + if (itemstack.isEmpty()) { + return; + } + } + } + } + + private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { + int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); + int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); + if (j > 0) { + itemstack1.grow(j); + itemstack.shrink(j); + this.setChanged(); + } + } + + @Override + public ItemStack removeItemNoUpdate(int i) { + ItemStack itemstack = this.items.get(i); + if (itemstack.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.items.set(i, ItemStack.EMPTY); + return itemstack; + } + } + + @Override + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.setChanged(); + } + + @Override + public int getContainerSize() { + return this.owner.getEnderChestInventory().getContainerSize(); + } + + @Override + public boolean isEmpty() { + return this.items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void startOpen(Player player) { + } + + @Override + public void stopOpen(Player player) { + } + + @Override + public boolean canPlaceItem(int i, ItemStack itemstack) { + return true; + } + + @Override + public void clearContent() { + this.items.clear(); + this.setChanged(); + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountStack(itemstack); + } + + } + + @Override + public List removeAllItems() { + List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clearContent(); + return list; + } + + @Override + public ItemStack removeItemType(Item item, int i) { + ItemStack itemstack = new ItemStack(item, 0); + + for(int j = this.getContainerSize() - 1; j >= 0; --j) { + ItemStack localItem = this.getItem(j); + if (localItem.getItem().equals(item)) { + int k = i - itemstack.getCount(); + ItemStack splitItem = localItem.split(k); + itemstack.grow(splitItem.getCount()); + if (itemstack.getCount() == i) { + break; + } + } + } + + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public String toString() { + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); + } + + @Override + public void fromTag(ListTag listTag) { + for (int i = 0; i < this.getContainerSize(); ++i) { + this.setItem(i, ItemStack.EMPTY); + } + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + if (j < this.getContainerSize()) { + this.setItem(j, ItemStack.of(compoundTag)); + } + } + + } + +} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java new file mode 100644 index 00000000..ffb96d33 --- /dev/null +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java @@ -0,0 +1,769 @@ +/* + * Copyright (C) 2011-2021 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R1; + +import com.google.common.collect.ImmutableList; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.Tag; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { + + private final CraftInventory inventory; + private boolean playerOnline; + private Player player; + private NonNullList items; + private NonNullList armor; + private NonNullList offhand; + private List> compartments; + + public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { + super(PlayerDataManager.getHandle(bukkitPlayer)); + this.inventory = new CraftInventory(this); + this.playerOnline = online; + this.player = super.player; + this.selected = player.getInventory().selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + if (!this.playerOnline) { + Player entityPlayer = PlayerDataManager.getHandle(player); + entityPlayer.getInventory().transaction.addAll(this.transaction); + this.player = entityPlayer; + for (int i = 0; i < getContainerSize(); ++i) { + this.player.getInventory().setItem(i, getRawItem(i)); + } + this.player.getInventory().selected = this.selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + this.playerOnline = true; + } + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public boolean isInUse() { + return !this.getViewers().isEmpty(); + } + + @Override + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } + + private @NotNull ItemStack getRawItem(int i) { + return super.getItem(i); + } + + private void setRawItem(int i, @NotNull ItemStack itemStack) { + super.setItem(i, itemStack); + } + + private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + + private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { + if (index < items.size()) { + return new IndexedCompartment(items, getReversedItemSlotNum(index)); + } + + index -= items.size(); + + if (index < armor.size()) { + return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); + } + + index -= armor.size(); + + if (index < offhand.size()) { + return new IndexedCompartment(offhand, index); + } + + index -= offhand.size(); + + return new IndexedCompartment(null, index); + } + + private int getReversedArmorSlotNum(final int i) { + if (i == 0) { + return 3; + } + if (i == 1) { + return 2; + } + if (i == 2) { + return 1; + } + if (i == 3) { + return 0; + } + return i; + } + + private int getReversedItemSlotNum(final int i) { + if (i >= 27) { + return i - 27; + } + return i + 9; + } + + private boolean contains(Predicate predicate) { + return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); + } + + @Override + public List getArmorContents() { + return this.armor; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.player.getInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.player.getInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.player.getInventory().getViewers(); + } + + @Override + public InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + @Override + public int getMaxStackSize() { + return this.player.getInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int size) { + this.player.getInventory().setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return this.player.getBukkitEntity().getLocation(); + } + + @Override + public boolean hasCustomName() { + return false; + } + + @Override + public List getContents() { + return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + + @Override + public ItemStack getSelected() { + return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; + } + + private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + @Override + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getRawItem(i); + if (itemstack1.isEmpty()) { + return itemstack.getCount(); + } + + if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); + } + + if (remains <= 0) { + return itemstack.getCount(); + } + } + + ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); + if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { + remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); + } + + return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; + } + + @Override + public int getFreeSlot() { + for(int i = 0; i < this.items.size(); ++i) { + if (this.items.get(i).isEmpty()) { + return i; + } + } + + return -1; + } + + @Override + public void setPickedItem(ItemStack itemstack) { + int i = this.findSlotMatchingItem(itemstack); + if (isHotbarSlot(i)) { + this.selected = i; + } else if (i == -1) { + this.selected = this.getSuitableHotbarSlot(); + if (!this.items.get(this.selected).isEmpty()) { + int j = this.getFreeSlot(); + if (j != -1) { + this.items.set(j, this.items.get(this.selected)); + } + } + + this.items.set(this.selected, itemstack); + } else { + this.pickSlot(i); + } + + } + + @Override + public void pickSlot(int i) { + this.selected = this.getSuitableHotbarSlot(); + ItemStack itemstack = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(i)); + this.items.set(i, itemstack); + } + + @Override + public int findSlotMatchingItem(ItemStack itemstack) { + for(int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { + return i; + } + } + + return -1; + } + + @Override + public int findSlotMatchingUnusedItem(ItemStack itemStack) { + for(int i = 0; i < this.items.size(); ++i) { + ItemStack localItem = this.items.get(i); + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { + return i; + } + } + + return -1; + } + + @Override + public int getSuitableHotbarSlot() { + int i; + int j; + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (this.items.get(i).isEmpty()) { + return i; + } + } + + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (!this.items.get(i).isEnchanted()) { + return i; + } + } + + return this.selected; + } + + @Override + public void swapPaint(double d0) { + if (d0 > 0.0D) { + d0 = 1.0D; + } + + if (d0 < 0.0D) { + d0 = -1.0D; + } + + this.selected = (int) (this.selected - d0); + + while (this.selected < 0) { + this.selected += 9; + } + + while(this.selected >= 9) { + this.selected -= 9; + } + } + + @Override + public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { + byte b0 = 0; + boolean flag = i == 0; + int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); + j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); + ItemStack itemstack = this.player.containerMenu.getCarried(); + j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); + if (itemstack.isEmpty()) { + this.player.containerMenu.setCarried(ItemStack.EMPTY); + } + + return j; + } + + private int addResource(ItemStack itemstack) { + int i = this.getSlotWithRemainingSpace(itemstack); + if (i == -1) { + i = this.getFreeSlot(); + } + + return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); + } + + private int addResource(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + int j = itemstack.getCount(); + ItemStack localItemStack = this.getRawItem(i); + if (localItemStack.isEmpty()) { + localItemStack = new ItemStack(item, 0); + if (itemstack.hasTag()) { + // hasTag ensures tag not null + //noinspection ConstantConditions + localItemStack.setTag(itemstack.getTag().copy()); + } + + this.setRawItem(i, localItemStack); + } + + int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); + + if (k > this.getMaxStackSize() - localItemStack.getCount()) { + k = this.getMaxStackSize() - localItemStack.getCount(); + } + + if (k != 0) { + j -= k; + localItemStack.grow(k); + localItemStack.setPopTime(5); + } + + return j; + } + + @Override + public int getSlotWithRemainingSpace(ItemStack itemstack) { + if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { + return this.selected; + } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { + return 40; + } else { + for(int i = 0; i < this.items.size(); ++i) { + if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + @Override + public void tick() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (!compartment.get(i).isEmpty()) { + compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); + } + } + } + + } + + @Override + public boolean add(ItemStack itemStack) { + return this.add(-1, itemStack); + } + + @Override + public boolean add(int i, ItemStack itemStack) { + if (itemStack.isEmpty()) { + return false; + } else { + try { + if (itemStack.isDamaged()) { + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i >= 0) { + this.items.set(i, itemStack.copy()); + this.items.get(i).setPopTime(5); + itemStack.setCount(0); + return true; + } else if (this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + do { + j = itemStack.getCount(); + if (i == -1) { + itemStack.setCount(this.addResource(itemStack)); + } else { + itemStack.setCount(this.addResource(i, itemStack)); + } + } while(!itemStack.isEmpty() && itemStack.getCount() < j); + + if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return itemStack.getCount() < j; + } + } + } catch (Throwable var6) { + CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); + crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); + crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); + crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); + throw new ReportedException(crashReport); + } + } + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack) { + this.placeItemBackInInventory(itemStack, true); + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { + while(true) { + if (!itemStack.isEmpty()) { + int i = this.getSlotWithRemainingSpace(itemStack); + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i != -1) { + int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); + if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { + ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); + } + continue; + } + + this.player.drop(itemStack, false); + } + + return; + } + } + + @Override + public ItemStack removeItem(int rawIndex, final int j) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null + || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { + return ItemStack.EMPTY; + } + + return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); + } + + @Override + public void removeItem(ItemStack itemStack) { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (compartment.get(i) == itemStack) { + compartment.set(i, ItemStack.EMPTY); + break; + } + } + } + } + + @Override + public ItemStack removeItemNoUpdate(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); + + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + + return removed; + } + + @Override + public void setItem(int rawIndex, final ItemStack itemStack) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + this.player.drop(itemStack, true); + return; + } + + indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); + } + + @Override + public float getDestroySpeed(BlockState blockState) { + return this.items.get(this.selected).getDestroySpeed(blockState); + } + + @Override + public ListTag save(ListTag listTag) { + for (int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)i); + this.items.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.armor.size(); ++i) { + if (!this.armor.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 100)); + this.armor.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.offhand.size(); ++i) { + if (!this.offhand.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 150)); + this.offhand.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + return listTag; + } + + @Override + public void load(ListTag listTag) { + this.items.clear(); + this.armor.clear(); + this.offhand.clear(); + + for(int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.of(compoundTag); + if (!itemstack.isEmpty()) { + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); + } + } + } + + } + + @Override + public int getContainerSize() { + return 45; + } + + @Override + public boolean isEmpty() { + return !contains(itemStack -> !itemStack.isEmpty()); + } + + @Override + public ItemStack getItem(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + return indexedCompartment.compartment().get(indexedCompartment.index()); + } + + @Override + public Component getName() { + return this.player.getName(); + } + + @Override + public ItemStack getArmor(int index) { + return this.armor.get(index); + } + + @Override + public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { + if (damage > 0.0F) { + damage /= 4.0F; + if (damage < 1.0F) { + damage = 1.0F; + } + + for (int index : armorIndices) { + ItemStack itemstack = this.armor.get(index); + if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { + itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); + } + } + } + } + + @Override + public void dropAll() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + ItemStack itemstack = compartment.get(i); + if (!itemstack.isEmpty()) { + this.player.drop(itemstack, true, false); + compartment.set(i, ItemStack.EMPTY); + } + } + } + } + + @Override + public void setChanged() { + super.setChanged(); + } + + @Override + public int getTimesChanged() { + return super.getTimesChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public boolean contains(ItemStack itemstack) { + return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); + } + + @Override + public boolean contains(Tag tag) { + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag)); + } + + @Override + public void replaceWith(Inventory inventory) { + Function getter; + + if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { + getter = specialPlayerInventory::getRawItem; + } else { + getter = inventory::getItem; + } + + for(int i = 0; i < this.getContainerSize(); ++i) { + this.setRawItem(i, getter.apply(i)); + } + + this.selected = inventory.selected; + } + + @Override + public void clearContent() { + for (NonNullList compartment : this.compartments) { + compartment.clear(); + } + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountSimpleStack(itemstack); + } + } + + @Override + public ItemStack removeFromSelected(boolean dropWholeStack) { + ItemStack itemstack = this.getSelected(); + return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); + } + +} diff --git a/pom.xml b/pom.xml index ef04ab1b..ceb20dda 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ api plugin internal/v1_17_R1 + internal/v1_18_R1 assembly From 98fbcebfd950c9db6faf7b713c0b6b5b58c38ab8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 2 Dec 2021 15:44:19 -0500 Subject: [PATCH 040/340] Fix non-translatable component, wrong flag value --- .../openinv/internal/v1_17_R1/AnySilentContainer.java | 8 ++++---- internal/v1_18_R1/pom.xml | 2 +- .../openinv/internal/v1_18_R1/AnySilentContainer.java | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java index 514aa924..53feec82 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java @@ -24,7 +24,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; @@ -147,7 +147,7 @@ public boolean activateContainer( MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, new TextComponent("container.enderchest"))); + }, new TranslatableComponent("container.enderchest"))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } @@ -161,8 +161,8 @@ public boolean activateContainer( if (block instanceof ChestBlock chestBlock) { - // boolean flag: check if chest is blocked - Optional menuOptional = chestBlock.combine(blockState, level, blockPos, false).apply( + // boolean flag: do not check if chest is blocked + Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER new DoubleBlockCombiner.Combiner>() { @Override diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index 8629e1e9..e702bb01 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -32,7 +32,7 @@ 17 17 - 1.18-pre5-R0.1-SNAPSHOT + 1.18-R0.1-SNAPSHOT diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java index a1d82727..793283b7 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java @@ -24,7 +24,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; @@ -140,14 +140,14 @@ public boolean activateContainer( } if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, new TextComponent("container.enderchest"))); + }, new TranslatableComponent("container.enderchest"))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } @@ -161,8 +161,8 @@ public boolean activateContainer( if (block instanceof ChestBlock chestBlock) { - // boolean flag: check if chest is blocked - Optional menuOptional = chestBlock.combine(blockState, level, blockPos, false).apply( + // boolean flag: do not check if chest is blocked + Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER new DoubleBlockCombiner.Combiner>() { @Override From 160ae47bf534a0f86e4859fe1f8415eaed8a8ed4 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 2 Dec 2021 16:12:53 -0500 Subject: [PATCH 041/340] Correct wrong item list usage --- .../v1_17_R1/SpecialPlayerInventory.java | 27 +++++++++++++++++-- .../v1_18_R1/SpecialPlayerInventory.java | 27 +++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java index 8b2f24a3..3fc5c02c 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.lishid.openinv.internal.ISpecialPlayerInventory; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; @@ -113,11 +114,33 @@ public boolean isInUse() { } private @NotNull ItemStack getRawItem(int i) { - return super.getItem(i); + if (i < 0) { + return ItemStack.EMPTY; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + return list.get(i); + } + } + + return ItemStack.EMPTY; } private void setRawItem(int i, @NotNull ItemStack itemStack) { - super.setItem(i, itemStack); + if (i < 0) { + return; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + list.set(i, itemStack); + } + } } private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java index ffb96d33..93242316 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.lishid.openinv.internal.ISpecialPlayerInventory; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; @@ -113,11 +114,33 @@ public boolean isInUse() { } private @NotNull ItemStack getRawItem(int i) { - return super.getItem(i); + if (i < 0) { + return ItemStack.EMPTY; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + return list.get(i); + } + } + + return ItemStack.EMPTY; } private void setRawItem(int i, @NotNull ItemStack itemStack) { - super.setItem(i, itemStack); + if (i < 0) { + return; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + list.set(i, itemStack); + } + } } private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} From 3e2b60199700fc1cd13de150f834a5e0ba6a216e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 2 Dec 2021 16:15:22 -0500 Subject: [PATCH 042/340] Bump version to 4.1.9 for release --- .github/workflows/release.yml | 2 +- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_17_R1/pom.xml | 2 +- internal/v1_18_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b78047e..1a984a42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: - name: Create CurseForge Release uses: itsmeow/curseforge-upload@v3 with: - token: ${{ secrets.CURSEFORGE_TOKEN }} + token: "${{ secrets.CURSEFORGE_TOKEN }}" project_id: 31432 game_endpoint: minecraft file_path: ./OpenInv.jar diff --git a/api/pom.xml b/api/pom.xml index b8f62591..0180dcf0 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.9-SNAPSHOT + 4.1.9 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 53bd6f54..7765f2f1 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.9-SNAPSHOT + 4.1.9 openinvassembly diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 613aa381..d33be5f5 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.9-SNAPSHOT + 4.1.9 openinvadapter1_17_R1 diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index e702bb01..0f368430 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.9-SNAPSHOT + 4.1.9 openinvadapter1_18_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 9a8a5387..efcde103 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.9-SNAPSHOT + 4.1.9 openinvplugincore diff --git a/pom.xml b/pom.xml index ceb20dda..ec7a3f39 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.9-SNAPSHOT + 4.1.9 pom @@ -81,13 +81,13 @@ openinvapi com.lishid compile - 4.1.9-SNAPSHOT + 4.1.9 openinvplugincore com.lishid compile - 4.1.9-SNAPSHOT + 4.1.9 From 6a31015ee2a4991293b5533428455a8a51e4ed49 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 2 Dec 2021 16:15:34 -0500 Subject: [PATCH 043/340] Bump version to 4.1.10-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_17_R1/pom.xml | 2 +- internal/v1_18_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 0180dcf0..eaab029b 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.9 + 4.1.10-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 7765f2f1..2f134443 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.9 + 4.1.10-SNAPSHOT openinvassembly diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index d33be5f5..10596332 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.9 + 4.1.10-SNAPSHOT openinvadapter1_17_R1 diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index 0f368430..adb2a7d3 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.9 + 4.1.10-SNAPSHOT openinvadapter1_18_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index efcde103..d56caa9b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.9 + 4.1.10-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index ec7a3f39..181b7d75 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.9 + 4.1.10-SNAPSHOT pom @@ -81,13 +81,13 @@ openinvapi com.lishid compile - 4.1.9 + 4.1.10-SNAPSHOT openinvplugincore com.lishid compile - 4.1.9 + 4.1.10-SNAPSHOT From f8fb8cd03403d76c6710d4763f73c71042af3427 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 19 Jan 2022 10:11:16 -0500 Subject: [PATCH 044/340] Reduce duplicate configuration details Adapters must now declare a `spigot.version` property. * Greatly simplifies and speeds up retrieval of Spigot versions --- internal/v1_17_R1/pom.xml | 33 +++------------------------------ internal/v1_18_R1/pom.xml | 28 ---------------------------- pom.xml | 32 +++++++++++++++++++++++++++++++- scripts/get_spigot_versions.sh | 22 +++++----------------- 4 files changed, 39 insertions(+), 76 deletions(-) diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 10596332..789a56eb 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -1,6 +1,6 @@ + unknown @@ -144,6 +146,34 @@ net.md-5 specialsource-maven-plugin 1.2.2 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang + true + org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot + org.spigotmc:spigot:${spigot.version}:jar:remapped-obf + + + diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh index 1e079f39..c4e7b264 100644 --- a/scripts/get_spigot_versions.sh +++ b/scripts/get_spigot_versions.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (C) 2011-2021 lishid. All rights reserved. +# Copyright (C) 2011-2022 lishid. All rights reserved. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -42,22 +42,10 @@ readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -D declare -n versions="spigot_versions" for module in "${modules[@]}"; do - # Get number of dependencies declared in pom of specified internal module. - max_index=$(mvn help:evaluate -Dexpression=project.dependencies -q -DforceStdout -P all -pl "$module" | grep -c "") - - for ((i=0; i < max_index; i++)); do - # Get artifactId of dependency. - artifact_id=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].artifactId -q -DforceStdout -P all -pl "$module") - - # Ensure dependency is Spigot. - if [[ "$artifact_id" == spigot ]]; then - # Get Spigot version. - spigot_version=$(mvn help:evaluate -Dexpression=project.dependencies["$i"].version -q -DforceStdout -P all -pl "$module") - versions+=("$spigot_version") - echo "$spigot_version" - break - fi - done + # Get Spigot version. + spigot_version=$(mvn help:evaluate -Dexpression=spigot.version -q -DforceStdout -P all -pl "$module") + versions+=("$spigot_version") + echo "$spigot_version" done # Reset JVM parameters From 027d4e56378d1a05be5cc538f7ceb74a2b2d8807 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 19 Jan 2022 10:21:54 -0500 Subject: [PATCH 045/340] Bump Spigot version to 1.18.1 --- internal/v1_18_R1/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index 8617dca9..d0486df0 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 4.1.10-SNAPSHOT + + + openinvadapter1_18_R2 + OpenInvAdapter1_18_R2 + + + 17 + 17 + 1.18.2-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot-api + ${spigot.version} + + + spigot + org.spigotmc + provided + ${spigot.version} + remapped-mojang + + + openinvapi + com.lishid + + + openinvplugincore + com.lishid + + + annotations + org.jetbrains + + + + + + + maven-shade-plugin + + + false + + + + maven-compiler-plugin + + + net.md-5 + specialsource-maven-plugin + + + + + diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java new file mode 100644 index 00000000..45930cdf --- /dev/null +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R2; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.util.ReflectionHelper; +import java.lang.reflect.Field; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.CompoundContainer; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.DoubleBlockCombiner; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.ShulkerBox; +import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AnySilentContainer implements IAnySilentContainer { + + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer() { + try { + try { + // IDE warns about field not existing, but SpecialSource does not remap strings used in reflection. + // The warning is not suppressed as a reminder that it must manually be checked on updates. + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(Level.WARNING, "Error obtaining GameType field", e); + } + } + + @Override + public boolean isShulkerBlocked(ShulkerBox box) { + org.bukkit.World bukkitWorld = box.getWorld(); + if (!(bukkitWorld instanceof CraftWorld)) { + bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); + } + + if (!(bukkitWorld instanceof CraftWorld craftWorld)) { + Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); + OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); + return false; + } + + final ServerLevel world = craftWorld.getHandle(); + final BlockPos blockPosition = new BlockPos(box.getX(), box.getY(), box.getZ()); + final BlockEntity tile = world.getBlockEntity(blockPosition); + + if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) + || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { + return false; + } + + BlockState blockState = world.getBlockState(blockPosition); + + // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen + AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) + .move(blockPosition) + .deflate(1.0E-6D); + return !world.noCollision(boundingBox); + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + final ServerLevel level = player.getLevel(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); + + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, new TranslatableComponent("container.enderchest"))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: do not check if chest is blocked + Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( + // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER + new DoubleBlockCombiner.Combiner>() { + @Override + public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { + CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); + return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); + } + + @Override + public Optional acceptSingle(ChestBlockEntity localChest) { + return Optional.of(localChest); + } + + @Override + public Optional acceptNone() { + return Optional.empty(); + } + }); + + if (menuOptional.isEmpty()) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + menuProvider = menuOptional.get(); + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; + } + + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null) { + return; + } + + InventoryView view = bukkitPlayer.getOpenInventory(); + switch (view.getType()) { + case CHEST: + case ENDER_CHEST: + case SHULKER_BOX: + case BARREL: + break; + default: + return; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // ServerPlayer#closeContainer cannot be called without entering an + // infinite loop because this method is called during inventory close. + // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent + player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); + // From ServerPlayer#closeContainer + player.doCloseContainer(); + // Regular inventory close will handle the rest - packet sending, etc. + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java new file mode 100644 index 00000000..aeaf5457 --- /dev/null +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R2; + +import java.io.File; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.apache.logging.log4j.LogManager; +import org.bukkit.craftbukkit.v1_18_R2.CraftServer; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; + +public class OpenPlayer extends CraftPlayer { + + public OpenPlayer(CraftServer server, ServerPlayer entity) { + super(server, entity); + } + + @Override + public void loadData() { + // See CraftPlayer#loadData + CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); + if (loaded != null) { + readExtraData(loaded); + } + } + + @Override + public void saveData() { + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + setExtraData(playerData); + + if (!isOnline()) { + // Special case: save old vehicle data + CompoundTag oldData = worldNBTStorage.load(player); + + if (oldData != null && oldData.contains("RootVehicle", 10)) { + // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) + playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + } + + File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + NbtIo.writeCompressed(playerData, file); + File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); + File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(file1, file, file2); + } catch (Exception e) { + LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString()); + } + } + +} diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java new file mode 100644 index 00000000..40d6e8e0 --- /dev/null +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R2; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IPlayerDataManager; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.OpenInventoryView; +import com.mojang.authlib.GameProfile; +import java.lang.reflect.Field; +import java.util.logging.Logger; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_18_R2.CraftServer; +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.CraftContainer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerDataManager implements IPlayerDataManager { + + private @Nullable Field bukkitEntity; + + public PlayerDataManager() { + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } + + // Create a profile and entity to load the player data + // See net.minecraft.server.PlayerList#attemptLogin + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + + if (worldServer == null) { + return null; + } + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + // Get the bukkit entity + Player target = entity.getBukkitEntity(); + if (target != null) { + // Load data + target.loadData(); + } + // Return the entity + return target; + } + + void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + } + + @NotNull + @Override + public Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return player; + } + } + + @Nullable + @Override + public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + + ServerPlayer nmsPlayer = getHandle(player); + + if (nmsPlayer.connection == null) { + return null; + } + + InventoryView view = getView(player, inventory); + + if (view == null) { + return player.openInventory(inventory.getBukkitInventory()); + } + + AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + @Override + public MenuType getType() { + return getContainers(inventory.getBukkitInventory().getSize()); + } + }; + + container.setTitle(new TextComponent(view.getTitle())); + container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + + if (container == null) { + return null; + } + + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), + new TextComponent(container.getBukkitView().getTitle()))); + nmsPlayer.containerMenu = container; + nmsPlayer.initMenu(container); + + return container.getBukkitView(); + + } + + private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { + if (inventory instanceof SpecialEnderChest) { + return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); + } else if (inventory instanceof SpecialPlayerInventory) { + return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); + } else { + return null; + } + } + + static @NotNull MenuType getContainers(int inventorySize) { + + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> MenuType.GENERIC_9x3; // Default 27-slot inventory + }; + } + + @Override + public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java new file mode 100644 index 00000000..d9518f5f --- /dev/null +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R2; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ContainerListener; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { + + private final CraftInventory inventory; + private ServerPlayer owner; + private NonNullList items; + private boolean playerOnline; + + public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { + super(PlayerDataManager.getHandle(player)); + this.inventory = new CraftInventory(this); + this.owner = PlayerDataManager.getHandle(player); + this.playerOnline = online; + this.items = this.owner.getEnderChestInventory().items; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return inventory; + } + + @Override + public boolean isInUse() { + return !this.getViewers().isEmpty(); + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { + if (!this.playerOnline) { + try { + this.owner = PlayerDataManager.getHandle(player); + PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); + for (int i = 0; i < enderChest.getContainerSize(); ++i) { + enderChest.setItem(i, this.items.get(i)); + } + this.items = enderChest.items; + enderChest.transaction.addAll(this.transaction); + } catch (Exception ignored) {} + this.playerOnline = true; + } + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public List getContents() { + return this.items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.owner.getEnderChestInventory().getViewers(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void setActiveChest(EnderChestBlockEntity enderChest) { + this.owner.getEnderChestInventory().setActiveChest(enderChest); + } + + @Override + public boolean isActiveChest(EnderChestBlockEntity enderChest) { + return this.owner.getEnderChestInventory().isActiveChest(enderChest); + } + + @Override + public int getMaxStackSize() { + return this.owner.getEnderChestInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int i) { + this.owner.getEnderChestInventory().setMaxStackSize(i); + } + + @Override + public InventoryHolder getOwner() { + return this.owner.getEnderChestInventory().getOwner(); + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void addListener(ContainerListener listener) { + this.owner.getEnderChestInventory().addListener(listener); + } + + @Override + public void removeListener(ContainerListener listener) { + this.owner.getEnderChestInventory().removeListener(listener); + } + + @Override + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int i, int j) { + ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack addItem(ItemStack itemstack) { + ItemStack localItem = itemstack.copy(); + this.moveItemToOccupiedSlotsWithSameType(localItem); + if (localItem.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.moveItemToEmptySlots(localItem); + return localItem.isEmpty() ? ItemStack.EMPTY : localItem; + } + } + + @Override + public boolean canAddItem(ItemStack itemstack) { + for (ItemStack itemstack1 : this.items) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + private void moveItemToEmptySlots(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (localItem.isEmpty()) { + this.setItem(i, itemstack.copy()); + itemstack.setCount(0); + return; + } + } + } + + private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (ItemStack.isSameItemSameTags(localItem, itemstack)) { + this.moveItemsBetweenStacks(itemstack, localItem); + if (itemstack.isEmpty()) { + return; + } + } + } + } + + private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { + int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); + int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); + if (j > 0) { + itemstack1.grow(j); + itemstack.shrink(j); + this.setChanged(); + } + } + + @Override + public ItemStack removeItemNoUpdate(int i) { + ItemStack itemstack = this.items.get(i); + if (itemstack.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.items.set(i, ItemStack.EMPTY); + return itemstack; + } + } + + @Override + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.setChanged(); + } + + @Override + public int getContainerSize() { + return this.owner.getEnderChestInventory().getContainerSize(); + } + + @Override + public boolean isEmpty() { + return this.items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void startOpen(Player player) { + } + + @Override + public void stopOpen(Player player) { + } + + @Override + public boolean canPlaceItem(int i, ItemStack itemstack) { + return true; + } + + @Override + public void clearContent() { + this.items.clear(); + this.setChanged(); + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountStack(itemstack); + } + + } + + @Override + public List removeAllItems() { + List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clearContent(); + return list; + } + + @Override + public ItemStack removeItemType(Item item, int i) { + ItemStack itemstack = new ItemStack(item, 0); + + for(int j = this.getContainerSize() - 1; j >= 0; --j) { + ItemStack localItem = this.getItem(j); + if (localItem.getItem().equals(item)) { + int k = i - itemstack.getCount(); + ItemStack splitItem = localItem.split(k); + itemstack.grow(splitItem.getCount()); + if (itemstack.getCount() == i) { + break; + } + } + } + + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public String toString() { + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); + } + + @Override + public void fromTag(ListTag listTag) { + for (int i = 0; i < this.getContainerSize(); ++i) { + this.setItem(i, ItemStack.EMPTY); + } + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + if (j < this.getContainerSize()) { + this.setItem(j, ItemStack.of(compoundTag)); + } + } + + } + +} diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java new file mode 100644 index 00000000..3b410e96 --- /dev/null +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_18_R2; + +import com.google.common.collect.ImmutableList; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { + + private final CraftInventory inventory; + private boolean playerOnline; + private Player player; + private NonNullList items; + private NonNullList armor; + private NonNullList offhand; + private List> compartments; + + public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { + super(PlayerDataManager.getHandle(bukkitPlayer)); + this.inventory = new CraftInventory(this); + this.playerOnline = online; + this.player = super.player; + this.selected = player.getInventory().selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + if (!this.playerOnline) { + Player entityPlayer = PlayerDataManager.getHandle(player); + entityPlayer.getInventory().transaction.addAll(this.transaction); + this.player = entityPlayer; + for (int i = 0; i < getContainerSize(); ++i) { + this.player.getInventory().setItem(i, getRawItem(i)); + } + this.player.getInventory().selected = this.selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + this.playerOnline = true; + } + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public boolean isInUse() { + List viewers = this.getViewers(); + return viewers.size() > 1 || !viewers.isEmpty() && !viewers.get(0).getUniqueId().equals(this.player.getUUID()); + } + + @Override + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } + + private @NotNull ItemStack getRawItem(int i) { + if (i < 0) { + return ItemStack.EMPTY; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + return list.get(i); + } + } + + return ItemStack.EMPTY; + } + + private void setRawItem(int i, @NotNull ItemStack itemStack) { + if (i < 0) { + return; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + list.set(i, itemStack); + } + } + } + + private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + + private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { + if (index < items.size()) { + return new IndexedCompartment(items, getReversedItemSlotNum(index)); + } + + index -= items.size(); + + if (index < armor.size()) { + return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); + } + + index -= armor.size(); + + if (index < offhand.size()) { + return new IndexedCompartment(offhand, index); + } + + index -= offhand.size(); + + return new IndexedCompartment(null, index); + } + + private int getReversedArmorSlotNum(final int i) { + if (i == 0) { + return 3; + } + if (i == 1) { + return 2; + } + if (i == 2) { + return 1; + } + if (i == 3) { + return 0; + } + return i; + } + + private int getReversedItemSlotNum(final int i) { + if (i >= 27) { + return i - 27; + } + return i + 9; + } + + private boolean contains(Predicate predicate) { + return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); + } + + @Override + public List getArmorContents() { + return this.armor; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.player.getInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.player.getInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.player.getInventory().getViewers(); + } + + @Override + public InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + @Override + public int getMaxStackSize() { + return this.player.getInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int size) { + this.player.getInventory().setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return this.player.getBukkitEntity().getLocation(); + } + + @Override + public boolean hasCustomName() { + return false; + } + + @Override + public List getContents() { + return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + + @Override + public ItemStack getSelected() { + return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; + } + + private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + @Override + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getRawItem(i); + if (itemstack1.isEmpty()) { + return itemstack.getCount(); + } + + if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); + } + + if (remains <= 0) { + return itemstack.getCount(); + } + } + + ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); + if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { + remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); + } + + return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; + } + + @Override + public int getFreeSlot() { + for(int i = 0; i < this.items.size(); ++i) { + if (this.items.get(i).isEmpty()) { + return i; + } + } + + return -1; + } + + @Override + public void setPickedItem(ItemStack itemstack) { + int i = this.findSlotMatchingItem(itemstack); + if (isHotbarSlot(i)) { + this.selected = i; + } else if (i == -1) { + this.selected = this.getSuitableHotbarSlot(); + if (!this.items.get(this.selected).isEmpty()) { + int j = this.getFreeSlot(); + if (j != -1) { + this.items.set(j, this.items.get(this.selected)); + } + } + + this.items.set(this.selected, itemstack); + } else { + this.pickSlot(i); + } + + } + + @Override + public void pickSlot(int i) { + this.selected = this.getSuitableHotbarSlot(); + ItemStack itemstack = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(i)); + this.items.set(i, itemstack); + } + + @Override + public int findSlotMatchingItem(ItemStack itemstack) { + for(int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { + return i; + } + } + + return -1; + } + + @Override + public int findSlotMatchingUnusedItem(ItemStack itemStack) { + for(int i = 0; i < this.items.size(); ++i) { + ItemStack localItem = this.items.get(i); + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { + return i; + } + } + + return -1; + } + + @Override + public int getSuitableHotbarSlot() { + int i; + int j; + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (this.items.get(i).isEmpty()) { + return i; + } + } + + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (!this.items.get(i).isEnchanted()) { + return i; + } + } + + return this.selected; + } + + @Override + public void swapPaint(double d0) { + if (d0 > 0.0D) { + d0 = 1.0D; + } + + if (d0 < 0.0D) { + d0 = -1.0D; + } + + this.selected = (int) (this.selected - d0); + + while (this.selected < 0) { + this.selected += 9; + } + + while(this.selected >= 9) { + this.selected -= 9; + } + } + + @Override + public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { + byte b0 = 0; + boolean flag = i == 0; + int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); + j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); + ItemStack itemstack = this.player.containerMenu.getCarried(); + j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); + if (itemstack.isEmpty()) { + this.player.containerMenu.setCarried(ItemStack.EMPTY); + } + + return j; + } + + private int addResource(ItemStack itemstack) { + int i = this.getSlotWithRemainingSpace(itemstack); + if (i == -1) { + i = this.getFreeSlot(); + } + + return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); + } + + private int addResource(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + int j = itemstack.getCount(); + ItemStack localItemStack = this.getRawItem(i); + if (localItemStack.isEmpty()) { + localItemStack = new ItemStack(item, 0); + if (itemstack.hasTag()) { + // hasTag ensures tag not null + //noinspection ConstantConditions + localItemStack.setTag(itemstack.getTag().copy()); + } + + this.setRawItem(i, localItemStack); + } + + int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); + + if (k > this.getMaxStackSize() - localItemStack.getCount()) { + k = this.getMaxStackSize() - localItemStack.getCount(); + } + + if (k != 0) { + j -= k; + localItemStack.grow(k); + localItemStack.setPopTime(5); + } + + return j; + } + + @Override + public int getSlotWithRemainingSpace(ItemStack itemstack) { + if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { + return this.selected; + } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { + return 40; + } else { + for(int i = 0; i < this.items.size(); ++i) { + if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + @Override + public void tick() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (!compartment.get(i).isEmpty()) { + compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); + } + } + } + + } + + @Override + public boolean add(ItemStack itemStack) { + return this.add(-1, itemStack); + } + + @Override + public boolean add(int i, ItemStack itemStack) { + if (itemStack.isEmpty()) { + return false; + } else { + try { + if (itemStack.isDamaged()) { + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i >= 0) { + this.items.set(i, itemStack.copy()); + this.items.get(i).setPopTime(5); + itemStack.setCount(0); + return true; + } else if (this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + do { + j = itemStack.getCount(); + if (i == -1) { + itemStack.setCount(this.addResource(itemStack)); + } else { + itemStack.setCount(this.addResource(i, itemStack)); + } + } while(!itemStack.isEmpty() && itemStack.getCount() < j); + + if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return itemStack.getCount() < j; + } + } + } catch (Throwable var6) { + CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); + crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); + crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); + crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); + throw new ReportedException(crashReport); + } + } + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack) { + this.placeItemBackInInventory(itemStack, true); + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { + while(true) { + if (!itemStack.isEmpty()) { + int i = this.getSlotWithRemainingSpace(itemStack); + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i != -1) { + int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); + if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { + ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); + } + continue; + } + + this.player.drop(itemStack, false); + } + + return; + } + } + + @Override + public ItemStack removeItem(int rawIndex, final int j) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null + || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { + return ItemStack.EMPTY; + } + + return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); + } + + @Override + public void removeItem(ItemStack itemStack) { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (compartment.get(i) == itemStack) { + compartment.set(i, ItemStack.EMPTY); + break; + } + } + } + } + + @Override + public ItemStack removeItemNoUpdate(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); + + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + + return removed; + } + + @Override + public void setItem(int rawIndex, final ItemStack itemStack) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + this.player.drop(itemStack, true); + return; + } + + indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); + } + + @Override + public float getDestroySpeed(BlockState blockState) { + return this.items.get(this.selected).getDestroySpeed(blockState); + } + + @Override + public ListTag save(ListTag listTag) { + for (int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)i); + this.items.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.armor.size(); ++i) { + if (!this.armor.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 100)); + this.armor.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.offhand.size(); ++i) { + if (!this.offhand.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 150)); + this.offhand.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + return listTag; + } + + @Override + public void load(ListTag listTag) { + this.items.clear(); + this.armor.clear(); + this.offhand.clear(); + + for(int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.of(compoundTag); + if (!itemstack.isEmpty()) { + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); + } + } + } + + } + + @Override + public int getContainerSize() { + return 45; + } + + @Override + public boolean isEmpty() { + return !contains(itemStack -> !itemStack.isEmpty()); + } + + @Override + public ItemStack getItem(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + return indexedCompartment.compartment().get(indexedCompartment.index()); + } + + @Override + public Component getName() { + return this.player.getName(); + } + + @Override + public ItemStack getArmor(int index) { + return this.armor.get(index); + } + + @Override + public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { + if (damage > 0.0F) { + damage /= 4.0F; + if (damage < 1.0F) { + damage = 1.0F; + } + + for (int index : armorIndices) { + ItemStack itemstack = this.armor.get(index); + if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { + itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); + } + } + } + } + + @Override + public void dropAll() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + ItemStack itemstack = compartment.get(i); + if (!itemstack.isEmpty()) { + this.player.drop(itemstack, true, false); + compartment.set(i, ItemStack.EMPTY); + } + } + } + } + + @Override + public void setChanged() { + super.setChanged(); + } + + @Override + public int getTimesChanged() { + return super.getTimesChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public boolean contains(ItemStack itemstack) { + return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); + } + + @Override + public boolean contains(TagKey tagKey) { + + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); + } + + @Override + public void replaceWith(Inventory inventory) { + Function getter; + + if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { + getter = specialPlayerInventory::getRawItem; + } else { + getter = inventory::getItem; + } + + for(int i = 0; i < this.getContainerSize(); ++i) { + this.setRawItem(i, getter.apply(i)); + } + + this.selected = inventory.selected; + } + + @Override + public void clearContent() { + for (NonNullList compartment : this.compartments) { + compartment.clear(); + } + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountSimpleStack(itemstack); + } + } + + @Override + public ItemStack removeFromSelected(boolean dropWholeStack) { + ItemStack itemstack = this.getSelected(); + return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); + } + +} From bb75c723a7ec2c9a0d67b7af4d8bb8d9d3ea6b42 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 28 Feb 2022 22:16:26 -0500 Subject: [PATCH 052/340] Actually build 1.18.2 module --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index d853514c..25d6a29c 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ plugin internal/v1_17_R1 internal/v1_18_R1 + internal/v1_18_R2 assembly From a547ab79fb213fd84cfb4fb27ddac8fde2b44f3a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Mar 2022 08:31:11 -0500 Subject: [PATCH 053/340] Bump version to 4.1.10 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_17_R1/pom.xml | 2 +- internal/v1_18_R1/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index eaab029b..92d99858 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.10-SNAPSHOT + 4.1.10 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 2f134443..4f91c916 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.10-SNAPSHOT + 4.1.10 openinvassembly diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 789a56eb..28ea807a 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.10-SNAPSHOT + 4.1.10 openinvadapter1_17_R1 diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index d0486df0..2ea2bd06 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.10-SNAPSHOT + 4.1.10 openinvadapter1_18_R1 diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 83724eed..1ffc636c 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.10-SNAPSHOT + 4.1.10 openinvadapter1_18_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index d56caa9b..549cff1e 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.10-SNAPSHOT + 4.1.10 openinvplugincore diff --git a/pom.xml b/pom.xml index 25d6a29c..313389c2 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.10-SNAPSHOT + 4.1.10 pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.1.10-SNAPSHOT + 4.1.10 openinvplugincore com.lishid compile - 4.1.10-SNAPSHOT + 4.1.10 From 3103e8822b77d2292c526266f623d42a045d908a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 11 Mar 2022 08:31:26 -0500 Subject: [PATCH 054/340] Bump version to 4.1.11-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_17_R1/pom.xml | 2 +- internal/v1_18_R1/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 92d99858..4e2a1a9f 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.10 + 4.1.11-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 4f91c916..21f72f27 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.1.10 + 4.1.11-SNAPSHOT openinvassembly diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 28ea807a..f4576071 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.10 + 4.1.11-SNAPSHOT openinvadapter1_17_R1 diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index 2ea2bd06..866cb584 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.10 + 4.1.11-SNAPSHOT openinvadapter1_18_R1 diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 1ffc636c..75363dcf 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.1.10 + 4.1.11-SNAPSHOT openinvadapter1_18_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index 549cff1e..a9d8ad3b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.1.10 + 4.1.11-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 313389c2..6d173201 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.1.10 + 4.1.11-SNAPSHOT pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.1.10 + 4.1.11-SNAPSHOT openinvplugincore com.lishid compile - 4.1.10 + 4.1.11-SNAPSHOT From 173f49680b6299d6a719f1a4806ec83cd1c4c964 Mon Sep 17 00:00:00 2001 From: Max Lee Date: Tue, 5 Apr 2022 23:10:58 +0200 Subject: [PATCH 055/340] Log error message when failing to save player data (#72) --- .../java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java | 2 +- .../java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java | 2 +- .../java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java index c4c17e85..a4bc7000 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java @@ -67,7 +67,7 @@ public void saveData() { File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); Util.safeReplaceFile(file1, file, file2); } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString()); + LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage()); } } diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java index 2a0a7793..adcb22c0 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java @@ -67,7 +67,7 @@ public void saveData() { File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); Util.safeReplaceFile(file1, file, file2); } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString()); + LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage()); } } diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java index aeaf5457..4fdcfc00 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java @@ -67,7 +67,7 @@ public void saveData() { File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); Util.safeReplaceFile(file1, file, file2); } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}", player.getName().getString()); + LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage()); } } From 22407aa8658e239e93866d1aee720108d56bb0d8 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 29 Apr 2022 13:32:48 -0400 Subject: [PATCH 056/340] Update dependencies, add Dependabot (#75) --- .github/dependabot.yml | 10 ++++++ .github/workflows/ci.yml | 64 ++++++++++++++++++++++++++------------- internal/v1_17_R1/pom.xml | 6 +--- internal/v1_18_R1/pom.xml | 6 +--- internal/v1_18_R2/pom.xml | 6 +--- pom.xml | 18 +++++++---- 6 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5ab4dca3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b190db22..5cacbe0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,25 +8,15 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Set Up Java - uses: actions/setup-java@v2 + - uses: actions/setup-java@v3 with: distribution: 'adopt' java-version: '17' + cache: 'maven' - # Use cache to speed up build - - name: Cache Maven Repo - uses: actions/cache@v2 - id: cache - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - - # Install Spigot dependencies. - # This script uses Maven to check all required installations and ensure that they are present. + # Install Spigot dependencies if necessary. - name: Install Spigot Dependencies run: . scripts/install_spigot_dependencies.sh @@ -36,37 +26,69 @@ jobs: # Upload artifacts - name: Upload Distributable Jar id: upload-final - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: dist path: ./target/OpenInv.jar - name: Upload API Jar id: upload-api - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: api path: ./api/target/openinvapi*.jar + merge-dependabot: + name: Auto-merge Dependabot PRs + needs: [ build ] + if: github.event.name == 'pull_request_target' && github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + # Filter dependency changes based on path. + - uses: dorny/paths-filter@v2 + id: changes + with: + filters: | + maven: + - '**/pom.xml' + + # Only auto-merge Maven changes. + - if: steps.changes.outputs.maven == 'true' + name: Approve + uses: hmarr/auto-approve-action@v2.0.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - if: steps.changes.outputs.maven == 'true' + name: Merge + uses: pascalgn/automerge-action@v0.15.2 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_LABELS: "dependencies" + MERGE_METHOD: "squash" + release: name: Create Github Release needs: [ build ] if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@v2 + # Fetch all history - used to assemble changelog. + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set Release Variables run: bash ./scripts/set_release_env.sh - - name: Download Artifacts - uses: actions/download-artifact@v2 + - name: Download Artifact + uses: actions/download-artifact@v3 + with: + name: dist + path: dist - name: Create Release id: create-release - uses: softprops/action-gh-release@v0.1.5 + uses: softprops/action-gh-release@v0.1.14 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index f4576071..427a2081 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -43,10 +43,6 @@ ${spigot.version} remapped-mojang - - openinvapi - com.lishid - openinvplugincore com.lishid @@ -61,7 +57,7 @@ maven-shade-plugin - + false diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index 866cb584..3a62a7e4 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -48,10 +48,6 @@ ${spigot.version} remapped-mojang - - openinvapi - com.lishid - openinvplugincore com.lishid @@ -66,7 +62,7 @@ maven-shade-plugin - + false diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 75363dcf..0a0f4294 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -48,10 +48,6 @@ ${spigot.version} remapped-mojang - - openinvapi - com.lishid - openinvplugincore com.lishid @@ -66,7 +62,7 @@ maven-shade-plugin - + false diff --git a/pom.xml b/pom.xml index 6d173201..2ef3c9f6 100644 --- a/pom.xml +++ b/pom.xml @@ -27,8 +27,8 @@ UTF-8 - 1.8 - 1.8 + 16 + 16 unknown @@ -72,13 +72,13 @@ annotations org.jetbrains provided - 21.0.1 + 23.0.0 spigot-api org.spigotmc provided - 1.16.5-R0.1-SNAPSHOT + 1.17.1-R0.1-SNAPSHOT openinvapi @@ -91,6 +91,12 @@ com.lishid compile 4.1.11-SNAPSHOT + + + com.lishid + openinvapi + + @@ -128,7 +134,7 @@ org.apache.maven.plugins - 3.2.4 + 3.3.0 @@ -146,7 +152,7 @@ net.md-5 specialsource-maven-plugin - 1.2.2 + 1.2.4 package From 7903942b51e04fdf6410ceb05a99067a8fd86c24 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 29 Apr 2022 13:40:00 -0400 Subject: [PATCH 057/340] Correct Action indentation Wish it was more clearly indicated that the action was failing anywhere but the actions tab. No notification even, kind of annoying to have actually made a PR to double-check and then still have it fail. --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cacbe0d..be78fbad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,15 +56,15 @@ jobs: - if: steps.changes.outputs.maven == 'true' name: Approve uses: hmarr/auto-approve-action@v2.0.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" - if: steps.changes.outputs.maven == 'true' name: Merge uses: pascalgn/automerge-action@v0.15.2 - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - MERGE_LABELS: "dependencies" - MERGE_METHOD: "squash" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_LABELS: "dependencies" + MERGE_METHOD: "squash" release: name: Create Github Release From 60c1d91e25ae0f6c99007071b10d9cb6e3e3bc6d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 29 Apr 2022 13:50:32 -0400 Subject: [PATCH 058/340] Simplify Maven check Apparently Dependabot actually manages language labels when multiple options are enabled. Convenient! --- .github/workflows/ci.yml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be78fbad..690504ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,26 +40,16 @@ jobs: merge-dependabot: name: Auto-merge Dependabot PRs needs: [ build ] - if: github.event.name == 'pull_request_target' && github.actor == 'dependabot[bot]' + if: "github.event.name == 'pull_request_target' + && github.actor == 'dependabot[bot]' + && contains( github.event.pull_request.labels.*.name, 'java')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - # Filter dependency changes based on path. - - uses: dorny/paths-filter@v2 - id: changes - with: - filters: | - maven: - - '**/pom.xml' - - # Only auto-merge Maven changes. - - if: steps.changes.outputs.maven == 'true' - name: Approve + - name: Approve uses: hmarr/auto-approve-action@v2.0.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - - if: steps.changes.outputs.maven == 'true' - name: Merge + - name: Merge uses: pascalgn/automerge-action@v0.15.2 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From cc3d96590f2dc649bcaf58008c933474b4fb509d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 13:19:34 -0400 Subject: [PATCH 059/340] Bump hmarr/auto-approve-action from 2.0.0 to 2.2.1 (#77) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 690504ac..35cb2d9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Approve - uses: hmarr/auto-approve-action@v2.0.0 + uses: hmarr/auto-approve-action@v2.2.1 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Merge From d6152f2f245161771229b045df921197ca854808 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:28:42 -0400 Subject: [PATCH 060/340] Bump dsaltares/fetch-gh-release-asset from 0.0.5 to 1.0.0 (#83) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1a984a42..13c09979 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: fetch-depth: 0 - name: Fetch Github Release Asset - uses: dsaltares/fetch-gh-release-asset@0.0.5 + uses: dsaltares/fetch-gh-release-asset@1.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ github.event.release.id }} From 65c3358b22f774f5f3047fc9f767d43bc2257afa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:29:37 -0400 Subject: [PATCH 061/340] Bump pascalgn/automerge-action from 0.15.2 to 0.15.3 (#82) Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.15.2 to 0.15.3. - [Release notes](https://github.com/pascalgn/automerge-action/releases) - [Commits](https://github.com/pascalgn/automerge-action/compare/v0.15.2...v0.15.3) --- updated-dependencies: - dependency-name: pascalgn/automerge-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35cb2d9a..176cd38e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Merge - uses: pascalgn/automerge-action@v0.15.2 + uses: pascalgn/automerge-action@v0.15.3 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "dependencies" From 525982ce64022fcc0164e5d6a931ab3c3350dde6 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 12 May 2022 08:48:51 -0400 Subject: [PATCH 062/340] Harden event/internal access slightly --- .../openinv/{util => }/InternalAccessor.java | 94 +++++++------------ .../{listeners => }/InventoryListener.java | 17 +--- .../{listeners => }/PlayerListener.java | 20 ++-- 3 files changed, 43 insertions(+), 88 deletions(-) rename plugin/src/main/java/com/lishid/openinv/{util => }/InternalAccessor.java (70%) rename plugin/src/main/java/com/lishid/openinv/{listeners => }/InventoryListener.java (94%) rename plugin/src/main/java/com/lishid/openinv/{listeners => }/PlayerListener.java (86%) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java similarity index 70% rename from plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java rename to plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index 399154a0..dbe7e988 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -14,28 +14,28 @@ * along with this program. If not, see . */ -package com.lishid.openinv.util; +package com.lishid.openinv; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.util.InventoryAccess; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; -public class InternalAccessor { +class InternalAccessor { - private final Plugin plugin; + private final @NotNull Plugin plugin; private final String version; private boolean supported = false; private IPlayerDataManager playerDataManager; private IAnySilentContainer anySilentContainer; - public InternalAccessor(final Plugin plugin) { + InternalAccessor(@NotNull Plugin plugin) { this.plugin = plugin; String packageName = plugin.getServer().getClass().getPackage().getName(); @@ -51,58 +51,29 @@ public InternalAccessor(final Plugin plugin) { } public String getReleasesLink() { - switch (version) { - case "1_4_5": - case "1_4_6": - case "v1_4_R1": - case "v1_5_R2": - case "v1_5_R3": - case "v1_6_R1": - case "v1_6_R2": - case "v1_6_R3": - case "v1_7_R1": - case "v1_7_R2": - case "v1_7_R3": - case "v1_7_R4": - case "v1_8_R1": - case "v1_8_R2": - case "v1_9_R1": - case "v1_9_R2": - case "v1_10_R1": - case "v1_11_R1": - case "v1_12_R1": - return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)"; - case "v1_13_R1": - return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; - case "v1_13_R2": - return "https://github.com/lishid/OpenInv/releases/tag/4.0.7"; - case "v1_14_R1": - return "https://github.com/lishid/OpenInv/releases/tag/4.1.1"; - case "v1_16_R1": - return "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; - case "v1_8_R3": - case "v1_15_R1": - case "v1_16_R2": - return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; - case "v1_16_R3": - return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; - case "v1_17_R1": - default: - return "https://github.com/Jikoo/OpenInv/releases"; - } + + return switch (version) { + case "1_4_5", "1_4_6", "v1_4_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3", + "v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", + "v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1" + -> "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)"; + case "v1_13_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; + case "v1_13_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.7"; + case "v1_14_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.1"; + case "v1_16_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; + case "v1_8_R3", "v1_15_R1", "v1_16_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; + case "v1_16_R3" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; + default -> "https://github.com/Jikoo/OpenInv/releases"; + }; } - private T createObject(final Class assignableClass, final String className, - final Object... params) throws ClassCastException, ClassNotFoundException, - InstantiationException, IllegalAccessException, IllegalArgumentException, - InvocationTargetException, NoSuchMethodException, SecurityException { + private @NotNull T createObject( + @NotNull Class assignableClass, + @NotNull String className, + @NotNull Object @NotNull ... params) + throws ClassCastException, ReflectiveOperationException { // Fetch internal class if it exists. Class internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className); - if (!assignableClass.isAssignableFrom(internalClass)) { - String message = String.format("Found class %s but cannot cast to %s!", internalClass.getName(), assignableClass.getName()); - this.plugin.getLogger().warning(message); - throw new IllegalStateException(message); - } // Quick return: no parameters, no need to fiddle about finding the correct constructor. if (params.length == 0) { @@ -133,10 +104,10 @@ private T createObject(final Class assignableClass, final Strin String message = builder.append(']').toString(); this.plugin.getLogger().warning(message); - throw new IllegalArgumentException(message); + throw new NoSuchMethodException(message); } - private T createSpecialInventory( + private @NotNull T createSpecialInventory( @NotNull Class assignableClass, @NotNull String className, @NotNull Player player, @@ -159,7 +130,7 @@ private T createSpecialInventory( * @return the IAnySilentContainer * @throws IllegalStateException if server version is unsupported */ - public IAnySilentContainer getAnySilentContainer() { + public @NotNull IAnySilentContainer getAnySilentContainer() { if (!this.supported) { throw new IllegalStateException(String.format("Unsupported server version %s!", this.version)); } @@ -172,7 +143,7 @@ public IAnySilentContainer getAnySilentContainer() { * @return the IPlayerDataManager * @throws IllegalStateException if server version is unsupported */ - public IPlayerDataManager getPlayerDataManager() { + public @NotNull IPlayerDataManager getPlayerDataManager() { if (!this.supported) { throw new IllegalStateException(String.format("Unsupported server version %s!", this.version)); } @@ -180,13 +151,12 @@ public IPlayerDataManager getPlayerDataManager() { } /** - * Gets the server implementation version. If not initialized, returns the string "null" - * instead. + * Gets the server implementation version. * - * @return the version, or "null" + * @return the version */ - public String getVersion() { - return this.version != null ? this.version : "null"; + public @NotNull String getVersion() { + return this.version; } /** diff --git a/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java similarity index 94% rename from plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java rename to plugin/src/main/java/com/lishid/openinv/InventoryListener.java index ea8dad31..8e1c27e1 100644 --- a/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,9 +14,8 @@ * along with this program. If not, see . */ -package com.lishid.openinv.listeners; +package com.lishid.openinv; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; @@ -45,22 +44,14 @@ * * @author Jikoo */ -public class InventoryListener implements Listener { - - private final OpenInv plugin; - - public InventoryListener(final OpenInv plugin) { - this.plugin = plugin; - } +record InventoryListener(OpenInv plugin) implements Listener { @EventHandler public void onInventoryClose(@NotNull final InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player)) { + if (!(event.getPlayer() instanceof Player player)) { return; } - Player player = (Player) event.getPlayer(); - if (this.plugin.getPlayerSilentChestStatus(player)) { this.plugin.getAnySilentContainer().deactivateContainer(player); } diff --git a/plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java similarity index 86% rename from plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java rename to plugin/src/main/java/com/lishid/openinv/PlayerListener.java index ca5a23e8..4ec29507 100644 --- a/plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java @@ -14,9 +14,8 @@ * along with this program. If not, see . */ -package com.lishid.openinv.listeners; +package com.lishid.openinv; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.Permissions; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; @@ -28,32 +27,27 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.jetbrains.annotations.NotNull; -public class PlayerListener implements Listener { - - private final OpenInv plugin; - - public PlayerListener(OpenInv plugin) { - this.plugin = plugin; - } +record PlayerListener(OpenInv plugin) implements Listener { @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerJoin(final PlayerJoinEvent event) { + public void onPlayerJoin(@NotNull PlayerJoinEvent event) { plugin.setPlayerOnline(event.getPlayer()); } @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuit(PlayerQuitEvent event) { + public void onPlayerQuit(@NotNull PlayerQuitEvent event) { plugin.setPlayerOffline(event.getPlayer()); } @EventHandler - public void onWorldChange(PlayerChangedWorldEvent event) { + public void onWorldChange(@NotNull PlayerChangedWorldEvent event) { plugin.changeWorld(event.getPlayer()); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerInteract(PlayerInteractEvent event) { + public void onPlayerInteract(@NotNull PlayerInteractEvent event) { // Do not cancel 3rd party plugins' custom events if (!PlayerInteractEvent.class.equals(event.getClass())) { From f21019eba822fca6d3dcfd211c2dc553ef50393d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 12 May 2022 09:53:51 -0400 Subject: [PATCH 063/340] Extract help to location of use --- .../main/java/com/lishid/openinv/OpenInv.java | 35 ------------- .../openinv/commands/OpenInvCommand.java | 49 +++++++++++++++---- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 680683d3..238b5a2e 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -510,41 +510,6 @@ public void setPlayerSilentChestStatus(@NotNull final OfflinePlayer offline, fin this.saveConfig(); } - /** - * Displays all applicable help for OpenInv commands. - * - * @param player the Player to help - */ - public void showHelp(final Player player) { - // Get registered commands - for (String commandName : this.getDescription().getCommands().keySet()) { - PluginCommand command = this.getCommand(commandName); - - // Ensure command is successfully registered and player can use it - if (command == null || !command.testPermissionSilent(player)) { - continue; - } - - // Send usage - player.sendMessage(command.getUsage().replace("", commandName)); - - List aliases = command.getAliases(); - if (aliases.isEmpty()) { - continue; - } - - // Assemble alias list - StringBuilder aliasBuilder = new StringBuilder(" (aliases: "); - for (String alias : aliases) { - aliasBuilder.append(alias).append(", "); - } - aliasBuilder.delete(aliasBuilder.length() - 2, aliasBuilder.length()).append(')'); - - // Send all aliases - player.sendMessage(aliasBuilder.toString()); - } - } - @Override public void unload(@NotNull final OfflinePlayer offline) { this.playerCache.invalidate(this.getPlayerID(offline)); diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index 3eaa5e68..c97b154d 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -23,9 +23,11 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.StringJoiner; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -43,25 +45,24 @@ public OpenInvCommand(final OpenInv plugin) { @Override public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, @NotNull final String[] args) { - if (!(sender instanceof Player)) { - plugin.sendMessage(sender, "messages.error.consoleUnsupported"); + boolean openInv = command.getName().equals("openinv"); + + if (openInv && args.length > 0 && (args[0].equalsIgnoreCase("help") || args[0].equals("?"))) { + this.showHelp(sender); return true; } - if (args.length > 0 && (args[0].equalsIgnoreCase("help") || args[0].equals("?"))) { - this.plugin.showHelp((Player) sender); + if (!(sender instanceof Player player)) { + plugin.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } - final Player player = (Player) sender; - final boolean openinv = command.getName().equals("openinv"); - // History management - String history = (openinv ? this.openInvHistory : this.openEnderHistory).get(player); + String history = (openInv ? this.openInvHistory : this.openEnderHistory).get(player); if (history == null || history.isEmpty()) { history = player.getName(); - (openinv ? this.openInvHistory : this.openEnderHistory).put(player, history); + (openInv ? this.openInvHistory : this.openEnderHistory).put(player, history); } final String name; @@ -89,7 +90,7 @@ public void run() { if (!player.isOnline()) { return; } - OpenInvCommand.this.openInventory(player, offlinePlayer, openinv); + OpenInvCommand.this.openInventory(player, offlinePlayer, openInv); } }.runTask(OpenInvCommand.this.plugin); @@ -99,6 +100,34 @@ public void run() { return true; } + private void showHelp(final CommandSender sender) { + // Get registered commands + for (String commandName : plugin.getDescription().getCommands().keySet()) { + PluginCommand command = plugin.getCommand(commandName); + + // Ensure command is successfully registered and sender can use it + if (command == null || !command.testPermissionSilent(sender)) { + continue; + } + + // Send usage + sender.sendMessage(command.getUsage().replace("", commandName)); + + List aliases = command.getAliases(); + if (!aliases.isEmpty()) { + // Assemble alias list + StringJoiner aliasJoiner = new StringJoiner(", ", " (aliases: ", ")"); + for (String alias : aliases) { + aliasJoiner.add(alias); + } + + // Send all aliases + sender.sendMessage(aliasJoiner.toString()); + } + + } + } + private void openInventory(final Player player, final OfflinePlayer target, boolean openinv) { Player onlineTarget; boolean online = target.isOnline(); From ac00261afb8a76c99c6841d2e936ac36ea4e70fb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 12 May 2022 12:18:18 -0400 Subject: [PATCH 064/340] Note Spigot requirement --- .../main/java/com/lishid/openinv/OpenInv.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 238b5a2e..3ad4f5c9 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -112,6 +112,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { private InternalAccessor accessor; private LanguageManager languageManager; + private boolean isSpigot = false; /** * Evicts all viewers lacking cross-world permissions from a Player's inventory. @@ -355,8 +356,16 @@ public void onEnable() { this.languageManager = new LanguageManager(this, "en_us"); + try { + Class.forName("org.bukkit.entity.Player$Spigot"); + isSpigot = true; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + isSpigot = false; + } + // Version check - if (this.accessor.isSupported()) { + if (isSpigot && this.accessor.isSupported()) { // Update existing configuration. May require internal access. new ConfigUpdater(this).checkForUpdates(); @@ -379,8 +388,16 @@ public void onEnable() { } private void sendVersionError(Consumer messageMethod) { - messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported."); - messageMethod.accept("Please obtain an appropriate version here: " + accessor.getReleasesLink()); + if (!this.accessor.isSupported()) { + messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported."); + messageMethod.accept("Please obtain an appropriate version here: " + this.accessor.getReleasesLink()); + } + if (!isSpigot) { + messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread"); + messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are"); + messageMethod.accept("encountering an inconsistency with vanilla that prevents you from using Spigot,"); + messageMethod.accept("that is considered a Spigot bug and should be reported as such."); + } } private void setCommandExecutor(CommandExecutor executor, String... commands) { @@ -394,7 +411,7 @@ private void setCommandExecutor(CommandExecutor executor, String... commands) { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!this.accessor.isSupported()) { + if (!isSpigot || !this.accessor.isSupported()) { this.sendVersionError(sender::sendMessage); return true; } From b6e8e2ba42bdb71019b87ac2a28acf82f56587c2 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 13 May 2022 09:11:12 -0400 Subject: [PATCH 065/340] Extract some duplicate code from internal implementations --- .../openinv/internal/ISpecialInventory.java | 8 +++-- .../internal/ISpecialPlayerInventory.java | 31 ++++++++++++++++++- .../internal/v1_17_R1/SpecialEnderChest.java | 7 +---- .../v1_17_R1/SpecialPlayerInventory.java | 6 ---- .../internal/v1_18_R1/SpecialEnderChest.java | 7 +---- .../v1_18_R1/SpecialPlayerInventory.java | 6 ---- .../internal/v1_18_R2/SpecialEnderChest.java | 5 --- .../v1_18_R2/SpecialPlayerInventory.java | 6 ---- 8 files changed, 37 insertions(+), 39 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java index 86a2dc16..dc94ce44 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,11 +43,13 @@ public interface ISpecialInventory { void setPlayerOffline(); /** - * Gets whether or not this ISpecialInventory is in use. + * Gets whether this ISpecialInventory is in use. * * @return true if the ISpecialInventory is in use */ - boolean isInUse(); + default boolean isInUse() { + return !getBukkitInventory().getViewers().isEmpty(); + } /** * Gets the Player associated with this ISpecialInventory. diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java index f15bc1aa..412656cd 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,35 @@ package com.lishid.openinv.internal; +import java.util.List; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.Inventory; + +/** + * Interface defining behavior specific to a player inventory. + */ public interface ISpecialPlayerInventory extends ISpecialInventory { + /* + * Player inventory usage varies from all other inventories - as the inventory is technically open at all times, + * if the player is online or has been online while the inventory is open, they are in the viewer list. + */ + @Override + default boolean isInUse() { + Inventory inventory = getBukkitInventory(); + List viewers = inventory.getViewers(); + + if (viewers.size() != 1) { + return !viewers.isEmpty(); + } + + HumanEntity viewer = viewers.get(0); + + if (!viewer.getUniqueId().equals(getPlayer().getUniqueId())) { + return true; + } + + return viewer.getOpenInventory().getTopInventory().equals(inventory); + } + } diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java index fed9b564..6d48d549 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,11 +60,6 @@ public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean on return inventory; } - @Override - public boolean isInUse() { - return !this.getViewers().isEmpty(); - } - @Override public void setPlayerOffline() { this.playerOnline = false; diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java index 1a85469e..af7d5cb2 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java @@ -103,12 +103,6 @@ public void setPlayerOffline() { this.playerOnline = false; } - @Override - public boolean isInUse() { - List viewers = this.getViewers(); - return viewers.size() > 1 || !viewers.isEmpty() && !viewers.get(0).getUniqueId().equals(this.player.getUUID()); - } - @Override public @NotNull HumanEntity getPlayer() { return this.player.getBukkitEntity(); diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java index d10bfddd..ea67c5ff 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,11 +60,6 @@ public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean on return inventory; } - @Override - public boolean isInUse() { - return !this.getViewers().isEmpty(); - } - @Override public void setPlayerOffline() { this.playerOnline = false; diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java index 7133f80e..6508f3d7 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java @@ -103,12 +103,6 @@ public void setPlayerOffline() { this.playerOnline = false; } - @Override - public boolean isInUse() { - List viewers = this.getViewers(); - return viewers.size() > 1 || !viewers.isEmpty() && !viewers.get(0).getUniqueId().equals(this.player.getUUID()); - } - @Override public @NotNull HumanEntity getPlayer() { return this.player.getBukkitEntity(); diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java index d9518f5f..f46ff616 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java @@ -60,11 +60,6 @@ public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean on return inventory; } - @Override - public boolean isInUse() { - return !this.getViewers().isEmpty(); - } - @Override public void setPlayerOffline() { this.playerOnline = false; diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java index 3b410e96..04c2c5fb 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java @@ -103,12 +103,6 @@ public void setPlayerOffline() { this.playerOnline = false; } - @Override - public boolean isInUse() { - List viewers = this.getViewers(); - return viewers.size() > 1 || !viewers.isEmpty() && !viewers.get(0).getUniqueId().equals(this.player.getUUID()); - } - @Override public @NotNull HumanEntity getPlayer() { return this.player.getBukkitEntity(); From c9ba401a6e383f2611477b4a2c3d24e294b7ae33 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 13 May 2022 11:14:45 -0400 Subject: [PATCH 066/340] Don't inject player if unnecessary --- .../lishid/openinv/internal/v1_17_R1/PlayerDataManager.java | 5 ++++- .../lishid/openinv/internal/v1_18_R1/PlayerDataManager.java | 5 ++++- .../lishid/openinv/internal/v1_18_R2/PlayerDataManager.java | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java index df7697e7..e17c1440 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java +++ b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -130,6 +130,9 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException { public Player inject(@NotNull Player player) { try { ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java index a982b998..75f18d54 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java +++ b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -130,6 +130,9 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException { public Player inject(@NotNull Player player) { try { ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java index 40d6e8e0..1be5f57b 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java @@ -130,6 +130,9 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException { public Player inject(@NotNull Player player) { try { ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { From 9502b29fe3b15fc02d28928b7fffe4ed43891c8f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 13 May 2022 11:15:40 -0400 Subject: [PATCH 067/340] Clean up compiler warnings Compiler doesn't understand that api is provided by plugin during build process --- internal/v1_17_R1/pom.xml | 5 +++++ internal/v1_18_R1/pom.xml | 5 +++++ internal/v1_18_R2/pom.xml | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/internal/v1_17_R1/pom.xml b/internal/v1_17_R1/pom.xml index 427a2081..8dcdec42 100644 --- a/internal/v1_17_R1/pom.xml +++ b/internal/v1_17_R1/pom.xml @@ -43,6 +43,11 @@ ${spigot.version} remapped-mojang + + openinvapi + com.lishid + provided + openinvplugincore com.lishid diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_18_R1/pom.xml index 3a62a7e4..9cbf09b1 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_18_R1/pom.xml @@ -48,6 +48,11 @@ ${spigot.version} remapped-mojang + + openinvapi + com.lishid + provided + openinvplugincore com.lishid diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 0a0f4294..c0d4b2e0 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -48,6 +48,11 @@ ${spigot.version} remapped-mojang + + openinvapi + com.lishid + provided + openinvplugincore com.lishid From fdf920062b75932c9a290a4125d201384f9001cb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 13 May 2022 11:49:48 -0400 Subject: [PATCH 068/340] Remove cache, add disable-offline-access config node Fixes various permissions not being respected during login/logout with inventories already open. This will result in a performance hit for repeated open/closes - there will be significantly more disk I/O. Closes lishid#171 Closes #56 --- .../java/com/lishid/openinv/IOpenInv.java | 98 +++--- .../com/lishid/openinv/InventoryListener.java | 17 +- .../com/lishid/openinv/OfflineHandler.java | 41 +++ .../main/java/com/lishid/openinv/OpenInv.java | 319 +++++++++--------- .../com/lishid/openinv/PlayerListener.java | 8 +- .../openinv/commands/OpenInvCommand.java | 4 +- .../openinv/listeners/PluginListener.java | 42 --- .../java/com/lishid/openinv/util/Cache.java | 187 ---------- .../lishid/openinv/util/ConfigUpdater.java | 20 +- .../com/lishid/openinv/util/Permissions.java | 5 +- plugin/src/main/resources/config.yml | 3 +- 11 files changed, 287 insertions(+), 457 deletions(-) create mode 100644 plugin/src/main/java/com/lishid/openinv/OfflineHandler.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/listeners/PluginListener.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/util/Cache.java diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index 64697ffb..5b12e6a9 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,15 +42,23 @@ public interface IOpenInv { /** - * Check the configuration value for whether or not OpenInv saves player data when unloading - * players. This is exclusively for users who do not allow editing of inventories, only viewing, - * and wish to prevent any possibility of bugs such as lishid#40. If true, OpenInv will not ever - * save any edits made to players. + * Check the configuration value for whether OpenInv saves player data when unloading players. This is exclusively + * for users who do not allow editing of inventories, only viewing, and wish to prevent any possibility of bugs such + * as lishid#40. If true, OpenInv will not ever save any edits made to players. * * @return false unless configured otherwise */ boolean disableSaving(); + /** + * Check the configuration value for whether OpenInv allows offline access. This does not prevent other plugins from + * using existing loaded players while offline. + * + * @return false unless configured otherwise + * @since 4.2.0 + */ + boolean disableOfflineAccess(); + /** * Gets the active ISilentContainer implementation. * @@ -60,12 +68,9 @@ public interface IOpenInv { @NotNull IAnySilentContainer getAnySilentContainer(); /** - * Gets the active IInventoryAccess implementation. - * - * @return the IInventoryAccess - * @throws IllegalStateException if the server version is unsupported + * @deprecated Use static {@link InventoryAccess} methods. */ - @Deprecated + @Deprecated(forRemoval = true) default @NotNull IInventoryAccess getInventoryAccess() { return new InventoryAccess(); } @@ -129,6 +134,15 @@ public interface IOpenInv { */ boolean isSupportedVersion(); + /** + * Check if a {@link Player} is currently loaded by OpenInv. + * + * @param playerUuid the {@link UUID} of the {@code Player} + * @return whether the {@code Player} is loaded + * @since 4.2.0 + */ + boolean isPlayerLoaded(@NotNull UUID playerUuid); + /** * Load a Player from an OfflinePlayer. May return null under some circumstances. * @@ -227,57 +241,51 @@ public interface IOpenInv { * @return true unless configured otherwise * @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings. */ - @Deprecated + @Deprecated(forRemoval = true) default boolean notifyAnyChest() { return true; } /** - * Check the configuration value for whether or not OpenInv displays a notification to the user - * when a container is activated with SilentChest. - * - * @return true unless configured otherwise - * @deprecated OpenInv uses action bar chat for notifications. Whether or not they show is based on language settings. + * @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings. */ - @Deprecated + @Deprecated(forRemoval = true) default boolean notifySilentChest() { return true; } /** - * Mark a Player as no longer in use by a Plugin to allow OpenInv to remove it from the cache - * when eligible. - * - * @param player the Player - * @param plugin the Plugin no longer holding a reference to the Player - * @throws IllegalStateException if the server version is unsupported + * @deprecated see {@link #retainPlayer(Player, Plugin)} */ - void releasePlayer(@NotNull Player player, @NotNull Plugin plugin); + @Deprecated(forRemoval = true, since = "4.2.0") + default void releasePlayer(@NotNull Player player, @NotNull Plugin plugin) {} /** - * Mark a Player as in use by a Plugin to prevent it from being removed from the cache. Used to - * prevent issues with multiple copies of the same Player being loaded such as lishid#49. - * Changes made to loaded copies overwrite changes to the others when saved, leading to - * duplication bugs and more. - *

- * When finished with the Player object, be sure to call {@link #releasePlayer(Player, Plugin)} - * to prevent the cache from keeping it stored until the plugin is disabled. - *

- * When using a Player object from OpenInv, you must handle the Player coming online, replacing - * your Player reference with the Player from the PlayerJoinEvent. In addition, you must change - * any values in the Player to reflect any unsaved alterations to the existing Player which do - * not affect the inventory or ender chest contents. - *

- * OpenInv only saves player data when unloading a Player from the cache, and then only if - * {@link #disableSaving()} returns false. If you are making changes that OpenInv does not cause - * to persist when a Player logs in as noted above, it is suggested that you manually call - * {@link Player#saveData()} when releasing your reference to ensure your changes persist. + * @deprecated OpenInv no longer uses an internal cache beyond maintaining copies of currently open inventories. + * If you wish to use/modify a player, ensure either {@link IOpenInv#isPlayerLoaded(UUID)} is false or the player + * instance is the same memory address as the one in use by OpenInv. + *

+     *  public @NotNull Player savePlayerData(@NotNull Player player) {
+     *     IOpenInv openInv = ...
+     *     if (!openInv.disableSaving() && openInv.isPlayerLoaded(player.getUniqueId())) {
+     *         Player openInvLoadedPlayer = openInv.loadPlayer(myInUsePlayer);
+     *         if (openInvLoadedPlayer != player) {
+     *             // The copy loaded by OpenInv is not the same as our loaded copy. Push our changes.
+     *             copyPlayerModifications(player, openInvLoadedPlayer);
+     *         }
+     *         // OpenInv will handle saving data when the player is unloaded.
+     *         // Optionally, to be sure our changes will persist, save now.
+     *         // openInvLoadedPlayer.saveData();
+     *         return openInvLoadedPlayer;
+     *     }
      *
-     * @param player the Player
-     * @param plugin the Plugin holding the reference to the Player
-     * @throws IllegalStateException if the server version is unsupported
+     *     player.saveData();
+     *     return player;
+     * }
+     * 
*/ - void retainPlayer(@NotNull Player player, @NotNull Plugin plugin); + @Deprecated(forRemoval = true, since = "4.2.0") + default void retainPlayer(@NotNull Player player, @NotNull Plugin plugin) {} /** * Sets a player's AnyChest setting. diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index 8e1c27e1..1a6a799f 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -16,6 +16,7 @@ package com.lishid.openinv; +import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; @@ -47,7 +48,7 @@ record InventoryListener(OpenInv plugin) implements Listener { @EventHandler - public void onInventoryClose(@NotNull final InventoryCloseEvent event) { + private void onInventoryClose(@NotNull final InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player player)) { return; } @@ -55,10 +56,20 @@ public void onInventoryClose(@NotNull final InventoryCloseEvent event) { if (this.plugin.getPlayerSilentChestStatus(player)) { this.plugin.getAnySilentContainer().deactivateContainer(player); } + + ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory()); + if (specialInventory != null) { + this.plugin.handleCloseInventory(event.getPlayer(), specialInventory); + } else { + specialInventory = InventoryAccess.getPlayerInventory(event.getInventory()); + if (specialInventory != null) { + this.plugin.handleCloseInventory(event.getPlayer(), specialInventory); + } + } } @EventHandler(priority = EventPriority.LOWEST) - public void onInventoryClick(@NotNull final InventoryClickEvent event) { + private void onInventoryClick(@NotNull final InventoryClickEvent event) { if (handleInventoryInteract(event)) { return; } @@ -92,7 +103,7 @@ public void onInventoryClick(@NotNull final InventoryClickEvent event) { } @EventHandler(priority = EventPriority.LOWEST) - public void onInventoryDrag(@NotNull final InventoryDragEvent event) { + private void onInventoryDrag(@NotNull final InventoryDragEvent event) { if (handleInventoryInteract(event)) { return; } diff --git a/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java b/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java new file mode 100644 index 00000000..ea0e8c3f --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv; + +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.util.Permissions; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import org.jetbrains.annotations.NotNull; + +record OfflineHandler( + @NotNull BiFunction, UUID, ISpecialInventory> fetch, + @NotNull Consumer<@NotNull ISpecialInventory> handle) { + + static final OfflineHandler REMOVE_AND_CLOSE = new OfflineHandler( + Map::remove, + inventory -> OpenInv.ejectViewers(inventory, viewer -> true) + ); + + static final OfflineHandler REQUIRE_PERMISSIONS = new OfflineHandler( + Map::get, + inventory -> OpenInv.ejectViewers(inventory, viewer -> !Permissions.OPENOFFLINE.hasPermission(viewer)) + ); + +} diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 3ad4f5c9..5e3591b4 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,8 +16,6 @@ package com.lishid.openinv; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import com.lishid.openinv.commands.ContainerSettingCommand; import com.lishid.openinv.commands.OpenInvCommand; import com.lishid.openinv.commands.SearchContainerCommand; @@ -27,23 +25,19 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.listeners.InventoryListener; -import com.lishid.openinv.listeners.PlayerListener; -import com.lishid.openinv.listeners.PluginListener; -import com.lishid.openinv.util.Cache; import com.lishid.openinv.util.ConfigUpdater; -import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.LanguageManager; import com.lishid.openinv.util.Permissions; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; @@ -56,10 +50,8 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; -import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -70,63 +62,21 @@ */ public class OpenInv extends JavaPlugin implements IOpenInv { - private final Map inventories = new HashMap<>(); - private final Map enderChests = new HashMap<>(); - private final Multimap> pluginUsage = HashMultimap.create(); - - private final Cache playerCache = new Cache<>(300000L, - value -> { - String key = OpenInv.this.getPlayerID(value); - - return OpenInv.this.inventories.containsKey(key) - && OpenInv.this.inventories.get(key).isInUse() - || OpenInv.this.enderChests.containsKey(key) - && OpenInv.this.enderChests.get(key).isInUse() - || OpenInv.this.pluginUsage.containsKey(key); - }, - value -> { - String key = OpenInv.this.getPlayerID(value); - - // Check if inventory is stored, and if it is, remove it and eject all viewers - if (OpenInv.this.inventories.containsKey(key)) { - Inventory inv = OpenInv.this.inventories.remove(key).getBukkitInventory(); - List viewers = new ArrayList<>(inv.getViewers()); - for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) { - entity.closeInventory(); - } - } - - // Check if ender chest is stored, and if it is, remove it and eject all viewers - if (OpenInv.this.enderChests.containsKey(key)) { - Inventory inv = OpenInv.this.enderChests.remove(key).getBukkitInventory(); - List viewers = new ArrayList<>(inv.getViewers()); - for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) { - entity.closeInventory(); - } - } - - if (!OpenInv.this.disableSaving() && !value.isOnline()) { - value.saveData(); - } - }); + private final Map inventories = new ConcurrentHashMap<>(); + private final Map enderChests = new ConcurrentHashMap<>(); private InternalAccessor accessor; private LanguageManager languageManager; private boolean isSpigot = false; + private OfflineHandler offlineHandler; /** - * Evicts all viewers lacking cross-world permissions from a Player's inventory. + * Evict all viewers lacking cross-world permissions from a Player's inventory. * * @param player the Player */ - public void changeWorld(final Player player) { - - String key = this.getPlayerID(player); - - // Check if the player is cached. If not, neither of their inventories is open. - if (!this.playerCache.containsKey(key)) { - return; - } + void changeWorld(@NotNull Player player) { + UUID key = player.getUniqueId(); if (this.inventories.containsKey(key)) { kickCrossWorldViewers(player, this.inventories.get(key)); @@ -137,15 +87,19 @@ public void changeWorld(final Player player) { } } - private void kickCrossWorldViewers(Player player, ISpecialInventory inventory) { - List viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers()); - for (HumanEntity human : viewers) { - // If player has permission or is in the same world, allow continued access - // Just in case, also allow null worlds. - if (Permissions.CROSSWORLD.hasPermission(human) || Objects.equals(human.getWorld(), player.getWorld())) { - continue; + private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInventory inventory) { + ejectViewers( + inventory, + viewer -> + !Permissions.CROSSWORLD.hasPermission(viewer) + && Objects.equals(viewer.getWorld(), player.getWorld())); + } + + static void ejectViewers(@NotNull ISpecialInventory inventory, Predicate predicate) { + for (HumanEntity viewer : new ArrayList<>(inventory.getBukkitInventory().getViewers())) { + if (predicate.test(viewer)) { + viewer.closeInventory(); } - human.closeInventory(); } } @@ -159,7 +113,7 @@ private void kickCrossWorldViewers(Player player, ISpecialInventory inventory) { * @param rawSlot the raw slot in the view * @return the converted slot number */ - public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int convertToPlayerSlot(InventoryView view, int rawSlot) { return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot); } @@ -168,9 +122,13 @@ public boolean disableSaving() { return this.getConfig().getBoolean("settings.disable-saving", false); } - @NotNull @Override - public IAnySilentContainer getAnySilentContainer() { + public boolean disableOfflineAccess() { + return this.getConfig().getBoolean("settings.disable-offline-access", false); + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { return this.accessor.getAnySilentContainer(); } @@ -202,31 +160,31 @@ public boolean getPlayerSilentChestStatus(@NotNull final OfflinePlayer offline) return this.getConfig().getBoolean("toggles.silent-chest." + this.getPlayerID(offline), defaultState); } - @NotNull @Override - public ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) + public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) throws InstantiationException { - String id = this.getPlayerID(player); - if (this.enderChests.containsKey(id)) { - return this.enderChests.get(id); + UUID key = player.getUniqueId(); + + if (this.enderChests.containsKey(key)) { + return this.enderChests.get(key); } + ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online); - this.enderChests.put(id, inv); - this.playerCache.put(id, player); + this.enderChests.put(key, inv); return inv; } - @NotNull @Override - public ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) + public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) throws InstantiationException { - String id = this.getPlayerID(player); - if (this.inventories.containsKey(id)) { - return this.inventories.get(id); + UUID key = player.getUniqueId(); + + if (this.inventories.containsKey(key)) { + return this.inventories.get(key); } + ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online); - this.inventories.put(id, inv); - this.playerCache.put(id, player); + this.inventories.put(key, inv); return inv; } @@ -235,21 +193,29 @@ public boolean isSupportedVersion() { return this.accessor != null && this.accessor.isSupported(); } + @Override + public boolean isPlayerLoaded(@NotNull UUID playerUuid) { + return this.inventories.containsKey(playerUuid) || this.enderChests.containsKey(playerUuid); + } + @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + UUID key = offline.getUniqueId(); - String key = this.getPlayerID(offline); - if (this.playerCache.containsKey(key)) { - return this.playerCache.get(key); + if (this.inventories.containsKey(key)) { + return (Player) this.inventories.get(key).getPlayer(); + } + + if (this.enderChests.containsKey(key)) { + return (Player) this.enderChests.get(key).getPlayer(); } Player player = offline.getPlayer(); if (player != null) { - this.playerCache.put(key, player); return player; } - if (!this.isSupportedVersion()) { + if (disableOfflineAccess() || !this.isSupportedVersion()) { return null; } @@ -267,10 +233,6 @@ public boolean isSupportedVersion() { return null; } - if (player != null) { - this.playerCache.put(key, player); - } - return player; } @@ -338,9 +300,23 @@ public void onDisable() { return; } - if (this.isSupportedVersion()) { - this.playerCache.invalidateAll(); - } + Stream.concat(inventories.values().stream(), enderChests.values().stream()) + .map(inventory -> { + // Cheat a bit - rather than stream twice, evict all viewers during remapping. + ejectViewers(inventory, viewer -> true); + if (inventory.getPlayer() instanceof Player player) { + return player; + } + return null; + }) + .filter(Objects::nonNull) + .distinct() + .forEach(player -> { + if (!player.isOnline()) { + player = accessor.getPlayerDataManager().inject(player); + } + player.saveData(); + }); } @Override @@ -355,6 +331,7 @@ public void onEnable() { this.accessor = new InternalAccessor(this); this.languageManager = new LanguageManager(this, "en_us"); + this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; try { Class.forName("org.bukkit.entity.Player$Spigot"); @@ -371,7 +348,6 @@ public void onEnable() { // Register listeners pm.registerEvents(new PlayerListener(this), this); - pm.registerEvents(new PluginListener(this), this); pm.registerEvents(new InventoryListener(this), this); // Register commands to their executors @@ -390,7 +366,7 @@ public void onEnable() { private void sendVersionError(Consumer messageMethod) { if (!this.accessor.isSupported()) { messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported."); - messageMethod.accept("Please obtain an appropriate version here: " + this.accessor.getReleasesLink()); + messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink()); } if (!isSpigot) { messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread"); @@ -418,40 +394,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - public void releaseAllPlayers(final Plugin plugin) { - Iterator>> iterator = this.pluginUsage.entries().iterator(); - - if (!iterator.hasNext()) { - return; - } - - for (Map.Entry> entry = iterator.next(); iterator.hasNext(); entry = iterator.next()) { - if (entry.getValue().equals(plugin.getClass())) { - iterator.remove(); - } - } - } - - @Override - public void releasePlayer(@NotNull final Player player, @NotNull final Plugin plugin) { - String key = this.getPlayerID(player); - - if (!this.pluginUsage.containsEntry(key, plugin.getClass())) { - return; - } - - this.pluginUsage.remove(key, plugin.getClass()); - } - @Override - public void retainPlayer(@NotNull final Player player, @NotNull final Plugin plugin) { - String key = this.getPlayerID(player); - - if (this.pluginUsage.containsEntry(key, plugin.getClass())) { - return; - } - - this.pluginUsage.put(key, plugin.getClass()); + public void reloadConfig() { + super.reloadConfig(); + this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; } @Override @@ -464,27 +410,71 @@ public void setPlayerAnyChestStatus(@NotNull final OfflinePlayer offline, final * Method for handling a Player going offline. * * @param player the Player - * @throws IllegalStateException if the server version is unsupported */ - public void setPlayerOffline(final Player player) { + void setPlayerOffline(@NotNull Player player) { + setPlayerOffline(player, offlineHandler); + } - String key = this.getPlayerID(player); + private void setPlayerOffline(@NotNull OfflinePlayer player, @NotNull OfflineHandler handler) { + UUID key = player.getUniqueId(); + + setPlayerOffline(inventories, key, handler); + setPlayerOffline(enderChests, key, handler); + } - // Check if the player is cached. If not, neither of their inventories is open. - if (!this.playerCache.containsKey(key)) { + private void setPlayerOffline( + @NotNull Map map, + @NotNull UUID key, + @NotNull OfflineHandler handler) { + ISpecialInventory inventory = handler.fetch().apply(map, key); + if (inventory == null) { return; } + inventory.setPlayerOffline(); + if (!inventory.isInUse()) { + map.remove(key); + } else { + handler.handle().accept(inventory); + } + } - // Replace stored player with our own version - this.playerCache.put(key, this.accessor.getPlayerDataManager().inject(player)); + void handleCloseInventory(@NotNull HumanEntity exViewer, @NotNull ISpecialInventory inventory) { + Map map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests; + UUID key = inventory.getPlayer().getUniqueId(); + @Nullable ISpecialInventory loaded = map.get(key); - if (this.inventories.containsKey(key)) { - this.inventories.get(key).setPlayerOffline(); + if (loaded == null) { + // Loaded inventory has already been removed. Removal will handle saving if necessary. + return; } - if (this.enderChests.containsKey(key)) { - this.enderChests.get(key).setPlayerOffline(); + if (loaded != inventory) { + Inventory bukkitInventory = inventory.getBukkitInventory(); + // Just in case, respect contents of the inventory that was just used. + loaded.getBukkitInventory().setContents(bukkitInventory.getContents()); + // We need to close this inventory to reduce risk of duplication bugs if the user is offline. + // We don't want to risk recursively closing the same inventory repeatedly, so we schedule dumping viewers. + // Worst case we schedule a couple redundant tasks if several people had the inventory open. + if (!bukkitInventory.getViewers().isEmpty()) { + getServer().getScheduler().runTask(this, () -> ejectViewers(inventory, viewer -> true)); + } } + + // Schedule task to check in use status later this tick. Closing user is still in viewer list. + getServer().getScheduler().runTask(this, () -> { + if (loaded.isInUse()) { + return; + } + + // Re-fetch from map - prevents duplicate saves on multi-close. + ISpecialInventory current = map.remove(key); + + if (!disableSaving() + && current != null + && current.getPlayer() instanceof Player player && !player.isOnline()) { + this.accessor.getPlayerDataManager().inject(player).saveData(); + } + }); } /** @@ -493,31 +483,34 @@ public void setPlayerOffline(final Player player) { * @param player the Player * @throws IllegalStateException if the server version is unsupported */ - public void setPlayerOnline(final Player player) { + void setPlayerOnline(@NotNull Player player) { + setPlayerOnline(inventories, player, player::updateInventory); + setPlayerOnline(enderChests, player, null); + } - String key = this.getPlayerID(player); + private void setPlayerOnline( + @NotNull Map map, + @NotNull Player player, + @Nullable Runnable task) { + ISpecialInventory inventory = map.get(player.getUniqueId()); - // Check if the player is cached. If not, neither of their inventories is open. - if (!this.playerCache.containsKey(key)) { + if (inventory == null) { + // Inventory not open. return; } - this.playerCache.put(key, player); + inventory.setPlayerOnline(player); - if (this.inventories.containsKey(key)) { - this.inventories.get(key).setPlayerOnline(player); - new BukkitRunnable() { - @Override - public void run() { - if (player.isOnline()) { - player.updateInventory(); - } - } - }.runTask(this); - } + // Eject viewers lacking permission. + ejectViewers( + inventory, + viewer -> + !Permissions.OPENONLINE.hasPermission(viewer) + || !Permissions.CROSSWORLD.hasPermission(viewer) + && !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld())); - if (this.enderChests.containsKey(key)) { - this.enderChests.get(key).setPlayerOnline(player); + if (task != null) { + getServer().getScheduler().runTask(this, task); } } @@ -529,7 +522,7 @@ public void setPlayerSilentChestStatus(@NotNull final OfflinePlayer offline, fin @Override public void unload(@NotNull final OfflinePlayer offline) { - this.playerCache.invalidate(this.getPlayerID(offline)); + setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE); } } diff --git a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java index 4ec29507..61a5f7d4 100644 --- a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java @@ -32,22 +32,22 @@ record PlayerListener(OpenInv plugin) implements Listener { @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerJoin(@NotNull PlayerJoinEvent event) { + private void onPlayerJoin(@NotNull PlayerJoinEvent event) { plugin.setPlayerOnline(event.getPlayer()); } @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuit(@NotNull PlayerQuitEvent event) { + private void onPlayerQuit(@NotNull PlayerQuitEvent event) { plugin.setPlayerOffline(event.getPlayer()); } @EventHandler - public void onWorldChange(@NotNull PlayerChangedWorldEvent event) { + private void onWorldChange(@NotNull PlayerChangedWorldEvent event) { plugin.changeWorld(event.getPlayer()); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerInteract(@NotNull PlayerInteractEvent event) { + private void onPlayerInteract(@NotNull PlayerInteractEvent event) { // Do not cancel 3rd party plugins' custom events if (!PlayerInteractEvent.class.equals(event.getClass())) { diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index c97b154d..189a41d9 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -133,7 +133,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool boolean online = target.isOnline(); if (!online) { - if (Permissions.OPENOFFLINE.hasPermission(player)) { + if (!plugin.disableOfflineAccess() && Permissions.OPENOFFLINE.hasPermission(player)) { // Try loading the player's data onlineTarget = this.plugin.loadPlayer(target); } else { diff --git a/plugin/src/main/java/com/lishid/openinv/listeners/PluginListener.java b/plugin/src/main/java/com/lishid/openinv/listeners/PluginListener.java deleted file mode 100644 index 850c9887..00000000 --- a/plugin/src/main/java/com/lishid/openinv/listeners/PluginListener.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.listeners; - -import com.lishid.openinv.OpenInv; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.server.PluginDisableEvent; - -/** - * Listener for plugin-related events. - * - * @author Jikoo - */ -public class PluginListener implements Listener { - - private final OpenInv plugin; - - public PluginListener(OpenInv plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onPluginDisable(PluginDisableEvent event) { - plugin.releaseAllPlayers(event.getPlugin()); - } - -} diff --git a/plugin/src/main/java/com/lishid/openinv/util/Cache.java b/plugin/src/main/java/com/lishid/openinv/util/Cache.java deleted file mode 100644 index 5f2e8dcf..00000000 --- a/plugin/src/main/java/com/lishid/openinv/util/Cache.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.util; - -import com.google.common.collect.Multimap; -import com.google.common.collect.TreeMultimap; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Predicate; - -/** - * A minimal thread-safe time-based cache implementation backed by a HashMap and TreeMultimap. - * - * @author Jikoo - */ -public class Cache { - - private final Map internal; - private final Multimap expiry; - private final long retention; - private final Predicate inUseCheck; - private final Consumer postRemoval; - - /** - * Constructs a Cache with the specified retention duration, in use function, and post-removal function. - * - * @param retention duration after which keys are automatically invalidated if not in use - * @param inUseCheck Predicate used to check if a key is considered in use - * @param postRemoval Consumer used to perform any operations required when a key is invalidated - */ - public Cache(final long retention, final Predicate inUseCheck, final Consumer postRemoval) { - this.internal = new HashMap<>(); - - this.expiry = TreeMultimap.create(Long::compareTo, (k1, k2) -> Objects.equals(k1, k2) ? 0 : 1); - - this.retention = retention; - this.inUseCheck = inUseCheck; - this.postRemoval = postRemoval; - } - - /** - * Set a key and value pair. Keys are unique. Using an existing key will cause the old value to - * be overwritten and the expiration timer to be reset. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - */ - public void put(final K key, final V value) { - // Invalidate key - runs lazy check and ensures value won't be cleaned up early - this.invalidate(key); - - synchronized (this.internal) { - this.internal.put(key, value); - this.expiry.put(System.currentTimeMillis() + this.retention, key); - } - } - - /** - * Returns the value to which the specified key is mapped, or null if no value is mapped for the key. - * - * @param key the key whose associated value is to be returned - * @return the value to which the specified key is mapped, or null if no value is mapped for the key - */ - public V get(final K key) { - // Run lazy check to clean cache - this.lazyCheck(); - - synchronized (this.internal) { - return this.internal.get(key); - } - } - - /** - * Returns true if the specified key is mapped to a value. - * - * @param key key to check if a mapping exists for - * @return true if a mapping exists for the specified key - */ - public boolean containsKey(final K key) { - // Run lazy check to clean cache - this.lazyCheck(); - - synchronized (this.internal) { - return this.internal.containsKey(key); - } - } - - /** - * Forcibly invalidates a key, even if it is considered to be in use. - * - * @param key key to invalidate - */ - public void invalidate(final K key) { - // Run lazy check to clean cache - this.lazyCheck(); - - synchronized (this.internal) { - if (!this.internal.containsKey(key)) { - // Value either not present or cleaned by lazy check. Either way, we're good - return; - } - - // Remove stored object - this.internal.remove(key); - - // Remove expiration entry - prevents more work later, plus prevents issues with values invalidating early - for (Iterator> iterator = this.expiry.entries().iterator(); iterator.hasNext();) { - if (key.equals(iterator.next().getValue())) { - iterator.remove(); - break; - } - } - } - } - - /** - * Forcibly invalidates all keys, even if they are considered to be in use. - */ - public void invalidateAll() { - synchronized (this.internal) { - for (V value : this.internal.values()) { - this.postRemoval.accept(value); - } - this.expiry.clear(); - this.internal.clear(); - } - } - - /** - * Invalidate all expired keys that are not considered in use. If a key is expired but is - * considered in use by the provided Function, its expiration time is reset. - */ - private void lazyCheck() { - long now = System.currentTimeMillis(); - synchronized (this.internal) { - List inUse = new ArrayList<>(); - for (Iterator> iterator = this.expiry.entries().iterator(); iterator - .hasNext();) { - Map.Entry entry = iterator.next(); - - if (entry.getKey() > now) { - break; - } - - iterator.remove(); - - if (this.inUseCheck.test(this.internal.get(entry.getValue()))) { - inUse.add(entry.getValue()); - continue; - } - - V value = this.internal.remove(entry.getValue()); - - if (value == null) { - continue; - } - - this.postRemoval.accept(value); - } - - long nextExpiry = now + this.retention; - for (K value : inUse) { - this.expiry.put(nextExpiry, value); - } - } - } - -} diff --git a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java index 9ed6fb8b..68839998 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,13 +25,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.configuration.ConfigurationSection; -public class ConfigUpdater { - - private final OpenInv plugin; - - public ConfigUpdater(OpenInv plugin) { - this.plugin = plugin; - } +public record ConfigUpdater(OpenInv plugin) { public void checkForUpdates() { final int version = plugin.getConfig().getInt("config-version", 1); @@ -60,6 +54,9 @@ public void checkForUpdates() { if (version < 4) { updateConfig3To4(); } + if (version < 5) { + updateConfig4To5(); + } plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.saveConfig(); @@ -68,6 +65,13 @@ public void checkForUpdates() { }); } + private void updateConfig4To5() { + plugin.getServer().getScheduler().runTask(plugin, () -> { + plugin.getConfig().set("settings.disable-offline-access", false); + plugin.getConfig().set("config-version", 5); + }); + } + private void updateConfig3To4() { plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.getConfig().set("notify", null); diff --git a/plugin/src/main/java/com/lishid/openinv/util/Permissions.java b/plugin/src/main/java/com/lishid/openinv/util/Permissions.java index 7876af34..c2af2e1c 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/Permissions.java +++ b/plugin/src/main/java/com/lishid/openinv/util/Permissions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ package com.lishid.openinv.util; import org.bukkit.permissions.Permissible; +import org.jetbrains.annotations.NotNull; public enum Permissions { @@ -50,7 +51,7 @@ public enum Permissions { this.uninheritable = uninheritable; } - public boolean hasPermission(Permissible permissible) { + public boolean hasPermission(@NotNull Permissible permissible) { boolean hasPermission = permissible.hasPermission(permission); if (uninheritable || hasPermission || permissible.isPermissionSet(permission)) { diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index d8bc7bb0..f0b56f0c 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,4 +1,5 @@ -config-version: 4 +config-version: 5 settings: + disable-offline-access: false disable-saving: false locale: 'en_us' From 6ad6e0c6bc84f670dbd1535e986db3e5799018f8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 13 May 2022 11:50:50 -0400 Subject: [PATCH 069/340] Bump version to 4.2.0-SNAPSHOT --- api/pom.xml | 4 ++-- assembly/pom.xml | 4 ++-- internal/v1_17_R1/pom.xml | 2 +- internal/v1_18_R1/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- plugin/pom.xml | 4 ++-- pom.xml | 6 +++--- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 4e2a1a9f..d815843b 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -1,5 +1,5 @@ - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 4.2.0-SNAPSHOT - - - openinvadapter1_17_R1 - OpenInvAdapter1_17_R1 - - - 16 - 16 - 1.17.1-R0.1-SNAPSHOT - - - - - spigot - org.spigotmc - provided - ${spigot.version} - remapped-mojang - - - openinvapi - com.lishid - provided - - - openinvplugincore - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-shade-plugin - - - false - - - - maven-compiler-plugin - - - net.md-5 - specialsource-maven-plugin - - - - - diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java deleted file mode 100644 index 61d4a204..00000000 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/AnySilentContainer.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_17_R1; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.util.ReflectionHelper; -import java.lang.reflect.Field; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.CompoundContainer; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.entity.monster.Shulker; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.DoubleBlockCombiner; -import net.minecraft.world.level.block.ShulkerBoxBlock; -import net.minecraft.world.level.block.TrappedChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.ChestBlockEntity; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class AnySilentContainer implements IAnySilentContainer { - - private @Nullable Field serverPlayerGameModeGameType; - - public AnySilentContainer() { - try { - try { - // IDE warns about field not existing, but SpecialSource does not remap strings used in reflection. - // The warning is not suppressed as a reminder that it must manually be checked on updates. - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); - this.serverPlayerGameModeGameType.setAccessible(true); - } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); - logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); - logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); - // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. - this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); - } - } catch (SecurityException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to directly write player game mode! SilentContainer will fail."); - logger.log(Level.WARNING, "Error obtaining GameType field", e); - } - } - - @Override - public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { - org.bukkit.World bukkitWorld = shulkerBox.getWorld(); - if (!(bukkitWorld instanceof CraftWorld)) { - bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); - } - - if (!(bukkitWorld instanceof CraftWorld craftWorld)) { - Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); - OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); - return false; - } - - final ServerLevel world = craftWorld.getHandle(); - final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); - final BlockEntity tile = world.getBlockEntity(blockPosition); - - if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) - || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { - return false; - } - - BlockState blockState = world.getBlockState(blockPosition); - - // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen - AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) - .move(blockPosition) - .deflate(1.0E-6D); - return !world.noCollision(boundingBox); - } - - @Override - public boolean activateContainer( - @NotNull final Player bukkitPlayer, - final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - final ServerLevel level = player.getLevel(); - final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final BlockEntity blockEntity = level.getBlockEntity(blockPos); - - if (blockEntity == null) { - return false; - } - - if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.BlockEnderChest - PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); - enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); - int rows = enderChest.getContainerSize() / 9; - return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, new TranslatableComponent("container.enderchest"))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(blockEntity instanceof MenuProvider menuProvider)) { - return false; - } - - BlockState blockState = level.getBlockState(blockPos); - Block block = blockState.getBlock(); - - if (block instanceof ChestBlock chestBlock) { - - // boolean flag: do not check if chest is blocked - Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( - // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER - new DoubleBlockCombiner.Combiner>() { - @Override - public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { - CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); - return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); - } - - @Override - public Optional acceptSingle(ChestBlockEntity localChest) { - return Optional.of(localChest); - } - - @Override - public Optional acceptNone() { - return Optional.empty(); - } - }); - - if (menuOptional.isEmpty()) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - menuProvider = menuOptional.get(); - - if (block instanceof TrappedChestBlock) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof ShulkerBoxBlock) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BarrelBlock) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { - player.openMenu(menuProvider); - return true; - } - - // SilentChest requires access to setting players' game mode directly. - if (this.serverPlayerGameModeGameType == null) { - return false; - } - - if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { - if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - player.openMenu(menuProvider); - this.forceGameType(player, gameType); - return true; - } - - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null) { - return; - } - - InventoryView view = bukkitPlayer.getOpenInventory(); - switch (view.getType()) { - case CHEST: - case ENDER_CHEST: - case SHULKER_BOX: - case BARREL: - break; - default: - return; - } - - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - // Force game mode change without informing plugins or players. - // Regular game mode set calls GameModeChangeEvent and is cancellable. - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - - // ServerPlayer#closeContainer cannot be called without entering an - // infinite loop because this method is called during inventory close. - // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent - player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); - // From ServerPlayer#closeContainer - player.doCloseContainer(); - // Regular inventory close will handle the rest - packet sending, etc. - - // Revert forced game mode. - this.forceGameType(player, gameType); - } - - private void forceGameType(final ServerPlayer player, final GameType gameMode) { - if (this.serverPlayerGameModeGameType == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - this.serverPlayerGameModeGameType.setAccessible(true); - this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } - -} diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java b/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java deleted file mode 100644 index e17c1440..00000000 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/PlayerDataManager.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_17_R1; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IPlayerDataManager; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.OpenInventoryView; -import com.mojang.authlib.GameProfile; -import java.lang.reflect.Field; -import java.util.logging.Logger; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.level.Level; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_17_R1.CraftServer; -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.CraftContainer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PlayerDataManager implements IPlayerDataManager { - - private @Nullable Field bukkitEntity; - - public PlayerDataManager() { - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); - logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); - bukkitEntity = null; - } - } - - public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - ServerPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); - } - - return nmsPlayer; - } - - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - - // Create a profile and entity to load the player data - // See net.minecraft.server.PlayerList#attemptLogin - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - ServerLevel worldServer = server.getLevel(Level.OVERWORLD); - - if (worldServer == null) { - return null; - } - - ServerPlayer entity = new ServerPlayer(server, worldServer, profile); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - // Get the bukkit entity - Player target = entity.getBukkitEntity(); - if (target != null) { - // Load data - target.loadData(); - } - // Return the entity - return target; - } - - void injectPlayer(ServerPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } - - bukkitEntity.setAccessible(true); - - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); - } - - @NotNull - @Override - public Player inject(@NotNull Player player) { - try { - ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { - return openPlayer; - } - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - return player; - } - } - - @Nullable - @Override - public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - - ServerPlayer nmsPlayer = getHandle(player); - - if (nmsPlayer.connection == null) { - return null; - } - - InventoryView view = getView(player, inventory); - - if (view == null) { - return player.openInventory(inventory.getBukkitInventory()); - } - - AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { - @Override - public MenuType getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - - container.setTitle(new TextComponent(view.getTitle())); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); - - if (container == null) { - return null; - } - - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - new TextComponent(container.getBukkitView().getTitle()))); - nmsPlayer.containerMenu = container; - nmsPlayer.initMenu(container); - - return container.getBukkitView(); - - } - - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - - static @NotNull MenuType getContainers(int inventorySize) { - - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; // PLAYER - case 41, 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> MenuType.GENERIC_9x3; // Default 27-slot inventory - }; - } - - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - -} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java deleted file mode 100644 index adcb22c0..00000000 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/OpenPlayer.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2011-2021 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_18_R1; - -import java.io.File; -import net.minecraft.Util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.storage.PlayerDataStorage; -import org.apache.logging.log4j.LogManager; -import org.bukkit.craftbukkit.v1_18_R1.CraftServer; -import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; - -public class OpenPlayer extends CraftPlayer { - - public OpenPlayer(CraftServer server, ServerPlayer entity) { - super(server, entity); - } - - @Override - public void loadData() { - // See CraftPlayer#loadData - CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); - if (loaded != null) { - readExtraData(loaded); - } - } - - @Override - public void saveData() { - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); - setExtraData(playerData); - - if (!isOnline()) { - // Special case: save old vehicle data - CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } - } - - File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); - NbtIo.writeCompressed(playerData, file); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); - File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(file1, file, file2); - } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage()); - } - } - -} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java deleted file mode 100644 index ea67c5ff..00000000 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialEnderChest.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_18_R1; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.ContainerListener; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { - - private final CraftInventory inventory; - private ServerPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChestInventory().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (!this.playerOnline) { - try { - this.owner = PlayerDataManager.getHandle(player); - PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); - for (int i = 0; i < enderChest.getContainerSize(); ++i) { - enderChest.setItem(i, this.items.get(i)); - } - this.items = enderChest.items; - enderChest.transaction.addAll(this.transaction); - } catch (Exception ignored) {} - this.playerOnline = true; - } - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChestInventory().getViewers(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public void setActiveChest(EnderChestBlockEntity enderChest) { - this.owner.getEnderChestInventory().setActiveChest(enderChest); - } - - @Override - public boolean isActiveChest(EnderChestBlockEntity enderChest) { - return this.owner.getEnderChestInventory().isActiveChest(enderChest); - } - - @Override - public int getMaxStackSize() { - return this.owner.getEnderChestInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChestInventory().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChestInventory().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void addListener(ContainerListener listener) { - this.owner.getEnderChestInventory().addListener(listener); - } - - @Override - public void removeListener(ContainerListener listener) { - this.owner.getEnderChestInventory().removeListener(listener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int i, int j) { - ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack addItem(ItemStack itemstack) { - ItemStack localItem = itemstack.copy(); - this.moveItemToOccupiedSlotsWithSameType(localItem); - if (localItem.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.moveItemToEmptySlots(localItem); - return localItem.isEmpty() ? ItemStack.EMPTY : localItem; - } - } - - @Override - public boolean canAddItem(ItemStack itemstack) { - for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { - return true; - } - } - - return false; - } - - private void moveItemToEmptySlots(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (localItem.isEmpty()) { - this.setItem(i, itemstack.copy()); - itemstack.setCount(0); - return; - } - } - } - - private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameTags(localItem, itemstack)) { - this.moveItemsBetweenStacks(itemstack, localItem); - if (itemstack.isEmpty()) { - return; - } - } - } - } - - private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { - int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); - int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); - if (j > 0) { - itemstack1.grow(j); - itemstack.shrink(j); - this.setChanged(); - } - } - - @Override - public ItemStack removeItemNoUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.items.set(i, ItemStack.EMPTY); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.setChanged(); - } - - @Override - public int getContainerSize() { - return this.owner.getEnderChestInventory().getContainerSize(); - } - - @Override - public boolean isEmpty() { - return this.items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void startOpen(Player player) { - } - - @Override - public void stopOpen(Player player) { - } - - @Override - public boolean canPlaceItem(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clearContent() { - this.items.clear(); - this.setChanged(); - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountStack(itemstack); - } - - } - - @Override - public List removeAllItems() { - List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clearContent(); - return list; - } - - @Override - public ItemStack removeItemType(Item item, int i) { - ItemStack itemstack = new ItemStack(item, 0); - - for(int j = this.getContainerSize() - 1; j >= 0; --j) { - ItemStack localItem = this.getItem(j); - if (localItem.getItem().equals(item)) { - int k = i - itemstack.getCount(); - ItemStack splitItem = localItem.split(k); - itemstack.grow(splitItem.getCount()); - if (itemstack.getCount() == i) { - break; - } - } - } - - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); - } - - @Override - public void fromTag(ListTag listTag) { - for (int i = 0; i < this.getContainerSize(); ++i) { - this.setItem(i, ItemStack.EMPTY); - } - - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.of(compoundTag)); - } - } - - } - -} diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java b/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java deleted file mode 100644 index 6508f3d7..00000000 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/SpecialPlayerInventory.java +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_18_R1; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.Tag; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_18_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private Player player; - private NonNullList items; - private NonNullList armor; - private NonNullList offhand; - private List> compartments; - - public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.selected = player.getInventory().selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (!this.playerOnline) { - Player entityPlayer = PlayerDataManager.getHandle(player); - entityPlayer.getInventory().transaction.addAll(this.transaction); - this.player = entityPlayer; - for (int i = 0; i < getContainerSize(); ++i) { - this.player.getInventory().setItem(i, getRawItem(i)); - } - this.player.getInventory().selected = this.selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - this.playerOnline = true; - } - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - private @NotNull ItemStack getRawItem(int i) { - if (i < 0) { - return ItemStack.EMPTY; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - return list.get(i); - } - } - - return ItemStack.EMPTY; - } - - private void setRawItem(int i, @NotNull ItemStack itemStack) { - if (i < 0) { - return; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - list.set(i, itemStack); - } - } - } - - private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - - private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { - if (index < items.size()) { - return new IndexedCompartment(items, getReversedItemSlotNum(index)); - } - - index -= items.size(); - - if (index < armor.size()) { - return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); - } - - index -= armor.size(); - - if (index < offhand.size()) { - return new IndexedCompartment(offhand, index); - } - - index -= offhand.size(); - - return new IndexedCompartment(null, index); - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - private boolean contains(Predicate predicate) { - return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.player.getInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.player.getInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.player.getInventory().getViewers(); - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public int getMaxStackSize() { - return this.player.getInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int size) { - this.player.getInventory().setMaxStackSize(size); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public boolean hasCustomName() { - return false; - } - - @Override - public List getContents() { - return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); - } - - @Override - public ItemStack getSelected() { - return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; - } - - private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getRawItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { - remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); - if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { - remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; - } - - @Override - public int getFreeSlot() { - for(int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void setPickedItem(ItemStack itemstack) { - int i = this.findSlotMatchingItem(itemstack); - if (isHotbarSlot(i)) { - this.selected = i; - } else if (i == -1) { - this.selected = this.getSuitableHotbarSlot(); - if (!this.items.get(this.selected).isEmpty()) { - int j = this.getFreeSlot(); - if (j != -1) { - this.items.set(j, this.items.get(this.selected)); - } - } - - this.items.set(this.selected, itemstack); - } else { - this.pickSlot(i); - } - - } - - @Override - public void pickSlot(int i) { - this.selected = this.getSuitableHotbarSlot(); - ItemStack itemstack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int findSlotMatchingItem(ItemStack itemstack) { - for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { - return i; - } - } - - return -1; - } - - @Override - public int findSlotMatchingUnusedItem(ItemStack itemStack) { - for(int i = 0; i < this.items.size(); ++i) { - ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { - return i; - } - } - - return -1; - } - - @Override - public int getSuitableHotbarSlot() { - int i; - int j; - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (!this.items.get(i).isEnchanted()) { - return i; - } - } - - return this.selected; - } - - @Override - public void swapPaint(double d0) { - if (d0 > 0.0D) { - d0 = 1.0D; - } - - if (d0 < 0.0D) { - d0 = -1.0D; - } - - this.selected = (int) (this.selected - d0); - - while (this.selected < 0) { - this.selected += 9; - } - - while(this.selected >= 9) { - this.selected -= 9; - } - } - - @Override - public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); - j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); - ItemStack itemstack = this.player.containerMenu.getCarried(); - j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); - if (itemstack.isEmpty()) { - this.player.containerMenu.setCarried(ItemStack.EMPTY); - } - - return j; - } - - private int addResource(ItemStack itemstack) { - int i = this.getSlotWithRemainingSpace(itemstack); - if (i == -1) { - i = this.getFreeSlot(); - } - - return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); - } - - private int addResource(int i, ItemStack itemstack) { - Item item = itemstack.getItem(); - int j = itemstack.getCount(); - ItemStack localItemStack = this.getRawItem(i); - if (localItemStack.isEmpty()) { - localItemStack = new ItemStack(item, 0); - if (itemstack.hasTag()) { - // hasTag ensures tag not null - //noinspection ConstantConditions - localItemStack.setTag(itemstack.getTag().copy()); - } - - this.setRawItem(i, localItemStack); - } - - int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); - - if (k > this.getMaxStackSize() - localItemStack.getCount()) { - k = this.getMaxStackSize() - localItemStack.getCount(); - } - - if (k != 0) { - j -= k; - localItemStack.grow(k); - localItemStack.setPopTime(5); - } - - return j; - } - - @Override - public int getSlotWithRemainingSpace(ItemStack itemstack) { - if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { - return this.selected; - } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { - return 40; - } else { - for(int i = 0; i < this.items.size(); ++i) { - if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void tick() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (!compartment.get(i).isEmpty()) { - compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); - } - } - } - - } - - @Override - public boolean add(ItemStack itemStack) { - return this.add(-1, itemStack); - } - - @Override - public boolean add(int i, ItemStack itemStack) { - if (itemStack.isEmpty()) { - return false; - } else { - try { - if (itemStack.isDamaged()) { - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i >= 0) { - this.items.set(i, itemStack.copy()); - this.items.get(i).setPopTime(5); - itemStack.setCount(0); - return true; - } else if (this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemStack.getCount(); - if (i == -1) { - itemStack.setCount(this.addResource(itemStack)); - } else { - itemStack.setCount(this.addResource(i, itemStack)); - } - } while(!itemStack.isEmpty() && itemStack.getCount() < j); - - if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return itemStack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); - crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); - crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); - crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); - throw new ReportedException(crashReport); - } - } - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack) { - this.placeItemBackInInventory(itemStack, true); - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { - while(true) { - if (!itemStack.isEmpty()) { - int i = this.getSlotWithRemainingSpace(itemStack); - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); - if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { - ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); - } - continue; - } - - this.player.drop(itemStack, false); - } - - return; - } - } - - @Override - public ItemStack removeItem(int rawIndex, final int j) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null - || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { - return ItemStack.EMPTY; - } - - return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); - } - - @Override - public void removeItem(ItemStack itemStack) { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (compartment.get(i) == itemStack) { - compartment.set(i, ItemStack.EMPTY); - break; - } - } - } - } - - @Override - public ItemStack removeItemNoUpdate(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); - - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - - return removed; - } - - @Override - public void setItem(int rawIndex, final ItemStack itemStack) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - this.player.drop(itemStack, true); - return; - } - - indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); - } - - @Override - public float getDestroySpeed(BlockState blockState) { - return this.items.get(this.selected).getDestroySpeed(blockState); - } - - @Override - public ListTag save(ListTag listTag) { - for (int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.offhand.size(); ++i) { - if (!this.offhand.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - return listTag; - } - - @Override - public void load(ListTag listTag) { - this.items.clear(); - this.armor.clear(); - this.offhand.clear(); - - for(int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.of(compoundTag); - if (!itemstack.isEmpty()) { - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } - } - } - - } - - @Override - public int getContainerSize() { - return 45; - } - - @Override - public boolean isEmpty() { - return !contains(itemStack -> !itemStack.isEmpty()); - } - - @Override - public ItemStack getItem(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - return indexedCompartment.compartment().get(indexedCompartment.index()); - } - - @Override - public Component getName() { - return this.player.getName(); - } - - @Override - public ItemStack getArmor(int index) { - return this.armor.get(index); - } - - @Override - public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { - if (damage > 0.0F) { - damage /= 4.0F; - if (damage < 1.0F) { - damage = 1.0F; - } - - for (int index : armorIndices) { - ItemStack itemstack = this.armor.get(index); - if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { - itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); - } - } - } - } - - @Override - public void dropAll() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - ItemStack itemstack = compartment.get(i); - if (!itemstack.isEmpty()) { - this.player.drop(itemstack, true, false); - compartment.set(i, ItemStack.EMPTY); - } - } - } - } - - @Override - public void setChanged() { - super.setChanged(); - } - - @Override - public int getTimesChanged() { - return super.getTimesChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean contains(ItemStack itemstack) { - return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); - } - - @Override - public boolean contains(Tag tag) { - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag)); - } - - @Override - public void replaceWith(Inventory inventory) { - Function getter; - - if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { - getter = specialPlayerInventory::getRawItem; - } else { - getter = inventory::getItem; - } - - for(int i = 0; i < this.getContainerSize(); ++i) { - this.setRawItem(i, getter.apply(i)); - } - - this.selected = inventory.selected; - } - - @Override - public void clearContent() { - for (NonNullList compartment : this.compartments) { - compartment.clear(); - } - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountSimpleStack(itemstack); - } - } - - @Override - public ItemStack removeFromSelected(boolean dropWholeStack) { - ItemStack itemstack = this.getSelected(); - return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); - } - -} diff --git a/internal/v1_18_R1/pom.xml b/internal/v1_19_R1/pom.xml similarity index 94% rename from internal/v1_18_R1/pom.xml rename to internal/v1_19_R1/pom.xml index 1c938b62..67fd1cb9 100644 --- a/internal/v1_18_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -26,13 +26,13 @@ 4.2.0-SNAPSHOT - openinvadapter1_18_R1 - OpenInvAdapter1_18_R1 + openinvadapter1_19_R1 + OpenInvAdapter1_19_R1 17 17 - 1.18.1-R0.1-SNAPSHOT + 1.19-R0.1-SNAPSHOT diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java similarity index 98% rename from internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java rename to internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java index f7eb6586..df537467 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/AnySilentContainer.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_18_R1; +package com.lishid.openinv.internal.v1_19_R1; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; @@ -24,7 +24,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; @@ -53,7 +53,7 @@ import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -147,7 +147,7 @@ public boolean activateContainer( MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, new TranslatableComponent("container.enderchest"))); + }, Component.translatable(("container.enderchest")))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/OpenPlayer.java similarity index 92% rename from internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java rename to internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/OpenPlayer.java index a4bc7000..bc1781ab 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/OpenPlayer.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/OpenPlayer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_17_R1; +package com.lishid.openinv.internal.v1_19_R1; import java.io.File; import net.minecraft.Util; @@ -23,8 +23,8 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.apache.logging.log4j.LogManager; -import org.bukkit.craftbukkit.v1_17_R1.CraftServer; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; public class OpenPlayer extends CraftPlayer { diff --git a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java similarity index 92% rename from internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java rename to internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java index 75f18d54..da0232ad 100644 --- a/internal/v1_18_R1/src/main/java/com/lishid/openinv/internal/v1_18_R1/PlayerDataManager.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_18_R1; +package com.lishid.openinv.internal.v1_19_R1; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; @@ -23,7 +23,7 @@ import com.mojang.authlib.GameProfile; import java.lang.reflect.Field; import java.util.logging.Logger; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; @@ -35,10 +35,10 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_18_R1.CraftServer; -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.CraftContainer; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +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.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -87,7 +87,8 @@ public PlayerDataManager() { } // Create a profile and entity to load the player data - // See net.minecraft.server.PlayerList#attemptLogin + // See net.minecraft.server.players.PlayerList#canPlayerLogin + // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello GameProfile profile = new GameProfile(offline.getUniqueId(), offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); @@ -97,7 +98,7 @@ public PlayerDataManager() { return null; } - ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, null); try { injectPlayer(entity); @@ -164,7 +165,7 @@ public MenuType getType() { } }; - container.setTitle(new TextComponent(view.getTitle())); + container.setTitle(Component.literal(view.getTitle())); container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); if (container == null) { @@ -172,7 +173,7 @@ public MenuType getType() { } nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - new TextComponent(container.getBukkitView().getTitle()))); + Component.literal(container.getBukkitView().getTitle()))); nmsPlayer.containerMenu = container; nmsPlayer.initMenu(container); diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java similarity index 97% rename from internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java rename to internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java index 6d48d549..e2a5546e 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialEnderChest.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_17_R1; +package com.lishid.openinv.internal.v1_19_R1; import com.lishid.openinv.internal.ISpecialEnderChest; import java.util.List; @@ -33,8 +33,8 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; @@ -317,7 +317,7 @@ public ItemStack removeItemType(Item item, int i) { @Override public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); } @Override diff --git a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java similarity index 98% rename from internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java rename to internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java index af7d5cb2..d819e356 100644 --- a/internal/v1_17_R1/src/main/java/com/lishid/openinv/internal/v1_17_R1/SpecialPlayerInventory.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_17_R1; +package com.lishid.openinv.internal.v1_19_R1; import com.google.common.collect.ImmutableList; import com.lishid.openinv.internal.ISpecialPlayerInventory; @@ -33,7 +33,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.Tag; +import net.minecraft.tags.TagKey; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; import net.minecraft.world.damagesource.DamageSource; @@ -46,8 +46,8 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; @@ -138,7 +138,7 @@ private void setRawItem(int i, @NotNull ItemStack itemStack) { } } - private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { if (index < items.size()) { @@ -743,8 +743,9 @@ public boolean contains(ItemStack itemstack) { } @Override - public boolean contains(Tag tag) { - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tag)); + public boolean contains(TagKey tagKey) { + + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); } @Override diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index dbe7e988..d1dbf4ad 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -63,6 +63,7 @@ public String getReleasesLink() { case "v1_16_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; case "v1_8_R3", "v1_15_R1", "v1_16_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; case "v1_16_R3" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; + case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10"; default -> "https://github.com/Jikoo/OpenInv/releases"; }; } diff --git a/pom.xml b/pom.xml index 5d5ee6a6..e09b7e73 100644 --- a/pom.xml +++ b/pom.xml @@ -39,9 +39,8 @@ api plugin - internal/v1_17_R1 - internal/v1_18_R1 internal/v1_18_R2 + internal/v1_19_R1 assembly From 9c934e440d5bfe9c00bf525ba305c8f871340442 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 7 Jun 2022 14:09:51 -0400 Subject: [PATCH 078/340] Update main plugin to Java 17 All supported versions require Java 17, there's no reason the plugin should not. Swap to Temurin for CI, Adopt is dead. --- .github/workflows/ci.yml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 176cd38e..09682ef8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/setup-java@v3 with: - distribution: 'adopt' + distribution: 'temurin' java-version: '17' cache: 'maven' diff --git a/pom.xml b/pom.xml index e09b7e73..037125fd 100644 --- a/pom.xml +++ b/pom.xml @@ -27,8 +27,8 @@ UTF-8 - 16 - 16 + 17 + 17 unknown From fcb9ccb6e64d1e9c10a7759fc09a0f61529875a5 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 7 Jun 2022 19:41:39 -0400 Subject: [PATCH 079/340] Bump version to 4.2.0 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d815843b..5d907ec2 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.0-SNAPSHOT + 4.2.0 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 9afb4bed..7a25390b 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.0-SNAPSHOT + 4.2.0 openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 67cb8cc6..0d2487ef 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.0-SNAPSHOT + 4.2.0 openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 67fd1cb9..728b3616 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.0-SNAPSHOT + 4.2.0 openinvadapter1_19_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 4f0c33db..a03873f0 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.0-SNAPSHOT + 4.2.0 openinvplugincore diff --git a/pom.xml b/pom.xml index 037125fd..309bef53 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.0-SNAPSHOT + 4.2.0 pom @@ -83,13 +83,13 @@ openinvapi com.lishid compile - 4.2.0-SNAPSHOT + 4.2.0
openinvplugincore com.lishid compile - 4.2.0-SNAPSHOT + 4.2.0 com.lishid From 89db744113500c5d26ca60f637131d55422e03d5 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 7 Jun 2022 19:41:52 -0400 Subject: [PATCH 080/340] Bump version to 4.2.1-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 5d907ec2..fd7a2f88 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.0 + 4.2.1-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 7a25390b..eea02a4e 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.0 + 4.2.1-SNAPSHOT openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 0d2487ef..9e7e94e2 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.0 + 4.2.1-SNAPSHOT openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 728b3616..63e19bc1 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.0 + 4.2.1-SNAPSHOT openinvadapter1_19_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index a03873f0..aaf3501d 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.0 + 4.2.1-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 309bef53..5f73e61e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.0 + 4.2.1-SNAPSHOT pom @@ -83,13 +83,13 @@ openinvapi com.lishid compile - 4.2.0 + 4.2.1-SNAPSHOT openinvplugincore com.lishid compile - 4.2.0 + 4.2.1-SNAPSHOT com.lishid From 1093b6d9111a6540a31c217b2c8cabde8d51e8bf Mon Sep 17 00:00:00 2001 From: Elioby <7284033+Elioby@users.noreply.github.com> Date: Sun, 31 Jul 2022 02:04:40 +0100 Subject: [PATCH 081/340] Update to 1.19.1 (#89) --- internal/v1_19_R1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 63e19bc1..9e4773ee 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -32,7 +32,7 @@ 17 17 - 1.19-R0.1-SNAPSHOT + 1.19.1-R0.1-SNAPSHOT From 23e2a7e5342603afeb8c885f0f7c378b348eeeb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 13:32:56 -0400 Subject: [PATCH 082/340] Bump maven-assembly-plugin from 3.3.0 to 3.4.2 (#94) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f73e61e..72ebd0e3 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ maven-assembly-plugin org.apache.maven.plugins - 3.3.0 + 3.4.2 From 6b38cb72017f45d956bc5f2160c11482e084ea1c Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 1 Aug 2022 13:35:00 -0400 Subject: [PATCH 083/340] Bump version to 4.2.1 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index fd7a2f88..700c9950 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.1-SNAPSHOT + 4.2.1 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index eea02a4e..ed10314c 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.1-SNAPSHOT + 4.2.1 openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 9e7e94e2..803b888a 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.1-SNAPSHOT + 4.2.1 openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 9e4773ee..76da41da 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.1-SNAPSHOT + 4.2.1 openinvadapter1_19_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index aaf3501d..9198f35e 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.1-SNAPSHOT + 4.2.1 openinvplugincore diff --git a/pom.xml b/pom.xml index 72ebd0e3..4c956ce4 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.1-SNAPSHOT + 4.2.1 pom @@ -83,13 +83,13 @@ openinvapi com.lishid compile - 4.2.1-SNAPSHOT + 4.2.1 openinvplugincore com.lishid compile - 4.2.1-SNAPSHOT + 4.2.1 com.lishid From 8c7698b40ed7d5af1df418e4ccc0bb1e48f57e3f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 1 Aug 2022 13:35:25 -0400 Subject: [PATCH 084/340] Bump version to 4.2.2-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 700c9950..540de2ae 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.1 + 4.2.2-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index ed10314c..fb22eb6e 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.1 + 4.2.2-SNAPSHOT openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 803b888a..f99be39f 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.1 + 4.2.2-SNAPSHOT openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 76da41da..d0ebc5e9 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.1 + 4.2.2-SNAPSHOT openinvadapter1_19_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 9198f35e..43948c40 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.1 + 4.2.2-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 4c956ce4..6a76cb24 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.1 + 4.2.2-SNAPSHOT pom @@ -83,13 +83,13 @@ openinvapi com.lishid compile - 4.2.1 + 4.2.2-SNAPSHOT openinvplugincore com.lishid compile - 4.2.1 + 4.2.2-SNAPSHOT com.lishid From 1e0fb7bdb195ffff80d392e1f4b16f1c07c44a92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 10 Sep 2022 14:52:17 -0400 Subject: [PATCH 085/340] Bump hmarr/auto-approve-action from 2.2.1 to 2.4.0 (#99) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09682ef8..2fe5f9ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Approve - uses: hmarr/auto-approve-action@v2.2.1 + uses: hmarr/auto-approve-action@v2.4.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Merge From 1ceaf296f255cf8a7af8787c057816af10af6733 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 12:36:48 -0400 Subject: [PATCH 086/340] Bump maven-compiler-plugin from 3.8.1 to 3.10.1 (#76) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a76cb24..6b3943b7 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,7 @@ maven-compiler-plugin org.apache.maven.plugins - 3.8.1 + 3.10.1 From d0648cc463c27781fd501dcd0179c5c6b0e55075 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 1 Oct 2022 12:43:57 -0400 Subject: [PATCH 087/340] Fix incorrect player's locale used in view title (#101) --- .../java/com/lishid/openinv/internal/OpenInventoryView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index f1aaadb0..7087e3da 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -67,7 +67,7 @@ public OpenInventoryView(Player player, ISpecialInventory inventory, String titl String localTitle = OpenInv.getPlugin(OpenInv.class) .getLocalizedMessage( - owner, + player, titleKey, "%player%", owner.getName()); From 2679a520a58fed372d2bc81934bc8523af2aac7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 15:12:52 -0400 Subject: [PATCH 088/340] Bump actions/checkout from 2 to 3 (#103) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13c09979..c633e688 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 From b872f94855a32c020882aa6969bc95469ed55315 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 15:13:03 -0400 Subject: [PATCH 089/340] Bump maven-shade-plugin from 3.3.0 to 3.4.0 (#102) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b3943b7..3dbb75a5 100644 --- a/pom.xml +++ b/pom.xml @@ -133,7 +133,7 @@ org.apache.maven.plugins - 3.3.0 + 3.4.0 From c443615c1d23f88cca93c7db14786b2119b33232 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 14 Oct 2022 13:45:26 -0400 Subject: [PATCH 090/340] Fix cache never being used for Spigot dependencies (#105) --- pom.xml | 10 ++++++++++ scripts/install_spigot_dependencies.sh | 6 ++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 3dbb75a5..b6f15d5b 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,16 @@ + + + maven-dependency-plugin + org.apache.maven.plugins + 3.3.0 + + maven-shade-plugin diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot_dependencies.sh index dd3cbfc5..61668d42 100644 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot_dependencies.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (C) 2011-2021 lishid. All rights reserved. +# Copyright (C) 2011-2022 lishid. All rights reserved. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -34,9 +34,7 @@ readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" echo Found Spigot dependencies: "${versions[@]}" # Install dependencies aside from Spigot prior to running in offline mode. -# Note that the default SuperPOM declares maven-dependency-plugin 2.8.0. -# Unfortunately, we run into MDEP-204 and require a version >= 3.1.2. -mvn org.apache.maven.plugins:maven-dependency-plugin:3.2.0:go-offline -DexcludeArtifactIds=spigot +mvn dependency:go-offline -DexcludeArtifactIds=spigot for version in "${versions[@]}"; do set -e From 0120d35a9aa7753d6642b41dc2da0b1bec97e718 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 14 Oct 2022 16:40:33 -0400 Subject: [PATCH 091/340] Fix advancement-related memory leak (#104) Fix memory leak with loaded players' advancements Fix missing transaction transfer for player inventories Fix incorrect transaction transfer for ender chests Minor code health --- .../internal/v1_18_R2/PlayerDataManager.java | 3 ++ .../internal/v1_18_R2/SpecialEnderChest.java | 35 ++++++++++----- .../v1_18_R2/SpecialPlayerInventory.java | 45 +++++++++++++------ .../internal/v1_19_R1/PlayerDataManager.java | 3 ++ .../internal/v1_19_R1/SpecialEnderChest.java | 33 +++++++++----- .../v1_19_R1/SpecialPlayerInventory.java | 43 ++++++++++++------ .../main/java/com/lishid/openinv/OpenInv.java | 5 ++- .../openinv/internal/IPlayerDataManager.java | 2 +- .../openinv/internal/OpenInventoryView.java | 14 +++--- 9 files changed, 125 insertions(+), 58 deletions(-) diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java index 1be5f57b..127e39c1 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java @@ -99,6 +99,9 @@ public PlayerDataManager() { ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + try { injectPlayer(entity); } catch (IllegalAccessException e) { diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java index f46ff616..e0e00a82 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialEnderChest.java @@ -67,18 +67,29 @@ public void setPlayerOffline() { @Override public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (!this.playerOnline) { - try { - this.owner = PlayerDataManager.getHandle(player); - PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); - for (int i = 0; i < enderChest.getContainerSize(); ++i) { - enderChest.setItem(i, this.items.get(i)); - } - this.items = enderChest.items; - enderChest.transaction.addAll(this.transaction); - } catch (Exception ignored) {} - this.playerOnline = true; + if (this.playerOnline) { + return; + } + + ServerPlayer offlinePlayer = this.owner; + ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + + // Set owner to new player. + this.owner = onlinePlayer; + + // Set player's ender chest contents to our modified contents. + PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); + for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { + onlineEnderChest.setItem(i, this.items.get(i)); } + + // Set our item array to the new inventory's array. + this.items = onlineEnderChest.items; + + // Add viewers to new inventory. + onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); + + this.playerOnline = true; } @Override @@ -317,7 +328,7 @@ public ItemStack removeItemType(Item item, int i) { @Override public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).collect(Collectors.toList()).toString(); + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); } @Override diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java index 04c2c5fb..b0071558 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/SpecialPlayerInventory.java @@ -77,20 +77,37 @@ public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @N @Override public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (!this.playerOnline) { - Player entityPlayer = PlayerDataManager.getHandle(player); - entityPlayer.getInventory().transaction.addAll(this.transaction); - this.player = entityPlayer; - for (int i = 0; i < getContainerSize(); ++i) { - this.player.getInventory().setItem(i, getRawItem(i)); - } - this.player.getInventory().selected = this.selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - this.playerOnline = true; + if (this.playerOnline) { + return; + } + + Player offlinePlayer = this.player; + Player onlinePlayer = PlayerDataManager.getHandle(player); + onlinePlayer.getInventory().transaction.addAll(this.transaction); + + // Set owner to new player. + this.player = onlinePlayer; + + // Set player's inventory contents to our modified contents. + Inventory onlineInventory = onlinePlayer.getInventory(); + for (int i = 0; i < getContainerSize(); ++i) { + onlineInventory.setItem(i, getRawItem(i)); } + onlineInventory.selected = this.selected; + + // Set our item arrays to the new inventory's arrays. + this.items = onlineInventory.items; + this.armor = onlineInventory.armor; + this.offhand = onlineInventory.offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + + // Add existing viewers to new viewer list. + Inventory offlineInventory = offlinePlayer.getInventory(); + // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. + offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); + onlineInventory.transaction.addAll(offlineInventory.transaction); + + this.playerOnline = true; } @Override @@ -138,7 +155,7 @@ private void setRawItem(int i, @NotNull ItemStack itemStack) { } } - private static record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { if (index < items.size()) { diff --git a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java index da0232ad..35344852 100644 --- a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/PlayerDataManager.java @@ -100,6 +100,9 @@ public PlayerDataManager() { ServerPlayer entity = new ServerPlayer(server, worldServer, profile, null); + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + try { injectPlayer(entity); } catch (IllegalAccessException e) { diff --git a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java index e2a5546e..d46583ff 100644 --- a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialEnderChest.java @@ -67,18 +67,29 @@ public void setPlayerOffline() { @Override public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (!this.playerOnline) { - try { - this.owner = PlayerDataManager.getHandle(player); - PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); - for (int i = 0; i < enderChest.getContainerSize(); ++i) { - enderChest.setItem(i, this.items.get(i)); - } - this.items = enderChest.items; - enderChest.transaction.addAll(this.transaction); - } catch (Exception ignored) {} - this.playerOnline = true; + if (this.playerOnline) { + return; + } + + ServerPlayer offlinePlayer = this.owner; + ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + + // Set owner to new player. + this.owner = onlinePlayer; + + // Set player's ender chest contents to our modified contents. + PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); + for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { + onlineEnderChest.setItem(i, this.items.get(i)); } + + // Set our item array to the new inventory's array. + this.items = onlineEnderChest.items; + + // Add viewers to new inventory. + onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); + + this.playerOnline = true; } @Override diff --git a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java index d819e356..cc5ecbd0 100644 --- a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/SpecialPlayerInventory.java @@ -77,20 +77,37 @@ public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @N @Override public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (!this.playerOnline) { - Player entityPlayer = PlayerDataManager.getHandle(player); - entityPlayer.getInventory().transaction.addAll(this.transaction); - this.player = entityPlayer; - for (int i = 0; i < getContainerSize(); ++i) { - this.player.getInventory().setItem(i, getRawItem(i)); - } - this.player.getInventory().selected = this.selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - this.playerOnline = true; + if (this.playerOnline) { + return; + } + + Player offlinePlayer = this.player; + Player onlinePlayer = PlayerDataManager.getHandle(player); + onlinePlayer.getInventory().transaction.addAll(this.transaction); + + // Set owner to new player. + this.player = onlinePlayer; + + // Set player's inventory contents to our modified contents. + Inventory onlineInventory = onlinePlayer.getInventory(); + for (int i = 0; i < getContainerSize(); ++i) { + onlineInventory.setItem(i, getRawItem(i)); } + onlineInventory.selected = this.selected; + + // Set our item arrays to the new inventory's arrays. + this.items = onlineInventory.items; + this.armor = onlineInventory.armor; + this.offhand = onlineInventory.offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + + // Add existing viewers to new viewer list. + Inventory offlineInventory = offlinePlayer.getInventory(); + // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. + offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); + onlineInventory.transaction.addAll(offlineInventory.transaction); + + this.playerOnline = true; } @Override diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 06cc68f4..98b634eb 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -476,8 +476,9 @@ void handleCloseInventory(@NotNull HumanEntity exViewer, @NotNull ISpecialInvent if (!disableSaving() && current != null - && current.getPlayer() instanceof Player player && !player.isOnline()) { - this.accessor.getPlayerDataManager().inject(player).saveData(); + && current.getPlayer() instanceof Player player + && !player.isOnline()) { + this.accessor.getPlayerDataManager().inject(player).saveData(); } }); } diff --git a/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java b/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java index 37780546..ca669324 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index 7087e3da..a7f4a958 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -27,13 +27,17 @@ public class OpenInventoryView extends InventoryView { - private final Player player; - private final ISpecialInventory inventory; - private final String titleKey; - private final String titleDefaultSuffix; + private final @NotNull Player player; + private final @NotNull ISpecialInventory inventory; + private final @NotNull String titleKey; + private final @NotNull String titleDefaultSuffix; private String title; - public OpenInventoryView(Player player, ISpecialInventory inventory, String titleKey, String titleDefaultSuffix) { + public OpenInventoryView( + @NotNull Player player, + @NotNull ISpecialInventory inventory, + @NotNull String titleKey, + @NotNull String titleDefaultSuffix) { this.player = player; this.inventory = inventory; this.titleKey = titleKey; From 961a9006329a24b90beb339a6953f2a934e02a77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:45:43 -0500 Subject: [PATCH 092/340] Bump maven-dependency-plugin from 3.3.0 to 3.4.0 (#108) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b6f15d5b..43077ecc 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ --> maven-dependency-plugin org.apache.maven.plugins - 3.3.0 + 3.4.0 From 1efc0d49047ca00a1c9c6a1183d44b120786d1f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:45:59 -0500 Subject: [PATCH 093/340] Bump maven-shade-plugin from 3.4.0 to 3.4.1 (#112) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 43077ecc..e4d04949 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ org.apache.maven.plugins - 3.4.0 + 3.4.1 From e5128fa8db461dca98b78c77a61207fb08375df7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:46:12 -0500 Subject: [PATCH 094/340] Bump pascalgn/automerge-action from 0.15.3 to 0.15.5 (#109) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fe5f9ff..f2046b8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Merge - uses: pascalgn/automerge-action@v0.15.3 + uses: pascalgn/automerge-action@v0.15.5 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "dependencies" From d7e78177e076fad396abd87e7d3ed0104f285ab7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:46:26 -0500 Subject: [PATCH 095/340] Bump softprops/action-gh-release from 0.1.14 to 0.1.15 (#110) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2046b8f..6ed1dae2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v0.1.14 + uses: softprops/action-gh-release@v0.1.15 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 801530490da957aa3b6c4925759a4d4e4695a0e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:46:55 -0500 Subject: [PATCH 096/340] Bump dsaltares/fetch-gh-release-asset from 1.0.0 to 1.1.0 (#111) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c633e688..e74709eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: fetch-depth: 0 - name: Fetch Github Release Asset - uses: dsaltares/fetch-gh-release-asset@1.0.0 + uses: dsaltares/fetch-gh-release-asset@1.1.0 with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ github.event.release.id }} From 7b16391a80ecf3aff61243b12e8696bfa9d3db1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:47:23 -0500 Subject: [PATCH 097/340] Bump hmarr/auto-approve-action from 2.4.0 to 3.1.0 (#113) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ed1dae2..3005d7a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Approve - uses: hmarr/auto-approve-action@v2.4.0 + uses: hmarr/auto-approve-action@v3.1.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Merge From 46f3396b47b7b4626771ffd01617ffdce42e0ee9 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Dec 2022 15:57:33 -0500 Subject: [PATCH 098/340] Update to Minecraft 1.19.3 1.19.2 is still supported - 1.19.1 was only dropped because there was no Spigot NMS revision bump. --- internal/v1_19_R2/pom.xml | 85 ++ .../internal/v1_19_R2/AnySilentContainer.java | 277 ++++++ .../openinv/internal/v1_19_R2/OpenPlayer.java | 74 ++ .../internal/v1_19_R2/PlayerDataManager.java | 234 ++++++ .../internal/v1_19_R2/SpecialEnderChest.java | 339 ++++++++ .../v1_19_R2/SpecialPlayerInventory.java | 788 ++++++++++++++++++ pom.xml | 1 + 7 files changed, 1798 insertions(+) create mode 100644 internal/v1_19_R2/pom.xml create mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java create mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java create mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java create mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java create mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java diff --git a/internal/v1_19_R2/pom.xml b/internal/v1_19_R2/pom.xml new file mode 100644 index 00000000..3e96c742 --- /dev/null +++ b/internal/v1_19_R2/pom.xml @@ -0,0 +1,85 @@ + + + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 4.2.2-SNAPSHOT + + + openinvadapter1_19_R2 + OpenInvAdapter1_19_R2 + + + 17 + 17 + 1.19.3-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot-api + ${spigot.version} + + + spigot + org.spigotmc + provided + ${spigot.version} + remapped-mojang + + + openinvapi + com.lishid + provided + + + openinvplugincore + com.lishid + + + annotations + org.jetbrains + + + + + + + maven-shade-plugin + + + false + + + + maven-compiler-plugin + + + net.md-5 + specialsource-maven-plugin + + + + + diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java new file mode 100644 index 00000000..3abd77ac --- /dev/null +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_19_R2; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.util.ReflectionHelper; +import java.lang.reflect.Field; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.CompoundContainer; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.DoubleBlockCombiner; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.ShulkerBox; +import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AnySilentContainer implements IAnySilentContainer { + + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer() { + try { + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } catch (SecurityException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(Level.WARNING, "Error obtaining GameType field", e); + } + } + + @Override + public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { + org.bukkit.World bukkitWorld = shulkerBox.getWorld(); + if (!(bukkitWorld instanceof CraftWorld)) { + bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); + } + + if (!(bukkitWorld instanceof CraftWorld craftWorld)) { + Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); + OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); + return false; + } + + final ServerLevel world = craftWorld.getHandle(); + final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); + final BlockEntity tile = world.getBlockEntity(blockPosition); + + if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) + || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { + return false; + } + + BlockState blockState = world.getBlockState(blockPosition); + + // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen + AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) + .move(blockPosition) + .deflate(1.0E-6D); + return !world.noCollision(boundingBox); + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + final ServerLevel level = player.getLevel(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); + + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, Component.translatable(("container.enderchest")))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: do not check if chest is blocked + Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( + // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER + new DoubleBlockCombiner.Combiner>() { + @Override + public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { + CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); + return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); + } + + @Override + public Optional acceptSingle(ChestBlockEntity localChest) { + return Optional.of(localChest); + } + + @Override + public Optional acceptNone() { + return Optional.empty(); + } + }); + + if (menuOptional.isEmpty()) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + menuProvider = menuOptional.get(); + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; + } + + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null) { + return; + } + + InventoryView view = bukkitPlayer.getOpenInventory(); + switch (view.getType()) { + case CHEST: + case ENDER_CHEST: + case SHULKER_BOX: + case BARREL: + break; + default: + return; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // ServerPlayer#closeContainer cannot be called without entering an + // infinite loop because this method is called during inventory close. + // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent + player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); + // From ServerPlayer#closeContainer + player.doCloseContainer(); + // Regular inventory close will handle the rest - packet sending, etc. + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java new file mode 100644 index 00000000..4dab3343 --- /dev/null +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_19_R2; + +import java.io.File; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.apache.logging.log4j.LogManager; +import org.bukkit.craftbukkit.v1_19_R2.CraftServer; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; + +public class OpenPlayer extends CraftPlayer { + + public OpenPlayer(CraftServer server, ServerPlayer entity) { + super(server, entity); + } + + @Override + public void loadData() { + // See CraftPlayer#loadData + CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); + if (loaded != null) { + readExtraData(loaded); + } + } + + @Override + public void saveData() { + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + setExtraData(playerData); + + if (!isOnline()) { + // Special case: save old vehicle data + CompoundTag oldData = worldNBTStorage.load(player); + + if (oldData != null && oldData.contains("RootVehicle", 10)) { + // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) + playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + } + + File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + NbtIo.writeCompressed(playerData, file); + File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); + File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(file1, file, file2); + } catch (Exception e) { + LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage()); + } + } + +} diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java new file mode 100644 index 00000000..4cc6939b --- /dev/null +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_19_R2; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IPlayerDataManager; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.OpenInventoryView; +import com.mojang.authlib.GameProfile; +import java.lang.reflect.Field; +import java.util.logging.Logger; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_19_R2.CraftServer; +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.CraftContainer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerDataManager implements IPlayerDataManager { + + private @Nullable Field bukkitEntity; + + public PlayerDataManager() { + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } + + // Create a profile and entity to load the player data + // See net.minecraft.server.players.PlayerList#canPlayerLogin + // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + + if (worldServer == null) { + return null; + } + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + // Get the bukkit entity + Player target = entity.getBukkitEntity(); + if (target != null) { + // Load data + target.loadData(); + } + // Return the entity + return target; + } + + void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + } + + @NotNull + @Override + public Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return player; + } + } + + @Nullable + @Override + public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + + ServerPlayer nmsPlayer = getHandle(player); + + if (nmsPlayer.connection == null) { + return null; + } + + InventoryView view = getView(player, inventory); + + if (view == null) { + return player.openInventory(inventory.getBukkitInventory()); + } + + AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + @Override + public MenuType getType() { + return getContainers(inventory.getBukkitInventory().getSize()); + } + }; + + container.setTitle(Component.literal(view.getTitle())); + container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + + if (container == null) { + return null; + } + + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), + Component.literal(container.getBukkitView().getTitle()))); + nmsPlayer.containerMenu = container; + nmsPlayer.initMenu(container); + + return container.getBukkitView(); + + } + + private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { + if (inventory instanceof SpecialEnderChest) { + return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); + } else if (inventory instanceof SpecialPlayerInventory) { + return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); + } else { + return null; + } + } + + static @NotNull MenuType getContainers(int inventorySize) { + + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> MenuType.GENERIC_9x3; // Default 27-slot inventory + }; + } + + @Override + public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java new file mode 100644 index 00000000..bb5680ce --- /dev/null +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_19_R2; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ContainerListener; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { + + private final CraftInventory inventory; + private ServerPlayer owner; + private NonNullList items; + private boolean playerOnline; + + public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { + super(PlayerDataManager.getHandle(player)); + this.inventory = new CraftInventory(this); + this.owner = PlayerDataManager.getHandle(player); + this.playerOnline = online; + this.items = this.owner.getEnderChestInventory().items; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { + if (!this.playerOnline) { + try { + this.owner = PlayerDataManager.getHandle(player); + PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); + for (int i = 0; i < enderChest.getContainerSize(); ++i) { + enderChest.setItem(i, this.items.get(i)); + } + this.items = enderChest.items; + enderChest.transaction.addAll(this.transaction); + } catch (Exception ignored) {} + this.playerOnline = true; + } + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public List getContents() { + return this.items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.owner.getEnderChestInventory().getViewers(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void setActiveChest(EnderChestBlockEntity enderChest) { + this.owner.getEnderChestInventory().setActiveChest(enderChest); + } + + @Override + public boolean isActiveChest(EnderChestBlockEntity enderChest) { + return this.owner.getEnderChestInventory().isActiveChest(enderChest); + } + + @Override + public int getMaxStackSize() { + return this.owner.getEnderChestInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int i) { + this.owner.getEnderChestInventory().setMaxStackSize(i); + } + + @Override + public InventoryHolder getOwner() { + return this.owner.getEnderChestInventory().getOwner(); + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void addListener(ContainerListener listener) { + this.owner.getEnderChestInventory().addListener(listener); + } + + @Override + public void removeListener(ContainerListener listener) { + this.owner.getEnderChestInventory().removeListener(listener); + } + + @Override + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int i, int j) { + ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack addItem(ItemStack itemstack) { + ItemStack localItem = itemstack.copy(); + this.moveItemToOccupiedSlotsWithSameType(localItem); + if (localItem.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.moveItemToEmptySlots(localItem); + return localItem.isEmpty() ? ItemStack.EMPTY : localItem; + } + } + + @Override + public boolean canAddItem(ItemStack itemstack) { + for (ItemStack itemstack1 : this.items) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + private void moveItemToEmptySlots(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (localItem.isEmpty()) { + this.setItem(i, itemstack.copy()); + itemstack.setCount(0); + return; + } + } + } + + private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (ItemStack.isSameItemSameTags(localItem, itemstack)) { + this.moveItemsBetweenStacks(itemstack, localItem); + if (itemstack.isEmpty()) { + return; + } + } + } + } + + private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { + int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); + int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); + if (j > 0) { + itemstack1.grow(j); + itemstack.shrink(j); + this.setChanged(); + } + } + + @Override + public ItemStack removeItemNoUpdate(int i) { + ItemStack itemstack = this.items.get(i); + if (itemstack.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.items.set(i, ItemStack.EMPTY); + return itemstack; + } + } + + @Override + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.setChanged(); + } + + @Override + public int getContainerSize() { + return this.owner.getEnderChestInventory().getContainerSize(); + } + + @Override + public boolean isEmpty() { + return this.items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void startOpen(Player player) { + } + + @Override + public void stopOpen(Player player) { + } + + @Override + public boolean canPlaceItem(int i, ItemStack itemstack) { + return true; + } + + @Override + public void clearContent() { + this.items.clear(); + this.setChanged(); + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountStack(itemstack); + } + + } + + @Override + public List removeAllItems() { + List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clearContent(); + return list; + } + + @Override + public ItemStack removeItemType(Item item, int i) { + ItemStack itemstack = new ItemStack(item, 0); + + for(int j = this.getContainerSize() - 1; j >= 0; --j) { + ItemStack localItem = this.getItem(j); + if (localItem.getItem().equals(item)) { + int k = i - itemstack.getCount(); + ItemStack splitItem = localItem.split(k); + itemstack.grow(splitItem.getCount()); + if (itemstack.getCount() == i) { + break; + } + } + } + + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public String toString() { + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); + } + + @Override + public void fromTag(ListTag listTag) { + for (int i = 0; i < this.getContainerSize(); ++i) { + this.setItem(i, ItemStack.EMPTY); + } + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + if (j < this.getContainerSize()) { + this.setItem(j, ItemStack.of(compoundTag)); + } + } + + } + +} diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java new file mode 100644 index 00000000..ef0cca6f --- /dev/null +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java @@ -0,0 +1,788 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_19_R2; + +import com.google.common.collect.ImmutableList; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_19_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { + + private final CraftInventory inventory; + private boolean playerOnline; + private Player player; + private NonNullList items; + private NonNullList armor; + private NonNullList offhand; + private List> compartments; + + public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { + super(PlayerDataManager.getHandle(bukkitPlayer)); + this.inventory = new CraftInventory(this); + this.playerOnline = online; + this.player = super.player; + this.selected = player.getInventory().selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + if (!this.playerOnline) { + Player entityPlayer = PlayerDataManager.getHandle(player); + entityPlayer.getInventory().transaction.addAll(this.transaction); + this.player = entityPlayer; + for (int i = 0; i < getContainerSize(); ++i) { + this.player.getInventory().setItem(i, getRawItem(i)); + } + this.player.getInventory().selected = this.selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + this.playerOnline = true; + } + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } + + private @NotNull ItemStack getRawItem(int i) { + if (i < 0) { + return ItemStack.EMPTY; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + return list.get(i); + } + } + + return ItemStack.EMPTY; + } + + private void setRawItem(int i, @NotNull ItemStack itemStack) { + if (i < 0) { + return; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + list.set(i, itemStack); + } + } + } + + private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + + private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { + if (index < items.size()) { + return new IndexedCompartment(items, getReversedItemSlotNum(index)); + } + + index -= items.size(); + + if (index < armor.size()) { + return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); + } + + index -= armor.size(); + + if (index < offhand.size()) { + return new IndexedCompartment(offhand, index); + } + + index -= offhand.size(); + + return new IndexedCompartment(null, index); + } + + private int getReversedArmorSlotNum(final int i) { + if (i == 0) { + return 3; + } + if (i == 1) { + return 2; + } + if (i == 2) { + return 1; + } + if (i == 3) { + return 0; + } + return i; + } + + private int getReversedItemSlotNum(final int i) { + if (i >= 27) { + return i - 27; + } + return i + 9; + } + + private boolean contains(Predicate predicate) { + return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); + } + + @Override + public List getArmorContents() { + return this.armor; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.player.getInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.player.getInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.player.getInventory().getViewers(); + } + + @Override + public InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + @Override + public int getMaxStackSize() { + return this.player.getInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int size) { + this.player.getInventory().setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return this.player.getBukkitEntity().getLocation(); + } + + @Override + public boolean hasCustomName() { + return false; + } + + @Override + public List getContents() { + return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + + @Override + public ItemStack getSelected() { + return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; + } + + private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + @Override + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getRawItem(i); + if (itemstack1.isEmpty()) { + return itemstack.getCount(); + } + + if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); + } + + if (remains <= 0) { + return itemstack.getCount(); + } + } + + ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); + if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { + remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); + } + + return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; + } + + @Override + public int getFreeSlot() { + for(int i = 0; i < this.items.size(); ++i) { + if (this.items.get(i).isEmpty()) { + return i; + } + } + + return -1; + } + + @Override + public void setPickedItem(ItemStack itemstack) { + int i = this.findSlotMatchingItem(itemstack); + if (isHotbarSlot(i)) { + this.selected = i; + } else if (i == -1) { + this.selected = this.getSuitableHotbarSlot(); + if (!this.items.get(this.selected).isEmpty()) { + int j = this.getFreeSlot(); + if (j != -1) { + this.items.set(j, this.items.get(this.selected)); + } + } + + this.items.set(this.selected, itemstack); + } else { + this.pickSlot(i); + } + + } + + @Override + public void pickSlot(int i) { + this.selected = this.getSuitableHotbarSlot(); + ItemStack itemstack = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(i)); + this.items.set(i, itemstack); + } + + @Override + public int findSlotMatchingItem(ItemStack itemstack) { + for(int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { + return i; + } + } + + return -1; + } + + @Override + public int findSlotMatchingUnusedItem(ItemStack itemStack) { + for(int i = 0; i < this.items.size(); ++i) { + ItemStack localItem = this.items.get(i); + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { + return i; + } + } + + return -1; + } + + @Override + public int getSuitableHotbarSlot() { + int i; + int j; + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (this.items.get(i).isEmpty()) { + return i; + } + } + + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (!this.items.get(i).isEnchanted()) { + return i; + } + } + + return this.selected; + } + + @Override + public void swapPaint(double d0) { + if (d0 > 0.0D) { + d0 = 1.0D; + } + + if (d0 < 0.0D) { + d0 = -1.0D; + } + + this.selected = (int) (this.selected - d0); + + while (this.selected < 0) { + this.selected += 9; + } + + while(this.selected >= 9) { + this.selected -= 9; + } + } + + @Override + public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { + byte b0 = 0; + boolean flag = i == 0; + int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); + j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); + ItemStack itemstack = this.player.containerMenu.getCarried(); + j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); + if (itemstack.isEmpty()) { + this.player.containerMenu.setCarried(ItemStack.EMPTY); + } + + return j; + } + + private int addResource(ItemStack itemstack) { + int i = this.getSlotWithRemainingSpace(itemstack); + if (i == -1) { + i = this.getFreeSlot(); + } + + return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); + } + + private int addResource(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + int j = itemstack.getCount(); + ItemStack localItemStack = this.getRawItem(i); + if (localItemStack.isEmpty()) { + localItemStack = new ItemStack(item, 0); + if (itemstack.hasTag()) { + // hasTag ensures tag not null + //noinspection ConstantConditions + localItemStack.setTag(itemstack.getTag().copy()); + } + + this.setRawItem(i, localItemStack); + } + + int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); + + if (k > this.getMaxStackSize() - localItemStack.getCount()) { + k = this.getMaxStackSize() - localItemStack.getCount(); + } + + if (k != 0) { + j -= k; + localItemStack.grow(k); + localItemStack.setPopTime(5); + } + + return j; + } + + @Override + public int getSlotWithRemainingSpace(ItemStack itemstack) { + if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { + return this.selected; + } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { + return 40; + } else { + for(int i = 0; i < this.items.size(); ++i) { + if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + @Override + public void tick() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (!compartment.get(i).isEmpty()) { + compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); + } + } + } + + } + + @Override + public boolean add(ItemStack itemStack) { + return this.add(-1, itemStack); + } + + @Override + public boolean add(int i, ItemStack itemStack) { + if (itemStack.isEmpty()) { + return false; + } else { + try { + if (itemStack.isDamaged()) { + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i >= 0) { + this.items.set(i, itemStack.copy()); + this.items.get(i).setPopTime(5); + itemStack.setCount(0); + return true; + } else if (this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + do { + j = itemStack.getCount(); + if (i == -1) { + itemStack.setCount(this.addResource(itemStack)); + } else { + itemStack.setCount(this.addResource(i, itemStack)); + } + } while(!itemStack.isEmpty() && itemStack.getCount() < j); + + if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return itemStack.getCount() < j; + } + } + } catch (Throwable var6) { + CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); + crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); + crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); + crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); + throw new ReportedException(crashReport); + } + } + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack) { + this.placeItemBackInInventory(itemStack, true); + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { + while(true) { + if (!itemStack.isEmpty()) { + int i = this.getSlotWithRemainingSpace(itemStack); + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i != -1) { + int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); + if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { + ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); + } + continue; + } + + this.player.drop(itemStack, false); + } + + return; + } + } + + @Override + public ItemStack removeItem(int rawIndex, final int j) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null + || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { + return ItemStack.EMPTY; + } + + return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); + } + + @Override + public void removeItem(ItemStack itemStack) { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (compartment.get(i) == itemStack) { + compartment.set(i, ItemStack.EMPTY); + break; + } + } + } + } + + @Override + public ItemStack removeItemNoUpdate(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); + + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + + return removed; + } + + @Override + public void setItem(int rawIndex, final ItemStack itemStack) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + this.player.drop(itemStack, true); + return; + } + + indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); + } + + @Override + public float getDestroySpeed(BlockState blockState) { + return this.items.get(this.selected).getDestroySpeed(blockState); + } + + @Override + public ListTag save(ListTag listTag) { + for (int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)i); + this.items.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.armor.size(); ++i) { + if (!this.armor.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 100)); + this.armor.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.offhand.size(); ++i) { + if (!this.offhand.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 150)); + this.offhand.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + return listTag; + } + + @Override + public void load(ListTag listTag) { + this.items.clear(); + this.armor.clear(); + this.offhand.clear(); + + for(int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.of(compoundTag); + if (!itemstack.isEmpty()) { + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); + } + } + } + + } + + @Override + public int getContainerSize() { + return 45; + } + + @Override + public boolean isEmpty() { + return !contains(itemStack -> !itemStack.isEmpty()); + } + + @Override + public ItemStack getItem(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + return indexedCompartment.compartment().get(indexedCompartment.index()); + } + + @Override + public Component getName() { + return this.player.getName(); + } + + @Override + public ItemStack getArmor(int index) { + return this.armor.get(index); + } + + @Override + public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { + if (damage > 0.0F) { + damage /= 4.0F; + if (damage < 1.0F) { + damage = 1.0F; + } + + for (int index : armorIndices) { + ItemStack itemstack = this.armor.get(index); + if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { + itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); + } + } + } + } + + @Override + public void dropAll() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + ItemStack itemstack = compartment.get(i); + if (!itemstack.isEmpty()) { + this.player.drop(itemstack, true, false); + compartment.set(i, ItemStack.EMPTY); + } + } + } + } + + @Override + public void setChanged() { + super.setChanged(); + } + + @Override + public int getTimesChanged() { + return super.getTimesChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public boolean contains(ItemStack itemstack) { + return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); + } + + @Override + public boolean contains(TagKey tagKey) { + + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); + } + + @Override + public void replaceWith(Inventory inventory) { + Function getter; + + if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { + getter = specialPlayerInventory::getRawItem; + } else { + getter = inventory::getItem; + } + + for(int i = 0; i < this.getContainerSize(); ++i) { + this.setRawItem(i, getter.apply(i)); + } + + this.selected = inventory.selected; + } + + @Override + public void clearContent() { + for (NonNullList compartment : this.compartments) { + compartment.clear(); + } + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountSimpleStack(itemstack); + } + } + + @Override + public ItemStack removeFromSelected(boolean dropWholeStack) { + ItemStack itemstack = this.getSelected(); + return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); + } + +} diff --git a/pom.xml b/pom.xml index e4d04949..8eef7ca0 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ plugin internal/v1_18_R2 internal/v1_19_R1 + internal/v1_19_R2 assembly From 381dbb2a7821dbb280b27befe612b114fb3e2988 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Dec 2022 16:11:00 -0500 Subject: [PATCH 099/340] Don't disable minimizeJar in internal modules MSHADE-366 was fixed in maven-shade-plugin 3.4.1. This is technically unnecessary either way as the only things currently being shaded are all our own modules. --- internal/v1_18_R2/pom.xml | 4 ---- internal/v1_19_R1/pom.xml | 4 ---- internal/v1_19_R2/pom.xml | 4 ---- 3 files changed, 12 deletions(-) diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index f99be39f..2460477d 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -67,10 +67,6 @@ maven-shade-plugin - - - false - maven-compiler-plugin diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index d0ebc5e9..22941353 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -67,10 +67,6 @@ maven-shade-plugin - - - false - maven-compiler-plugin diff --git a/internal/v1_19_R2/pom.xml b/internal/v1_19_R2/pom.xml index 3e96c742..cdfd0e6a 100644 --- a/internal/v1_19_R2/pom.xml +++ b/internal/v1_19_R2/pom.xml @@ -67,10 +67,6 @@ maven-shade-plugin - - - false - maven-compiler-plugin From 86bf24e7afaf298210eb83e003b5065458396cbe Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 11 Dec 2022 11:35:21 -0500 Subject: [PATCH 100/340] Bump version to 4.2.2 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- internal/v1_19_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 540de2ae..bbe290bb 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.2-SNAPSHOT + 4.2.2 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index fb22eb6e..e4321a89 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.2-SNAPSHOT + 4.2.2 openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 2460477d..2515cd08 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.2-SNAPSHOT + 4.2.2 openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 22941353..a735e752 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.2-SNAPSHOT + 4.2.2 openinvadapter1_19_R1 diff --git a/internal/v1_19_R2/pom.xml b/internal/v1_19_R2/pom.xml index cdfd0e6a..9441856e 100644 --- a/internal/v1_19_R2/pom.xml +++ b/internal/v1_19_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.2-SNAPSHOT + 4.2.2 openinvadapter1_19_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index 43948c40..e8a92fbf 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.2-SNAPSHOT + 4.2.2 openinvplugincore diff --git a/pom.xml b/pom.xml index 8eef7ca0..ea063f66 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.2-SNAPSHOT + 4.2.2 pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.2.2-SNAPSHOT + 4.2.2 openinvplugincore com.lishid compile - 4.2.2-SNAPSHOT + 4.2.2 com.lishid From 4816737b903bdec2dd22db45902c440c273e1610 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 11 Dec 2022 11:35:36 -0500 Subject: [PATCH 101/340] Bump version to 4.2.3-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- internal/v1_19_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index bbe290bb..38684ddc 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.2 + 4.2.3-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index e4321a89..c816a3e9 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.2 + 4.2.3-SNAPSHOT openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index 2515cd08..deec3627 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.2 + 4.2.3-SNAPSHOT openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index a735e752..3d2ae4fe 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.2 + 4.2.3-SNAPSHOT openinvadapter1_19_R1 diff --git a/internal/v1_19_R2/pom.xml b/internal/v1_19_R2/pom.xml index 9441856e..3a49ce89 100644 --- a/internal/v1_19_R2/pom.xml +++ b/internal/v1_19_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.2 + 4.2.3-SNAPSHOT openinvadapter1_19_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index e8a92fbf..8b600c31 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.2 + 4.2.3-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index ea063f66..49ad657d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.2 + 4.2.3-SNAPSHOT pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.2.2 + 4.2.3-SNAPSHOT openinvplugincore com.lishid compile - 4.2.2 + 4.2.3-SNAPSHOT com.lishid From e39f092f14c15767c2d49bb4214c8aba6e5010e5 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 20 Dec 2022 11:54:12 -0500 Subject: [PATCH 102/340] Add lookup cache, option to open self with no args (#115) Add a cache for the last 10 offline lookups done by OpenInv#matchPlayer(String) to reduce the performance hit of repeat opens Add config option to open self with no-arg command --- api/pom.xml | 2 +- .../java/com/lishid/openinv/IOpenInv.java | 80 ++-------- assembly/pom.xml | 2 +- internal/v1_18_R2/pom.xml | 2 +- internal/v1_19_R1/pom.xml | 2 +- internal/v1_19_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- .../main/java/com/lishid/openinv/OpenInv.java | 140 ++++++++++++++++++ .../openinv/commands/OpenInvCommand.java | 24 +-- .../lishid/openinv/util/ConfigUpdater.java | 10 ++ plugin/src/main/resources/config.yml | 5 +- pom.xml | 8 +- 12 files changed, 189 insertions(+), 90 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 38684ddc..c85e9b43 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvapi diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index 9909a98b..d71828f9 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -22,9 +22,7 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; -import com.lishid.openinv.util.StringMetric; import java.util.UUID; -import java.util.logging.Level; import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -65,6 +63,15 @@ public interface IOpenInv { */ boolean disableOfflineAccess(); + /** + * Check the configuration value for whether OpenInv uses history for opening commands. If false, OpenInv will use + * the previous parameterized search when no parameters are provided. + * + * @return false unless configured otherwise + * @since 4.3.0 + */ + boolean noArgsOpensSelf(); + /** * Get the active {@link IAnySilentContainer} implementation. * @@ -219,74 +226,7 @@ default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean * @param name the string to match * @return the user with the closest matching name */ - default @Nullable OfflinePlayer matchPlayer(@NotNull String name) { - - // Warn if called on the main thread - if we resort to searching offline players, this may take several seconds. - if (Bukkit.getServer().isPrimaryThread()) { - this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!"); - this.getLogger().warning("This can cause the server to hang, potentially severely."); - this.getLogger().log(Level.WARNING, "Current stack trace", new Throwable("Current stack trace")); - } - - OfflinePlayer player; - - try { - UUID uuid = UUID.fromString(name); - player = Bukkit.getOfflinePlayer(uuid); - // Ensure player is an existing player. - if (player.hasPlayedBefore() || player.isOnline()) { - return player; - } - // Return null otherwise. - return null; - } catch (IllegalArgumentException ignored) { - // Not a UUID - } - - // Exact online match first. - player = Bukkit.getServer().getPlayerExact(name); - - if (player != null) { - return player; - } - - // Exact offline match second - ensure offline access works when matchable users are online. - player = Bukkit.getServer().getOfflinePlayer(name); - - if (player.hasPlayedBefore()) { - return player; - } - - // Inexact online match. - player = Bukkit.getServer().getPlayer(name); - - if (player != null) { - return player; - } - - // Finally, inexact offline match. - float bestMatch = 0; - for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) { - if (offline.getName() == null) { - // Loaded by UUID only, name has never been looked up. - continue; - } - - float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName()); - - if (currentMatch == 1.0F) { - return offline; - } - - if (currentMatch > bestMatch) { - bestMatch = currentMatch; - player = offline; - } - } - - // Only null if no players have played ever, otherwise even the worst match will do. - return player; - } + @Nullable OfflinePlayer matchPlayer(@NotNull String name); /** * @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings. diff --git a/assembly/pom.xml b/assembly/pom.xml index c816a3e9..8bb80118 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvassembly diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_18_R2/pom.xml index deec3627..29b5e585 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_18_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvadapter1_18_R2 diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R1/pom.xml index 3d2ae4fe..df50f144 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvadapter1_19_R1 diff --git a/internal/v1_19_R2/pom.xml b/internal/v1_19_R2/pom.xml index 3a49ce89..1a66835c 100644 --- a/internal/v1_19_R2/pom.xml +++ b/internal/v1_19_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvadapter1_19_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index 8b600c31..d984530f 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvplugincore diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 98b634eb..cfca819b 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -16,6 +16,8 @@ package com.lishid.openinv; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.lishid.openinv.commands.ContainerSettingCommand; import com.lishid.openinv.commands.OpenInvCommand; import com.lishid.openinv.commands.SearchContainerCommand; @@ -28,7 +30,9 @@ import com.lishid.openinv.util.ConfigUpdater; import com.lishid.openinv.util.LanguageManager; import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.StringMetric; import java.util.ArrayList; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -37,6 +41,7 @@ import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.logging.Level; import java.util.stream.Stream; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; @@ -52,6 +57,7 @@ import org.bukkit.inventory.InventoryView; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,6 +68,7 @@ */ public class OpenInv extends JavaPlugin implements IOpenInv { + private final Cache offlineLookUpCache = CacheBuilder.newBuilder().maximumSize(10).build(); private final Map inventories = new ConcurrentHashMap<>(); private final Map enderChests = new ConcurrentHashMap<>(); @@ -190,6 +197,11 @@ public boolean disableOfflineAccess() { return this.getConfig().getBoolean("settings.disable-offline-access", false); } + @Override + public boolean noArgsOpensSelf() { + return this.getConfig().getBoolean("settings.command.open.no-args-opens-self", false); + } + @Override public @NotNull IAnySilentContainer getAnySilentContainer() { return this.accessor.getAnySilentContainer(); @@ -311,6 +323,95 @@ public boolean isPlayerLoaded(@NotNull UUID playerUuid) { return player; } + @Override + public @Nullable OfflinePlayer matchPlayer(@NotNull String name) { + + // Warn if called on the main thread - if we resort to searching offline players, this may take several seconds. + if (Bukkit.getServer().isPrimaryThread()) { + this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!"); + this.getLogger().warning("This can cause the server to hang, potentially severely."); + this.getLogger().log(Level.WARNING, "Current stack trace", new Throwable("Current stack trace")); + } + + OfflinePlayer player; + + try { + UUID uuid = UUID.fromString(name); + player = Bukkit.getOfflinePlayer(uuid); + // Ensure player is an existing player. + if (player.hasPlayedBefore() || player.isOnline()) { + return player; + } + // Return null otherwise. + return null; + } catch (IllegalArgumentException ignored) { + // Not a UUID + } + + // Exact online match first. + player = Bukkit.getServer().getPlayerExact(name); + + if (player != null) { + return player; + } + + // Cached offline match. + PlayerProfile cachedResult = offlineLookUpCache.getIfPresent(name); + if (cachedResult != null && cachedResult.getUniqueId() != null) { + player = Bukkit.getOfflinePlayer(cachedResult.getUniqueId()); + // Ensure player is an existing player. + if (player.hasPlayedBefore() || player.isOnline()) { + return player; + } + // Return null otherwise. + return null; + } + + // Exact offline match second - ensure offline access works when matchable users are online. + player = Bukkit.getServer().getOfflinePlayer(name); + + if (player.hasPlayedBefore()) { + offlineLookUpCache.put(name, player.getPlayerProfile()); + return player; + } + + // Inexact online match. + player = Bukkit.getServer().getPlayer(name); + + if (player != null) { + return player; + } + + // Finally, inexact offline match. + float bestMatch = 0; + for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) { + if (offline.getName() == null) { + // Loaded by UUID only, name has never been looked up. + continue; + } + + float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName()); + + if (currentMatch == 1.0F) { + return offline; + } + + if (currentMatch > bestMatch) { + bestMatch = currentMatch; + player = offline; + } + } + + if (player != null) { + // If a match was found, store it. + offlineLookUpCache.put(name, player.getPlayerProfile()); + return player; + } + + // No players have ever joined the server. + return null; + } + @Override public void unload(@NotNull final OfflinePlayer offline) { setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE); @@ -492,6 +593,45 @@ void handleCloseInventory(@NotNull HumanEntity exViewer, @NotNull ISpecialInvent void setPlayerOnline(@NotNull Player player) { setPlayerOnline(inventories, player, player::updateInventory); setPlayerOnline(enderChests, player, null); + + if (player.hasPlayedBefore()) { + return; + } + + // New player may have a name that already points to someone else in lookup cache. + String name = player.getName(); + this.offlineLookUpCache.invalidate(name); + + // If no offline matches are mapped, don't hit scheduler. + if (this.offlineLookUpCache.size() == 0) { + return; + } + + // New player may also be a more exact match than one already in the cache. + // I.e. new player "lava1" is a better match for "lava" than "lava123" + // Player joins are already quite intensive, so this is run on a delay. + this.getServer().getScheduler().runTaskLaterAsynchronously(this, () -> { + Iterator> iterator = this.offlineLookUpCache.asMap().entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String oldMatch = entry.getValue().getName(); + + // Shouldn't be possible - all profiles should be complete. + if (oldMatch == null) { + iterator.remove(); + continue; + } + + String lookup = entry.getKey(); + float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch); + float newMatchScore = StringMetric.compareJaroWinkler(lookup, name); + + // If new match exceeds old match, delete old match. + if (newMatchScore > oldMatchScore) { + iterator.remove(); + } + } + }, 101L); // Odd delay for pseudo load balancing; Player tasks are usually scheduled with full seconds. } private void setPlayerOnline( diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index 5d3b9862..81176979 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -57,19 +57,23 @@ public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Com return true; } - // History management - String history = (openInv ? this.openInvHistory : this.openEnderHistory).get(player); + String noArgValue; + if (plugin.noArgsOpensSelf()) { + noArgValue = player.getUniqueId().toString(); + } else { + // History management + noArgValue = (openInv ? this.openInvHistory : this.openEnderHistory).get(player); - if (history == null || history.isEmpty()) { - history = player.getName(); - (openInv ? this.openInvHistory : this.openEnderHistory).put(player, history); + if (noArgValue == null || noArgValue.isEmpty()) { + noArgValue = player.getUniqueId().toString(); + (openInv ? this.openInvHistory : this.openEnderHistory).put(player, noArgValue); + } } final String name; - // Read from history if target is not named if (args.length < 1) { - name = history; + name = noArgValue; } else { name = args[0]; } @@ -185,8 +189,10 @@ private void openInventory(final Player player, final OfflinePlayer target, bool } } - // Record the target - (openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); + if (!plugin.noArgsOpensSelf()) { + // Record the target + (openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); + } // Create the inventory final ISpecialInventory inv; diff --git a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java index 6f8a595c..6c746135 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java @@ -57,6 +57,9 @@ public void checkForUpdates() { if (version < 5) { updateConfig4To5(); } + if (version < 6) { + updateConfig5To6(); + } plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.saveConfig(); @@ -65,6 +68,13 @@ public void checkForUpdates() { }); } + private void updateConfig5To6() { + plugin.getServer().getScheduler().runTask(plugin, () -> { + plugin.getConfig().set("settings.command.open.no-args-opens-self", false); + plugin.getConfig().set("config-version", 6); + }); + } + private void updateConfig4To5() { plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.getConfig().set("settings.disable-offline-access", false); diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index f0b56f0c..38edd514 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,5 +1,8 @@ -config-version: 5 +config-version: 6 settings: + command: + open: + no-args-opens-self: false disable-offline-access: false disable-saving: false locale: 'en_us' diff --git a/pom.xml b/pom.xml index 49ad657d..a5e856a3 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT pom @@ -78,19 +78,19 @@ spigot-api org.spigotmc provided - 1.17.1-R0.1-SNAPSHOT + 1.18.2-R0.1-SNAPSHOT openinvapi com.lishid compile - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT openinvplugincore com.lishid compile - 4.2.3-SNAPSHOT + 4.3.0-SNAPSHOT com.lishid From 81eb60f628752e8653b07148c45e6f768d13754f Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 20 Dec 2022 15:54:30 -0500 Subject: [PATCH 103/340] Improve language manager (#116) * Clarify replacements internally to reduce the likelihood of messing up when including new ones * Fix missing keys not being written to disk for easy translation * Use a guessfile-like system where missing translation keys are inserted into a separate section for easy identification * Reduce verbosity of missing keys warning --- .../main/java/com/lishid/openinv/OpenInv.java | 7 +- .../commands/ContainerSettingCommand.java | 7 +- .../openinv/commands/OpenInvCommand.java | 13 +- .../commands/SearchContainerCommand.java | 19 +- .../commands/SearchEnchantCommand.java | 15 +- .../openinv/commands/SearchInvCommand.java | 26 ++- .../openinv/internal/OpenInventoryView.java | 4 +- .../lishid/openinv/util/LanguageManager.java | 168 -------------- .../openinv/util/lang/LanguageManager.java | 217 ++++++++++++++++++ .../lishid/openinv/util/lang/Replacement.java | 29 +++ 10 files changed, 310 insertions(+), 195 deletions(-) delete mode 100644 plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java create mode 100644 plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java create mode 100644 plugin/src/main/java/com/lishid/openinv/util/lang/Replacement.java diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index cfca819b..25b28aa1 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -28,9 +28,10 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.ConfigUpdater; -import com.lishid.openinv.util.LanguageManager; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.StringMetric; +import com.lishid.openinv.util.lang.LanguageManager; +import com.lishid.openinv.util.lang.Replacement; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; @@ -463,7 +464,7 @@ int convertToPlayerSlot(InventoryView view, int rawSlot) { public @Nullable String getLocalizedMessage( @NotNull CommandSender sender, @NotNull String key, - String @NotNull ... replacements) { + Replacement @NotNull ... replacements) { return this.languageManager.getValue(key, getLocale(sender), replacements); } @@ -483,7 +484,7 @@ public void sendMessage(@NotNull CommandSender sender, @NotNull String key) { } } - public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String @NotNull... replacements) { + public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) { String message = getLocalizedMessage(sender, key, replacements); if (message != null && !message.isEmpty()) { diff --git a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java index 406b472b..c8dd3c7d 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java @@ -18,6 +18,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.Replacement; import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; @@ -69,7 +70,11 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command onOff = String.valueOf(getSetting.test(player)); } - plugin.sendMessage(sender, "messages.info.settingState","%setting%", any ? "AnyContainer" : "SilentContainer", "%state%", onOff); + plugin.sendMessage( + sender, + "messages.info.settingState", + new Replacement("%setting%", any ? "AnyContainer" : "SilentContainer"), + new Replacement("%state%", onOff)); return true; } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index 81176979..d4f193aa 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -20,6 +20,7 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.Replacement; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -175,16 +176,20 @@ private void openInventory(final Player player, final OfflinePlayer target, bool // Protected check if (!Permissions.OVERRIDE.hasPermission(player) && Permissions.EXEMPT.hasPermission(onlineTarget)) { - plugin.sendMessage(player, "messages.error.permissionExempt", - "%target%", onlineTarget.getDisplayName()); + plugin.sendMessage( + player, + "messages.error.permissionExempt", + new Replacement("%target%", onlineTarget.getDisplayName())); return; } // Crossworld check if (!Permissions.CROSSWORLD.hasPermission(player) && !onlineTarget.getWorld().equals(player.getWorld())) { - plugin.sendMessage(player, "messages.error.permissionCrossWorld", - "%target%", onlineTarget.getDisplayName()); + plugin.sendMessage( + player, + "messages.error.permissionCrossWorld", + new Replacement("%target%", onlineTarget.getDisplayName())); return; } } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java index 37e7c53d..bef78bb6 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java @@ -18,6 +18,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.Replacement; import java.util.Collections; import java.util.List; import org.bukkit.Chunk; @@ -57,7 +58,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Material material = Material.getMaterial(args[0].toUpperCase()); if (material == null) { - plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args[0]); + plugin.sendMessage( + sender, + "messages.error.invalidMaterial", + new Replacement("%target%", args[0])); return false; } @@ -100,13 +104,18 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (locations.length() > 0) { locations.delete(locations.length() - 2, locations.length()); } else { - plugin.sendMessage(sender, "messages.info.container.noMatches", - "%target%", material.name()); + plugin.sendMessage( + sender, + "messages.info.container.noMatches", + new Replacement("%target%", material.name())); return true; } - plugin.sendMessage(sender, "messages.info.container.matches", - "%target%", material.name(), "%detail%", locations.toString()); + plugin.sendMessage( + sender, + "messages.info.container.matches", + new Replacement("%target%", material.name()), + new Replacement("%detail%", locations.toString())); return true; } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java index c79aa433..2c52f5d9 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java @@ -18,6 +18,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.Replacement; import java.util.Collections; import java.util.List; import org.bukkit.Material; @@ -114,14 +115,18 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command // Matches found, delete trailing comma and space players.delete(players.length() - 2, players.length()); } else { - plugin.sendMessage(sender, "messages.info.player.noMatches", - "%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level); + plugin.sendMessage( + sender, + "messages.info.player.noMatches", + new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level)); return true; } - plugin.sendMessage(sender, "messages.info.player.matches", - "%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level, - "%detail%", players.toString()); + plugin.sendMessage( + sender, + "messages.info.player.matches", + new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level), + new Replacement("%detail%", players.toString())); return true; } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java index 3b2cbfb6..60235bd4 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * Copyright (C) 2011-2022 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.Replacement; import java.util.Collections; import java.util.List; import org.bukkit.Material; @@ -46,7 +47,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } if (material == null) { - plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args.length > 0 ? args[0] : "null"); + plugin.sendMessage( + sender, + "messages.error.invalidMaterial", + new Replacement("%target%", args.length > 0 ? args[0] : "null")); return false; } @@ -56,7 +60,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command try { count = Integer.parseInt(args[1]); } catch (NumberFormatException ex) { - plugin.sendMessage(sender, "messages.error.invalidNumber", "%target%", args[1]); + plugin.sendMessage( + sender, + "messages.error.invalidNumber", + new Replacement("%target%", args[1])); return false; } } @@ -74,13 +81,18 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (players.length() > 0) { players.delete(players.length() - 2, players.length()); } else { - plugin.sendMessage(sender, "messages.info.player.noMatches", - "%target%", material.name()); + plugin.sendMessage( + sender, + "messages.info.player.noMatches", + new Replacement("%target%", material.name())); return true; } - plugin.sendMessage(sender, "messages.info.player.matches", - "%target%", material.name(), "%detail%", players.toString()); + plugin.sendMessage( + sender, + "messages.info.player.matches", + new Replacement("%target%", material.name()), + new Replacement("%detail%", players.toString())); return true; } diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index a7f4a958..ea7c438e 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -17,6 +17,7 @@ package com.lishid.openinv.internal; import com.lishid.openinv.OpenInv; +import com.lishid.openinv.util.lang.Replacement; import java.util.Objects; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; @@ -73,8 +74,7 @@ public OpenInventoryView( .getLocalizedMessage( player, titleKey, - "%player%", - owner.getName()); + new Replacement("%player%", owner.getName())); title = Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix); } diff --git a/plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java b/plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java deleted file mode 100644 index ff526b11..00000000 --- a/plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.util; - -import com.lishid.openinv.OpenInv; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.bukkit.ChatColor; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * A simple language manager supporting both custom and bundled languages. - * - * @author Jikoo - */ -public class LanguageManager { - - private final OpenInv plugin; - private final String defaultLocale; - private final Map locales; - - public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) { - this.plugin = plugin; - this.defaultLocale = defaultLocale; - this.locales = new HashMap<>(); - getOrLoadLocale(defaultLocale); - } - - private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) { - YamlConfiguration loaded = locales.get(locale); - if (loaded != null) { - return loaded; - } - - InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml"); - YamlConfiguration localeConfigDefaults; - if (resourceStream == null) { - localeConfigDefaults = new YamlConfiguration(); - } else { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) { - localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to load resource " + locale + ".yml", e); - localeConfigDefaults = new YamlConfiguration(); - } - } - - File file = new File(plugin.getDataFolder(), locale + ".yml"); - YamlConfiguration localeConfig; - - if (!file.exists()) { - localeConfig = localeConfigDefaults; - try { - localeConfigDefaults.save(file); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e); - } - } else { - localeConfig = YamlConfiguration.loadConfiguration(file); - - // Add new language keys - List newKeys = new ArrayList<>(); - for (String key : localeConfigDefaults.getKeys(true)) { - if (localeConfigDefaults.isConfigurationSection(key)) { - continue; - } - - if (localeConfig.isSet(key)) { - continue; - } - - localeConfig.set(key, localeConfigDefaults.get(key)); - newKeys.add(key); - } - - if (!newKeys.isEmpty()) { - plugin.getLogger().info("[LanguageManager] Added new language keys: " + String.join(", ", newKeys)); - try { - localeConfig.save(file); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e); - } - } - } - - if (!locale.equals(defaultLocale)) { - localeConfigDefaults = locales.get(defaultLocale); - - // Check for missing keys - List newKeys = new ArrayList<>(); - for (String key : localeConfigDefaults.getKeys(true)) { - if (localeConfigDefaults.isConfigurationSection(key)) { - continue; - } - - if (localeConfig.isSet(key)) { - continue; - } - - newKeys.add(key); - } - - if (!newKeys.isEmpty()) { - plugin.getLogger().info("[LanguageManager] Missing translations from " + locale + ".yml: " + String.join(", ", newKeys)); - } - - // Fall through to default locale - localeConfig.setDefaults(localeConfigDefaults); - } - - locales.put(locale, localeConfig); - return localeConfig; - } - - public @Nullable String getValue(@NotNull String key, @Nullable String locale) { - String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key); - if (value == null || value.isEmpty()) { - return null; - } - - value = ChatColor.translateAlternateColorCodes('&', value); - - return value; - } - - public @Nullable String getValue(@NotNull String key, @Nullable String locale, String @NotNull ... replacements) { - if (replacements.length % 2 != 0) { - plugin.getLogger().log(Level.WARNING, "[LanguageManager] Replacement data is uneven", new Exception()); - } - - String value = getValue(key, locale); - - if (value == null) { - return null; - } - - for (int i = 0; i < replacements.length; i += 2) { - value = value.replace(replacements[i], replacements[i + 1]); - } - - return value; - } - -} diff --git a/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java new file mode 100644 index 00000000..239031e0 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.util.lang; + +import com.lishid.openinv.OpenInv; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.logging.Level; +import org.bukkit.ChatColor; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A simple language manager supporting both custom and bundled languages. + * + * @author Jikoo + */ +public class LanguageManager { + + private final OpenInv plugin; + private final String defaultLocale; + private final Map locales; + + public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) { + this.plugin = plugin; + this.defaultLocale = defaultLocale; + this.locales = new HashMap<>(); + getOrLoadLocale(defaultLocale); + } + + private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) { + YamlConfiguration loaded = locales.get(locale); + if (loaded != null) { + return loaded; + } + + File file = new File(plugin.getDataFolder(), locale + ".yml"); + + // Load locale config from disk and bundled locale defaults. + YamlConfiguration localeConfig = loadLocale(locale, file); + + // If the locale is not the default locale, also handle any missing translations from the default locale. + if (!locale.equals(defaultLocale)) { + addTranslationFallthrough(locale, localeConfig, file); + + if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true) + && localeConfig.isConfigurationSection("guess")) { + // Warn that guess section exists. This should run once per language per server restart + // when accessed by a user to hint to server owners that they can make UX improvements. + plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + locale + ".yml! Check the guess section!"); + } + } + + locales.put(locale, localeConfig); + return localeConfig; + } + + private @NotNull YamlConfiguration loadLocale( + @NotNull String locale, + @NotNull File file) { + // Load defaults from the plugin's bundled resources. + InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml"); + YamlConfiguration localeConfigDefaults; + if (resourceStream == null) { + localeConfigDefaults = new YamlConfiguration(); + } else { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) { + localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + locale + ".yml"); + localeConfigDefaults = new YamlConfiguration(); + } + } + + if (!file.exists()) { + // If the file does not exist on disk, save bundled defaults. + try { + localeConfigDefaults.save(file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml"); + } + // Return loaded bundled locale. + return localeConfigDefaults; + } + + // If the file does exist on disk, load it. + YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(file); + // Check for missing translations from the bundled file. + List newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet); + + if (newKeys.isEmpty()) { + return localeConfig; + } + + // Get guess section for missing keys. + ConfigurationSection guess = localeConfig.getConfigurationSection("guess"); + + for (String newKey : newKeys) { + // Set all missing keys to defaults. + localeConfig.set(newKey, localeConfigDefaults.get(newKey)); + + // Delete relevant guess keys in case this is a new translation. + if (guess != null) { + guess.set(newKey, null); + } + } + + // If guess section is empty, delete it. + if (guess != null && guess.getKeys(false).isEmpty()) { + localeConfig.set("guess", null); + } + + plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + locale + ".yml: " + String.join(", ", newKeys)); + + // Write new keys to disk. + try { + localeConfig.save(file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml"); + } + + return localeConfig; + } + + private void addTranslationFallthrough( + @NotNull String locale, + @NotNull YamlConfiguration localeConfig, + @NotNull File file) { + YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale); + + // Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write. + List missingKeys = getMissingKeys( + defaultLocaleConfig, + key -> localeConfig.isSet(key) || localeConfig.isSet("guess." + key)); + + if (!missingKeys.isEmpty()) { + // Set up guess section for missing keys. + for (String key : missingKeys) { + localeConfig.set("guess." + key, defaultLocaleConfig.get(key)); + } + + // Write modified guess section to disk. + try { + localeConfig.save(file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml"); + } + } + + // Fall through to default locale. + localeConfig.setDefaults(defaultLocaleConfig); + } + + private @NotNull List getMissingKeys( + @NotNull Configuration configurationDefault, + @NotNull Predicate nodeSetPredicate) { + List missingKeys = new ArrayList<>(); + for (String key : configurationDefault.getKeys(true)) { + if (!configurationDefault.isConfigurationSection(key) && !nodeSetPredicate.test(key)) { + // Missing keys are non-section keys that fail the predicate. + missingKeys.add(key); + } + } + return missingKeys; + } + + public @Nullable String getValue(@NotNull String key, @Nullable String locale) { + String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key); + if (value == null || value.isEmpty()) { + return null; + } + + value = ChatColor.translateAlternateColorCodes('&', value); + + return value; + } + + public @Nullable String getValue(@NotNull String key, @Nullable String locale, Replacement @NotNull ... replacements) { + String value = getValue(key, locale); + + if (value == null) { + return null; + } + + for (Replacement replacement : replacements) { + value = value.replace(replacement.placeholder(), replacement.value()); + } + + return value; + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/lang/Replacement.java b/plugin/src/main/java/com/lishid/openinv/util/lang/Replacement.java new file mode 100644 index 00000000..6753044c --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/lang/Replacement.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.util.lang; + +import org.jetbrains.annotations.NotNull; + +/** + * A data holder for string replacement in translations. + * + * @param placeholder the placeholder to be replaced + * @param value the value to insert + */ +public record Replacement(@NotNull String placeholder, @NotNull String value) { + +} From 79b2a97c9e84a7b3fe19e166a54b88967452f59d Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 20 Dec 2022 16:44:49 -0500 Subject: [PATCH 104/340] Clamp /searchcontainer radius (#117) Default max is 10 because that's the default server view distance. --- .../com/lishid/openinv/commands/SearchContainerCommand.java | 4 ++++ .../src/main/java/com/lishid/openinv/util/ConfigUpdater.java | 1 + plugin/src/main/resources/config.yml | 2 ++ 3 files changed, 7 insertions(+) diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java index bef78bb6..161dfedf 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java @@ -76,6 +76,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } } + // Clamp radius. + int configMax = plugin.getConfig().getInt("settings.command.searchcontainer.max-radius", 10); + radius = Math.max(0, Math.min(radius, configMax)); + World world = senderPlayer.getWorld(); Chunk centerChunk = senderPlayer.getLocation().getChunk(); StringBuilder locations = new StringBuilder(); diff --git a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java index 6c746135..a657f849 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java @@ -71,6 +71,7 @@ public void checkForUpdates() { private void updateConfig5To6() { plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.getConfig().set("settings.command.open.no-args-opens-self", false); + plugin.getConfig().set("settings.command.searchcontainer.max-radius", 10); plugin.getConfig().set("config-version", 6); }); } diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 38edd514..6a68c9cb 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -3,6 +3,8 @@ settings: command: open: no-args-opens-self: false + searchcontainer: + max-radius: 10 disable-offline-access: false disable-saving: false locale: 'en_us' From dc8e36d11fc10eb30880d5ec5cd83e80337b04c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jan 2023 19:13:14 -0500 Subject: [PATCH 105/340] Bump annotations from 23.0.0 to 23.1.0 (#121) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a5e856a3..8568e4a8 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ annotations org.jetbrains provided - 23.0.0 + 23.1.0 spigot-api From 767476a7590471a9b6192a30b55cf92a44b27387 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:01:06 -0500 Subject: [PATCH 106/340] Bump annotations from 23.1.0 to 24.0.0 (#122) Bumps [annotations](https://github.com/JetBrains/java-annotations) from 23.1.0 to 24.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/23.1.0...24.0.0) --- updated-dependencies: - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8568e4a8..3d9cfb75 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ annotations org.jetbrains provided - 23.1.0 + 24.0.0 spigot-api From 2fe0322570cfa61f5013fdc7e86ff021544c3d8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:01:16 -0500 Subject: [PATCH 107/340] Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#123) Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.4.0 to 3.5.0. - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.4.0...maven-dependency-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3d9cfb75..7a303a3f 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ --> maven-dependency-plugin org.apache.maven.plugins - 3.4.0 + 3.5.0 From bd5e47760e0f10b447cd34f8fb6d19e069e035c7 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 18 Feb 2023 14:10:36 -0500 Subject: [PATCH 108/340] Fix duplicate closes swapping players to spectate (#125) --- .../openinv/internal/IAnySilentContainer.java | 24 ++++++++++++------- .../internal/v1_18_R2/AnySilentContainer.java | 17 +++---------- .../internal/v1_19_R1/AnySilentContainer.java | 17 +++---------- .../internal/v1_19_R2/AnySilentContainer.java | 17 +++---------- .../com/lishid/openinv/InventoryListener.java | 7 ++++-- 5 files changed, 29 insertions(+), 53 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 8c595300..f87ac6b1 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,6 @@ package com.lishid.openinv.internal; -import org.bukkit.Material; import org.bukkit.block.Barrel; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -148,13 +147,20 @@ default boolean isChestBlocked(@NotNull Block chest) { * @return true if the type is a supported container */ default boolean isAnySilentContainer(@NotNull Block block) { - if (block.getType() == Material.ENDER_CHEST) { - return true; - } - BlockState state = block.getState(); - return state instanceof org.bukkit.block.Chest - || state instanceof org.bukkit.block.ShulkerBox - || state instanceof org.bukkit.block.Barrel; + return isAnySilentContainer(block.getState()); + } + + /** + * Check if the given {@link BlockState} is a container which can be unblocked or silenced. + * + * @param blockState the potential container + * @return true if the type is a supported container + */ + default boolean isAnySilentContainer(@NotNull BlockState blockState) { + return blockState instanceof org.bukkit.block.EnderChest + || blockState instanceof org.bukkit.block.Chest + || blockState instanceof org.bukkit.block.ShulkerBox + || blockState instanceof org.bukkit.block.Barrel; } } diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java index 7fae7c5d..77aac17c 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/AnySilentContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,12 +50,12 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.block.ShulkerBox; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -231,21 +231,10 @@ public Optional acceptNone() { @Override public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { return; } - InventoryView view = bukkitPlayer.getOpenInventory(); - switch (view.getType()) { - case CHEST: - case ENDER_CHEST: - case SHULKER_BOX: - case BARREL: - break; - default: - return; - } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. diff --git a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java index df537467..9d4be846 100644 --- a/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java +++ b/internal/v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1/AnySilentContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,12 +50,12 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.block.ShulkerBox; import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -231,21 +231,10 @@ public Optional acceptNone() { @Override public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { return; } - InventoryView view = bukkitPlayer.getOpenInventory(); - switch (view.getType()) { - case CHEST: - case ENDER_CHEST: - case SHULKER_BOX: - case BARREL: - break; - default: - return; - } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java index 3abd77ac..37f13257 100644 --- a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,12 +50,12 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.block.ShulkerBox; import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -227,21 +227,10 @@ public Optional acceptNone() { @Override public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { return; } - InventoryView view = bukkitPlayer.getOpenInventory(); - switch (view.getType()) { - case CHEST: - case ENDER_CHEST: - case SHULKER_BOX: - case BARREL: - break; - default: - return; - } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index 61a08a48..cf595826 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ import java.util.Set; import java.util.stream.Collectors; import org.bukkit.GameMode; +import org.bukkit.block.Container; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -53,7 +54,9 @@ private void onInventoryClose(@NotNull final InventoryCloseEvent event) { return; } - if (this.plugin.getSilentContainerStatus(player)) { + if (this.plugin.getSilentContainerStatus(player) + && event.getInventory().getHolder() instanceof Container container + && this.plugin.getAnySilentContainer().isAnySilentContainer(container)) { this.plugin.getAnySilentContainer().deactivateContainer(player); } From c1ffd09d50a19333366302357aa8834a398124e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 18:20:31 -0500 Subject: [PATCH 109/340] Bump pascalgn/automerge-action from 0.15.5 to 0.15.6 (#126) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3005d7a0..e872fd02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Merge - uses: pascalgn/automerge-action@v0.15.5 + uses: pascalgn/automerge-action@v0.15.6 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "dependencies" From e15c566b82676e9fc5a689befac4b0d49069db54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 18:20:50 -0500 Subject: [PATCH 110/340] Bump maven-compiler-plugin from 3.10.1 to 3.11.0 (#128) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7a303a3f..fbde3d2a 100644 --- a/pom.xml +++ b/pom.xml @@ -150,7 +150,7 @@ maven-compiler-plugin org.apache.maven.plugins - 3.10.1 + 3.11.0 From e885bf9a65d357d6ebc5de7024c2449a9a6fe83d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 18:21:01 -0500 Subject: [PATCH 111/340] Bump maven-assembly-plugin from 3.4.2 to 3.5.0 (#127) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fbde3d2a..4bad5108 100644 --- a/pom.xml +++ b/pom.xml @@ -156,7 +156,7 @@ maven-assembly-plugin org.apache.maven.plugins - 3.4.2 + 3.5.0 From 3d4bed04d5816b58857f3260b8c775a326fb3a51 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Mar 2023 08:13:33 -0400 Subject: [PATCH 112/340] Split up actions (#129) * Refactor conditional dependent jobs into separate actions * Fix incorrect usage of pull_request_target * Fix Dependabot not merging due to labels being absent when issue is created * Prettify supported version listing --- .github/workflows/automerge_dependabot.yml | 26 +++++++++ .github/workflows/ci.yml | 53 +------------------ .github/workflows/draft_release.yml | 44 +++++++++++++++ .../{release.yml => external_release.yml} | 0 scripts/generate_changelog.sh | 4 +- 5 files changed, 73 insertions(+), 54 deletions(-) create mode 100644 .github/workflows/automerge_dependabot.yml create mode 100644 .github/workflows/draft_release.yml rename .github/workflows/{release.yml => external_release.yml} (100%) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml new file mode 100644 index 00000000..94083be6 --- /dev/null +++ b/.github/workflows/automerge_dependabot.yml @@ -0,0 +1,26 @@ +name: Auto-merge Dependabot PRs + +on: + workflow_run: + workflows: [ "OpenInv CI" ] + types: [ completed ] + +jobs: + merge-dependabot: + if: "github.actor == 'dependabot[bot]' + && github.event.workflow_run.event == 'pull_request' + && github.event.workflow_run.conclusion == 'success'" + runs-on: ubuntu-latest + steps: + - name: Approve + uses: hmarr/auto-approve-action@v3.1.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + pull-request-number: "${{ github.event.workflow_run.event.pull_request.id }}" + - name: Merge + uses: pascalgn/automerge-action@v0.15.6 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_LABELS: "dependencies,java" + MERGE_METHOD: "squash" + PULL_REQUEST: "${{ github.event.workflow_run.event.pull_request.id }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e872fd02..62cbfa27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: OpenInv CI on: push: - pull_request_target: + pull_request: jobs: build: @@ -36,54 +36,3 @@ jobs: with: name: api path: ./api/target/openinvapi*.jar - - merge-dependabot: - name: Auto-merge Dependabot PRs - needs: [ build ] - if: "github.event.name == 'pull_request_target' - && github.actor == 'dependabot[bot]' - && contains( github.event.pull_request.labels.*.name, 'java')" - runs-on: ubuntu-latest - steps: - - name: Approve - uses: hmarr/auto-approve-action@v3.1.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Merge - uses: pascalgn/automerge-action@v0.15.6 - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - MERGE_LABELS: "dependencies" - MERGE_METHOD: "squash" - - release: - name: Create Github Release - needs: [ build ] - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - # Fetch all history - used to assemble changelog. - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set Release Variables - run: bash ./scripts/set_release_env.sh - - - name: Download Artifact - uses: actions/download-artifact@v3 - with: - name: dist - path: dist - - - name: Create Release - id: create-release - uses: softprops/action-gh-release@v0.1.15 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - name: ${{ env.VERSIONED_NAME }} - body: ${{ env.GENERATED_CHANGELOG }} - draft: true - prerelease: false - files: ./dist/OpenInv.jar diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml new file mode 100644 index 00000000..a3ffdcd6 --- /dev/null +++ b/.github/workflows/draft_release.yml @@ -0,0 +1,44 @@ +name: Draft Github Release + +on: + workflow_run: + workflows: [ "OpenInv CI" ] + types: [ completed ] + +jobs: + draft_release: + if: "github.event.workflow_run.event == 'push' + && github.event.workflow_run.conclusion == 'success' + && startsWith(github.event.workflow_run.event.push.ref, 'refs/tags/')" + runs-on: ubuntu-latest + steps: + # Fetch all history - used to assemble changelog. + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set Release Variables + run: bash ./scripts/set_release_env.sh + + - name: Download Artifact + # Unfortunately actions/download-artifact cannot fetch from other workflow runs. + uses: dawidd6/action-download-artifact@v2.26.0 + with: + name: dist + path: dist + run_id: "${{ github.event.workflow_run.id }}" + run_number: "${{ github.event.workflow_run.run_number }}" + # Searching for a specific run ID that we know was successful, unset 'success' default. + workflow_conclusion: "" + + - name: Create Release + id: create-release + uses: softprops/action-gh-release@v0.1.15 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: ${{ env.VERSIONED_NAME }} + body: ${{ env.GENERATED_CHANGELOG }} + draft: true + prerelease: false + files: ./dist/OpenInv.jar diff --git a/.github/workflows/release.yml b/.github/workflows/external_release.yml similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows/external_release.yml diff --git a/scripts/generate_changelog.sh b/scripts/generate_changelog.sh index d2261f18..bc234b9d 100644 --- a/scripts/generate_changelog.sh +++ b/scripts/generate_changelog.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (C) 2011-2021 lishid. All rights reserved. +# Copyright (C) 2011-2023 lishid. All rights reserved. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ function get_minecraft_versions() { for version in "${versions[@]}"; do # Append comma if variable is set, then append version - minecraft_versions="${minecraft_versions:+${minecraft_versions},}${version%%-R*}" + minecraft_versions="${minecraft_versions:+${minecraft_versions}, }${version%%-R*}" done echo "${minecraft_versions}" From 0e6acdff362bc0f4f097628165ffd6b7360b8216 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 16 Mar 2023 16:30:43 -0400 Subject: [PATCH 113/340] Update to 1.19.4 (#130) * Improve data load/save slightly * Fix longstanding issue with opening players in deleted worlds on Paper --- .../openinv/internal/v1_18_R2/OpenPlayer.java | 7 ++-- .../internal/v1_18_R2/PlayerDataManager.java | 28 +++++++++---- .../openinv/internal/v1_19_R2/OpenPlayer.java | 4 +- .../internal/v1_19_R2/PlayerDataManager.java | 31 ++++++++++---- internal/{v1_19_R1 => v1_19_R3}/pom.xml | 8 ++-- .../v1_19_R3}/AnySilentContainer.java | 6 +-- .../internal/v1_19_R3}/OpenPlayer.java | 12 +++--- .../internal/v1_19_R3}/PlayerDataManager.java | 40 ++++++++++++------- .../internal/v1_19_R3}/SpecialEnderChest.java | 8 ++-- .../v1_19_R3}/SpecialPlayerInventory.java | 11 ++--- .../com/lishid/openinv/InternalAccessor.java | 3 +- pom.xml | 4 +- 12 files changed, 100 insertions(+), 62 deletions(-) rename internal/{v1_19_R1 => v1_19_R3}/pom.xml (92%) rename internal/{v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1 => v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3}/AnySilentContainer.java (97%) rename internal/{v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1 => v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3}/OpenPlayer.java (89%) rename internal/{v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1 => v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3}/PlayerDataManager.java (88%) rename internal/{v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1 => v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3}/SpecialEnderChest.java (97%) rename internal/{v1_19_R1/src/main/java/com/lishid/openinv/internal/v1_19_R1 => v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3}/SpecialPlayerInventory.java (98%) diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java index 4fdcfc00..10929ebc 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ public void loadData() { // See CraftPlayer#loadData CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); if (loaded != null) { - readExtraData(loaded); + getHandle().readAdditionalSaveData(loaded); } } @@ -49,7 +49,6 @@ public void saveData() { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; CompoundTag playerData = player.saveWithoutId(new CompoundTag()); - setExtraData(playerData); if (!isOnline()) { // Special case: save old vehicle data @@ -67,7 +66,7 @@ public void saveData() { File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); Util.safeReplaceFile(file1, file, file2); } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage()); + LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } } diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java index 127e39c1..ae02e79a 100644 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java +++ b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ import com.mojang.authlib.GameProfile; import java.lang.reflect.Field; import java.util.logging.Logger; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -108,14 +109,25 @@ public PlayerDataManager() { e.printStackTrace(); } - // Get the bukkit entity - Player target = entity.getBukkitEntity(); - if (target != null) { - // Load data - target.loadData(); + // Load data. This also reads basic data into the player. + // See CraftPlayer#loadData + CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); + + if (loadedData == null) { + // Exceptions with loading are logged by Mojang. + return null; + } + + // Also read "extra" data. + entity.readAdditionalSaveData(loadedData); + + if (entity.level == null) { + // Paper: Move player to spawn + entity.spawnIn(null); } - // Return the entity - return target; + + // Return the Bukkit entity. + return entity.getBukkitEntity(); } void injectPlayer(ServerPlayer player) throws IllegalAccessException { diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java index 4dab3343..721c7fc9 100644 --- a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/OpenPlayer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ public void loadData() { // See CraftPlayer#loadData CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); if (loaded != null) { - readExtraData(loaded); + getHandle().readAdditionalSaveData(loaded); } } diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java index 4cc6939b..423411a4 100644 --- a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java +++ b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/PlayerDataManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ import com.mojang.authlib.GameProfile; import java.lang.reflect.Field; import java.util.logging.Logger; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -100,20 +101,34 @@ public PlayerDataManager() { ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + try { injectPlayer(entity); } catch (IllegalAccessException e) { e.printStackTrace(); } - // Get the bukkit entity - Player target = entity.getBukkitEntity(); - if (target != null) { - // Load data - target.loadData(); + // Load data. This also reads basic data into the player. + // See CraftPlayer#loadData + CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); + + if (loadedData == null) { + // Exceptions with loading are logged by Mojang. + return null; } - // Return the entity - return target; + + // Also read "extra" data. + entity.readAdditionalSaveData(loadedData); + + if (entity.level == null) { + // Paper: Move player to spawn + entity.spawnIn(null); + } + + // Return the Bukkit entity. + return entity.getBukkitEntity(); } void injectPlayer(ServerPlayer player) throws IllegalAccessException { diff --git a/internal/v1_19_R1/pom.xml b/internal/v1_19_R3/pom.xml similarity index 92% rename from internal/v1_19_R1/pom.xml rename to internal/v1_19_R3/pom.xml index df50f144..66769d9a 100644 --- a/internal/v1_19_R1/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -1,6 +1,6 @@ maven-dependency-plugin org.apache.maven.plugins - 3.5.0 + 3.6.0 From cc7481fa9ab750c2d40975a39fcc97bf3e6dd862 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 10 Jun 2023 11:06:41 -0400 Subject: [PATCH 124/340] Update to Minecraft 1.20 (#148) --- .../java/com/lishid/openinv/IOpenInv.java | 3 +- .../openinv/internal/IAnySilentContainer.java | 20 +- .../openinv/internal/v1_18_R2/OpenPlayer.java | 73 -- .../internal/v1_18_R2/PlayerDataManager.java | 263 ------ internal/v1_19_R2/pom.xml | 81 -- .../internal/v1_19_R2/AnySilentContainer.java | 266 ------ .../internal/v1_19_R2/SpecialEnderChest.java | 339 -------- .../v1_19_R2/SpecialPlayerInventory.java | 788 ------------------ .../internal/v1_19_R3/AnySilentContainer.java | 30 +- internal/{v1_18_R2 => v1_20_R1}/pom.xml | 8 +- .../v1_20_R1}/AnySilentContainer.java | 47 +- .../internal/v1_20_R1}/OpenPlayer.java | 10 +- .../internal/v1_20_R1}/PlayerDataManager.java | 29 +- .../internal/v1_20_R1}/SpecialEnderChest.java | 8 +- .../v1_20_R1}/SpecialPlayerInventory.java | 15 +- .../com/lishid/openinv/InternalAccessor.java | 1 + .../com/lishid/openinv/InventoryListener.java | 7 +- pom.xml | 4 +- 18 files changed, 66 insertions(+), 1926 deletions(-) delete mode 100644 internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java delete mode 100644 internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java delete mode 100644 internal/v1_19_R2/pom.xml delete mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java delete mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java delete mode 100644 internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java rename internal/{v1_18_R2 => v1_20_R1}/pom.xml (92%) rename internal/{v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2 => v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1}/AnySilentContainer.java (82%) rename internal/{v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2 => v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1}/OpenPlayer.java (89%) rename internal/{v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2 => v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1}/PlayerDataManager.java (87%) rename internal/{v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2 => v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1}/SpecialEnderChest.java (97%) rename internal/{v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2 => v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1}/SpecialPlayerInventory.java (98%) diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index d71828f9..d1e0155f 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2022 lishid. All rights reserved. + * Copyright (C) 2011-2023 lishid. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -161,6 +161,7 @@ default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean * @return the identifier * @throws IllegalStateException if the server version is unsupported */ + @Deprecated(forRemoval = true) default @NotNull String getPlayerID(@NotNull OfflinePlayer offline) { return offline.getUniqueId().toString(); } diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index f87ac6b1..82b629f0 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -26,6 +26,7 @@ import org.bukkit.block.data.type.Chest; import org.bukkit.entity.Cat; import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; @@ -157,10 +158,21 @@ default boolean isAnySilentContainer(@NotNull Block block) { * @return true if the type is a supported container */ default boolean isAnySilentContainer(@NotNull BlockState blockState) { - return blockState instanceof org.bukkit.block.EnderChest - || blockState instanceof org.bukkit.block.Chest - || blockState instanceof org.bukkit.block.ShulkerBox - || blockState instanceof org.bukkit.block.Barrel; + return blockState instanceof InventoryHolder holder && isAnySilentContainer(holder); + } + + /** + * Check if the given {@link InventoryHolder} is a container which can be unblocked or silenced. + * + * @param holder the potential container + * @return true if the type is a supported container + */ + default boolean isAnySilentContainer(@NotNull InventoryHolder holder) { + return holder instanceof org.bukkit.block.EnderChest + || holder instanceof org.bukkit.block.Chest + || holder instanceof org.bukkit.block.DoubleChest + || holder instanceof org.bukkit.block.ShulkerBox + || holder instanceof org.bukkit.block.Barrel; } } diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java deleted file mode 100644 index 10929ebc..00000000 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/OpenPlayer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_18_R2; - -import java.io.File; -import net.minecraft.Util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.storage.PlayerDataStorage; -import org.apache.logging.log4j.LogManager; -import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; - -public class OpenPlayer extends CraftPlayer { - - public OpenPlayer(CraftServer server, ServerPlayer entity) { - super(server, entity); - } - - @Override - public void loadData() { - // See CraftPlayer#loadData - CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); - if (loaded != null) { - getHandle().readAdditionalSaveData(loaded); - } - } - - @Override - public void saveData() { - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); - - if (!isOnline()) { - // Special case: save old vehicle data - CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } - } - - File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); - NbtIo.writeCompressed(playerData, file); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); - File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(file1, file, file2); - } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); - } - } - -} diff --git a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java b/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java deleted file mode 100644 index a6145d59..00000000 --- a/internal/v1_18_R2/src/main/java/com/lishid/openinv/internal/v1_18_R2/PlayerDataManager.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_18_R2; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IPlayerDataManager; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.OpenInventoryView; -import com.mojang.authlib.GameProfile; -import java.lang.reflect.Field; -import java.util.logging.Logger; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.Vec3; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; -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.CraftContainer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PlayerDataManager implements IPlayerDataManager { - - private @Nullable Field bukkitEntity; - - public PlayerDataManager() { - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); - logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); - bukkitEntity = null; - } - } - - public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - ServerPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); - } - - return nmsPlayer; - } - - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - - // Create a profile and entity to load the player data - // See net.minecraft.server.PlayerList#attemptLogin - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - ServerLevel worldServer = server.getLevel(Level.OVERWORLD); - - if (worldServer == null) { - return null; - } - - ServerPlayer entity = new ServerPlayer(server, worldServer, profile); - - // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. - entity.getAdvancements().stopListening(); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - // Load data. This also reads basic data into the player. - // See CraftPlayer#loadData - CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); - - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return null; - } - - // Also read "extra" data. - entity.readAdditionalSaveData(loadedData); - - if (entity.level == null) { - // Paper: Move player to spawn - ServerLevel level = null; - Vec3 position = null; - if (entity.getRespawnDimension() != null) { - level = entity.server.getLevel(entity.getRespawnDimension()); - if (level != null && entity.getRespawnPosition() != null) { - position = net.minecraft.world.entity.player.Player.findRespawnPositionAndUseSpawnBlock(level, entity.getRespawnPosition(), entity.getRespawnAngle(), false, false).orElse(null); - } - } - if (level == null || position == null) { - level = ((CraftWorld) server.server.getWorlds().get(0)).getHandle(); - position = Vec3.atCenterOf(level.getSharedSpawnPos()); - } - entity.level = level; - entity.setPos(position); - } - - // Return the Bukkit entity. - return entity.getBukkitEntity(); - } - - void injectPlayer(ServerPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } - - bukkitEntity.setAccessible(true); - - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); - } - - @NotNull - @Override - public Player inject(@NotNull Player player) { - try { - ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { - return openPlayer; - } - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - return player; - } - } - - @Nullable - @Override - public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - - ServerPlayer nmsPlayer = getHandle(player); - - if (nmsPlayer.connection == null) { - return null; - } - - InventoryView view = getView(player, inventory); - - if (view == null) { - return player.openInventory(inventory.getBukkitInventory()); - } - - AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { - @Override - public MenuType getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - - container.setTitle(new TextComponent(view.getTitle())); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); - - if (container == null) { - return null; - } - - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - new TextComponent(container.getBukkitView().getTitle()))); - nmsPlayer.containerMenu = container; - nmsPlayer.initMenu(container); - - return container.getBukkitView(); - - } - - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - - static @NotNull MenuType getContainers(int inventorySize) { - - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; // PLAYER - case 41, 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> MenuType.GENERIC_9x3; // Default 27-slot inventory - }; - } - - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - -} diff --git a/internal/v1_19_R2/pom.xml b/internal/v1_19_R2/pom.xml deleted file mode 100644 index 2302185e..00000000 --- a/internal/v1_19_R2/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 4.3.1-SNAPSHOT - - - openinvadapter1_19_R2 - OpenInvAdapter1_19_R2 - - - 17 - 17 - 1.19.3-R0.1-SNAPSHOT - - - - - org.spigotmc - spigot-api - ${spigot.version} - - - spigot - org.spigotmc - provided - ${spigot.version} - remapped-mojang - - - openinvapi - com.lishid - provided - - - openinvplugincore - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-shade-plugin - - - maven-compiler-plugin - - - net.md-5 - specialsource-maven-plugin - - - - - diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java deleted file mode 100644 index 37f13257..00000000 --- a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/AnySilentContainer.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R2; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.util.ReflectionHelper; -import java.lang.reflect.Field; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.CompoundContainer; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.entity.monster.Shulker; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.DoubleBlockCombiner; -import net.minecraft.world.level.block.ShulkerBoxBlock; -import net.minecraft.world.level.block.TrappedChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.ChestBlockEntity; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class AnySilentContainer implements IAnySilentContainer { - - private @Nullable Field serverPlayerGameModeGameType; - - public AnySilentContainer() { - try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); - this.serverPlayerGameModeGameType.setAccessible(true); - } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); - logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); - logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); - // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. - this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); - } catch (SecurityException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to directly write player game mode! SilentContainer will fail."); - logger.log(Level.WARNING, "Error obtaining GameType field", e); - } - } - - @Override - public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { - org.bukkit.World bukkitWorld = shulkerBox.getWorld(); - if (!(bukkitWorld instanceof CraftWorld)) { - bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); - } - - if (!(bukkitWorld instanceof CraftWorld craftWorld)) { - Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); - OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); - return false; - } - - final ServerLevel world = craftWorld.getHandle(); - final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); - final BlockEntity tile = world.getBlockEntity(blockPosition); - - if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) - || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { - return false; - } - - BlockState blockState = world.getBlockState(blockPosition); - - // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen - AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) - .move(blockPosition) - .deflate(1.0E-6D); - return !world.noCollision(boundingBox); - } - - @Override - public boolean activateContainer( - @NotNull final Player bukkitPlayer, - final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - final ServerLevel level = player.getLevel(); - final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final BlockEntity blockEntity = level.getBlockEntity(blockPos); - - if (blockEntity == null) { - return false; - } - - if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock - PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); - enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); - int rows = enderChest.getContainerSize() / 9; - return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable(("container.enderchest")))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(blockEntity instanceof MenuProvider menuProvider)) { - return false; - } - - BlockState blockState = level.getBlockState(blockPos); - Block block = blockState.getBlock(); - - if (block instanceof ChestBlock chestBlock) { - - // boolean flag: do not check if chest is blocked - Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( - // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER - new DoubleBlockCombiner.Combiner>() { - @Override - public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { - CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); - return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); - } - - @Override - public Optional acceptSingle(ChestBlockEntity localChest) { - return Optional.of(localChest); - } - - @Override - public Optional acceptNone() { - return Optional.empty(); - } - }); - - if (menuOptional.isEmpty()) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - menuProvider = menuOptional.get(); - - if (block instanceof TrappedChestBlock) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof ShulkerBoxBlock) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BarrelBlock) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { - player.openMenu(menuProvider); - return true; - } - - // SilentChest requires access to setting players' game mode directly. - if (this.serverPlayerGameModeGameType == null) { - return false; - } - - if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { - if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - player.openMenu(menuProvider); - this.forceGameType(player, gameType); - return true; - } - - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { - return; - } - - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - // Force game mode change without informing plugins or players. - // Regular game mode set calls GameModeChangeEvent and is cancellable. - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - - // ServerPlayer#closeContainer cannot be called without entering an - // infinite loop because this method is called during inventory close. - // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent - player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); - // From ServerPlayer#closeContainer - player.doCloseContainer(); - // Regular inventory close will handle the rest - packet sending, etc. - - // Revert forced game mode. - this.forceGameType(player, gameType); - } - - private void forceGameType(final ServerPlayer player, final GameType gameMode) { - if (this.serverPlayerGameModeGameType == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - this.serverPlayerGameModeGameType.setAccessible(true); - this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } - -} diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java deleted file mode 100644 index bb5680ce..00000000 --- a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialEnderChest.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R2; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.ContainerListener; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_19_R2.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { - - private final CraftInventory inventory; - private ServerPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChestInventory().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (!this.playerOnline) { - try { - this.owner = PlayerDataManager.getHandle(player); - PlayerEnderChestContainer enderChest = owner.getEnderChestInventory(); - for (int i = 0; i < enderChest.getContainerSize(); ++i) { - enderChest.setItem(i, this.items.get(i)); - } - this.items = enderChest.items; - enderChest.transaction.addAll(this.transaction); - } catch (Exception ignored) {} - this.playerOnline = true; - } - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChestInventory().getViewers(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public void setActiveChest(EnderChestBlockEntity enderChest) { - this.owner.getEnderChestInventory().setActiveChest(enderChest); - } - - @Override - public boolean isActiveChest(EnderChestBlockEntity enderChest) { - return this.owner.getEnderChestInventory().isActiveChest(enderChest); - } - - @Override - public int getMaxStackSize() { - return this.owner.getEnderChestInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChestInventory().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChestInventory().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void addListener(ContainerListener listener) { - this.owner.getEnderChestInventory().addListener(listener); - } - - @Override - public void removeListener(ContainerListener listener) { - this.owner.getEnderChestInventory().removeListener(listener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int i, int j) { - ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack addItem(ItemStack itemstack) { - ItemStack localItem = itemstack.copy(); - this.moveItemToOccupiedSlotsWithSameType(localItem); - if (localItem.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.moveItemToEmptySlots(localItem); - return localItem.isEmpty() ? ItemStack.EMPTY : localItem; - } - } - - @Override - public boolean canAddItem(ItemStack itemstack) { - for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { - return true; - } - } - - return false; - } - - private void moveItemToEmptySlots(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (localItem.isEmpty()) { - this.setItem(i, itemstack.copy()); - itemstack.setCount(0); - return; - } - } - } - - private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameTags(localItem, itemstack)) { - this.moveItemsBetweenStacks(itemstack, localItem); - if (itemstack.isEmpty()) { - return; - } - } - } - } - - private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { - int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); - int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); - if (j > 0) { - itemstack1.grow(j); - itemstack.shrink(j); - this.setChanged(); - } - } - - @Override - public ItemStack removeItemNoUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.items.set(i, ItemStack.EMPTY); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.setChanged(); - } - - @Override - public int getContainerSize() { - return this.owner.getEnderChestInventory().getContainerSize(); - } - - @Override - public boolean isEmpty() { - return this.items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void startOpen(Player player) { - } - - @Override - public void stopOpen(Player player) { - } - - @Override - public boolean canPlaceItem(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clearContent() { - this.items.clear(); - this.setChanged(); - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountStack(itemstack); - } - - } - - @Override - public List removeAllItems() { - List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clearContent(); - return list; - } - - @Override - public ItemStack removeItemType(Item item, int i) { - ItemStack itemstack = new ItemStack(item, 0); - - for(int j = this.getContainerSize() - 1; j >= 0; --j) { - ItemStack localItem = this.getItem(j); - if (localItem.getItem().equals(item)) { - int k = i - itemstack.getCount(); - ItemStack splitItem = localItem.split(k); - itemstack.grow(splitItem.getCount()); - if (itemstack.getCount() == i) { - break; - } - } - } - - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); - } - - @Override - public void fromTag(ListTag listTag) { - for (int i = 0; i < this.getContainerSize(); ++i) { - this.setItem(i, ItemStack.EMPTY); - } - - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.of(compoundTag)); - } - } - - } - -} diff --git a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java b/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java deleted file mode 100644 index ef0cca6f..00000000 --- a/internal/v1_19_R2/src/main/java/com/lishid/openinv/internal/v1_19_R2/SpecialPlayerInventory.java +++ /dev/null @@ -1,788 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R2; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_19_R2.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private Player player; - private NonNullList items; - private NonNullList armor; - private NonNullList offhand; - private List> compartments; - - public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.selected = player.getInventory().selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (!this.playerOnline) { - Player entityPlayer = PlayerDataManager.getHandle(player); - entityPlayer.getInventory().transaction.addAll(this.transaction); - this.player = entityPlayer; - for (int i = 0; i < getContainerSize(); ++i) { - this.player.getInventory().setItem(i, getRawItem(i)); - } - this.player.getInventory().selected = this.selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - this.playerOnline = true; - } - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - private @NotNull ItemStack getRawItem(int i) { - if (i < 0) { - return ItemStack.EMPTY; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - return list.get(i); - } - } - - return ItemStack.EMPTY; - } - - private void setRawItem(int i, @NotNull ItemStack itemStack) { - if (i < 0) { - return; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - list.set(i, itemStack); - } - } - } - - private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - - private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { - if (index < items.size()) { - return new IndexedCompartment(items, getReversedItemSlotNum(index)); - } - - index -= items.size(); - - if (index < armor.size()) { - return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); - } - - index -= armor.size(); - - if (index < offhand.size()) { - return new IndexedCompartment(offhand, index); - } - - index -= offhand.size(); - - return new IndexedCompartment(null, index); - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - private boolean contains(Predicate predicate) { - return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.player.getInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.player.getInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.player.getInventory().getViewers(); - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public int getMaxStackSize() { - return this.player.getInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int size) { - this.player.getInventory().setMaxStackSize(size); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public boolean hasCustomName() { - return false; - } - - @Override - public List getContents() { - return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); - } - - @Override - public ItemStack getSelected() { - return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; - } - - private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getRawItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { - remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); - if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { - remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; - } - - @Override - public int getFreeSlot() { - for(int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void setPickedItem(ItemStack itemstack) { - int i = this.findSlotMatchingItem(itemstack); - if (isHotbarSlot(i)) { - this.selected = i; - } else if (i == -1) { - this.selected = this.getSuitableHotbarSlot(); - if (!this.items.get(this.selected).isEmpty()) { - int j = this.getFreeSlot(); - if (j != -1) { - this.items.set(j, this.items.get(this.selected)); - } - } - - this.items.set(this.selected, itemstack); - } else { - this.pickSlot(i); - } - - } - - @Override - public void pickSlot(int i) { - this.selected = this.getSuitableHotbarSlot(); - ItemStack itemstack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int findSlotMatchingItem(ItemStack itemstack) { - for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { - return i; - } - } - - return -1; - } - - @Override - public int findSlotMatchingUnusedItem(ItemStack itemStack) { - for(int i = 0; i < this.items.size(); ++i) { - ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { - return i; - } - } - - return -1; - } - - @Override - public int getSuitableHotbarSlot() { - int i; - int j; - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (!this.items.get(i).isEnchanted()) { - return i; - } - } - - return this.selected; - } - - @Override - public void swapPaint(double d0) { - if (d0 > 0.0D) { - d0 = 1.0D; - } - - if (d0 < 0.0D) { - d0 = -1.0D; - } - - this.selected = (int) (this.selected - d0); - - while (this.selected < 0) { - this.selected += 9; - } - - while(this.selected >= 9) { - this.selected -= 9; - } - } - - @Override - public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); - j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); - ItemStack itemstack = this.player.containerMenu.getCarried(); - j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); - if (itemstack.isEmpty()) { - this.player.containerMenu.setCarried(ItemStack.EMPTY); - } - - return j; - } - - private int addResource(ItemStack itemstack) { - int i = this.getSlotWithRemainingSpace(itemstack); - if (i == -1) { - i = this.getFreeSlot(); - } - - return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); - } - - private int addResource(int i, ItemStack itemstack) { - Item item = itemstack.getItem(); - int j = itemstack.getCount(); - ItemStack localItemStack = this.getRawItem(i); - if (localItemStack.isEmpty()) { - localItemStack = new ItemStack(item, 0); - if (itemstack.hasTag()) { - // hasTag ensures tag not null - //noinspection ConstantConditions - localItemStack.setTag(itemstack.getTag().copy()); - } - - this.setRawItem(i, localItemStack); - } - - int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); - - if (k > this.getMaxStackSize() - localItemStack.getCount()) { - k = this.getMaxStackSize() - localItemStack.getCount(); - } - - if (k != 0) { - j -= k; - localItemStack.grow(k); - localItemStack.setPopTime(5); - } - - return j; - } - - @Override - public int getSlotWithRemainingSpace(ItemStack itemstack) { - if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { - return this.selected; - } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { - return 40; - } else { - for(int i = 0; i < this.items.size(); ++i) { - if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void tick() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (!compartment.get(i).isEmpty()) { - compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); - } - } - } - - } - - @Override - public boolean add(ItemStack itemStack) { - return this.add(-1, itemStack); - } - - @Override - public boolean add(int i, ItemStack itemStack) { - if (itemStack.isEmpty()) { - return false; - } else { - try { - if (itemStack.isDamaged()) { - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i >= 0) { - this.items.set(i, itemStack.copy()); - this.items.get(i).setPopTime(5); - itemStack.setCount(0); - return true; - } else if (this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemStack.getCount(); - if (i == -1) { - itemStack.setCount(this.addResource(itemStack)); - } else { - itemStack.setCount(this.addResource(i, itemStack)); - } - } while(!itemStack.isEmpty() && itemStack.getCount() < j); - - if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return itemStack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); - crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); - crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); - crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); - throw new ReportedException(crashReport); - } - } - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack) { - this.placeItemBackInInventory(itemStack, true); - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { - while(true) { - if (!itemStack.isEmpty()) { - int i = this.getSlotWithRemainingSpace(itemStack); - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); - if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { - ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); - } - continue; - } - - this.player.drop(itemStack, false); - } - - return; - } - } - - @Override - public ItemStack removeItem(int rawIndex, final int j) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null - || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { - return ItemStack.EMPTY; - } - - return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); - } - - @Override - public void removeItem(ItemStack itemStack) { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (compartment.get(i) == itemStack) { - compartment.set(i, ItemStack.EMPTY); - break; - } - } - } - } - - @Override - public ItemStack removeItemNoUpdate(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); - - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - - return removed; - } - - @Override - public void setItem(int rawIndex, final ItemStack itemStack) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - this.player.drop(itemStack, true); - return; - } - - indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); - } - - @Override - public float getDestroySpeed(BlockState blockState) { - return this.items.get(this.selected).getDestroySpeed(blockState); - } - - @Override - public ListTag save(ListTag listTag) { - for (int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.offhand.size(); ++i) { - if (!this.offhand.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - return listTag; - } - - @Override - public void load(ListTag listTag) { - this.items.clear(); - this.armor.clear(); - this.offhand.clear(); - - for(int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.of(compoundTag); - if (!itemstack.isEmpty()) { - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } - } - } - - } - - @Override - public int getContainerSize() { - return 45; - } - - @Override - public boolean isEmpty() { - return !contains(itemStack -> !itemStack.isEmpty()); - } - - @Override - public ItemStack getItem(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - return indexedCompartment.compartment().get(indexedCompartment.index()); - } - - @Override - public Component getName() { - return this.player.getName(); - } - - @Override - public ItemStack getArmor(int index) { - return this.armor.get(index); - } - - @Override - public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { - if (damage > 0.0F) { - damage /= 4.0F; - if (damage < 1.0F) { - damage = 1.0F; - } - - for (int index : armorIndices) { - ItemStack itemstack = this.armor.get(index); - if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { - itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); - } - } - } - } - - @Override - public void dropAll() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - ItemStack itemstack = compartment.get(i); - if (!itemstack.isEmpty()) { - this.player.drop(itemstack, true, false); - compartment.set(i, ItemStack.EMPTY); - } - } - } - } - - @Override - public void setChanged() { - super.setChanged(); - } - - @Override - public int getTimesChanged() { - return super.getTimesChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean contains(ItemStack itemstack) { - return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); - } - - @Override - public boolean contains(TagKey tagKey) { - - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); - } - - @Override - public void replaceWith(Inventory inventory) { - Function getter; - - if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { - getter = specialPlayerInventory::getRawItem; - } else { - getter = inventory::getItem; - } - - for(int i = 0; i < this.getContainerSize(); ++i) { - this.setRawItem(i, getter.apply(i)); - } - - this.selected = inventory.selected; - } - - @Override - public void clearContent() { - for (NonNullList compartment : this.compartments) { - compartment.clear(); - } - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountSimpleStack(itemstack); - } - } - - @Override - public ItemStack removeFromSelected(boolean dropWholeStack) { - ItemStack itemstack = this.getSelected(); - return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); - } - -} diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java index 93e8d037..b78bb393 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java +++ b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java @@ -20,7 +20,6 @@ import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.util.ReflectionHelper; import java.lang.reflect.Field; -import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; import net.minecraft.core.BlockPos; @@ -28,7 +27,6 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.CompoundContainer; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleMenuProvider; import net.minecraft.world.entity.monster.Shulker; @@ -39,11 +37,9 @@ import net.minecraft.world.level.block.BarrelBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.DoubleBlockCombiner; import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.TrappedChestBlock; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.ChestBlockEntity; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; @@ -160,33 +156,13 @@ public boolean activateContainer( if (block instanceof ChestBlock chestBlock) { // boolean flag: do not check if chest is blocked - Optional menuOptional = chestBlock.combine(blockState, level, blockPos, true).apply( - // Combiner is a copy of private ChestBlock.MENU_PROVIDER_COMBINER - new DoubleBlockCombiner.Combiner>() { - @Override - public Optional acceptDouble(ChestBlockEntity localChest1, ChestBlockEntity localChest2) { - CompoundContainer doubleChest = new CompoundContainer(localChest1, localChest2); - return Optional.of(new ChestBlock.DoubleInventory(localChest1, localChest2, doubleChest)); - } - - @Override - public Optional acceptSingle(ChestBlockEntity localChest) { - return Optional.of(localChest); - } - - @Override - public Optional acceptNone() { - return Optional.empty(); - } - }); - - if (menuOptional.isEmpty()) { + menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); + + if (menuProvider == null) { OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } - menuProvider = menuOptional.get(); - if (block instanceof TrappedChestBlock) { bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); } else { diff --git a/internal/v1_18_R2/pom.xml b/internal/v1_20_R1/pom.xml similarity index 92% rename from internal/v1_18_R2/pom.xml rename to internal/v1_20_R1/pom.xml index 7569fc0d..b7e1d545 100644 --- a/internal/v1_18_R2/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -1,6 +1,6 @@ unknown @@ -39,9 +40,8 @@ api plugin - internal/v1_18_R2 - internal/v1_19_R2 internal/v1_19_R3 + internal/v1_20_R1 assembly From 5a7d1399b56d9f38ead566d62b82e364e3f1d14b Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Jun 2023 11:09:30 -0400 Subject: [PATCH 125/340] Bump version to 4.3.1 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index c2e74f76..e474913f 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.3.1-SNAPSHOT + 4.3.1 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 2a0a0cf5..4a8cdbc8 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.3.1-SNAPSHOT + 4.3.1 openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 4413745b..6e9a6c10 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.3.1-SNAPSHOT + 4.3.1 openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index b7e1d545..0b3600f6 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.3.1-SNAPSHOT + 4.3.1 openinvadapter1_20_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index dd315012..0b757e3a 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.3.1-SNAPSHOT + 4.3.1 openinvplugincore diff --git a/pom.xml b/pom.xml index 2347d3e8..d2384bfa 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.3.1-SNAPSHOT + 4.3.1 pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.3.1-SNAPSHOT + 4.3.1 openinvplugincore com.lishid compile - 4.3.1-SNAPSHOT + 4.3.1 com.lishid From 9ec7bebecbb0967bb39efaff6c6098f2aa5f125a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Jun 2023 11:09:43 -0400 Subject: [PATCH 126/340] Bump version to 4.3.2-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index e474913f..fe0552d0 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.3.1 + 4.3.2-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 4a8cdbc8..54f94a1f 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.3.1 + 4.3.2-SNAPSHOT openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 6e9a6c10..8e940b68 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.3.1 + 4.3.2-SNAPSHOT openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index 0b3600f6..cabff6b4 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.3.1 + 4.3.2-SNAPSHOT openinvadapter1_20_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 0b757e3a..77fb5af1 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.3.1 + 4.3.2-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index d2384bfa..f9103624 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.3.1 + 4.3.2-SNAPSHOT pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.3.1 + 4.3.2-SNAPSHOT openinvplugincore com.lishid compile - 4.3.1 + 4.3.2-SNAPSHOT com.lishid From 922586c92532ccfcda41c974fd8bbf10607b0f8c Mon Sep 17 00:00:00 2001 From: XPYEX <50171612+0XPYEX0@users.noreply.github.com> Date: Sat, 17 Jun 2023 19:35:21 +0800 Subject: [PATCH 127/340] Improve support for non-UTF-8 systems (#151) --- .../java/com/lishid/openinv/util/lang/LanguageManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java index 239031e0..4438977b 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -89,7 +90,7 @@ public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) { if (resourceStream == null) { localeConfigDefaults = new YamlConfiguration(); } else { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream, StandardCharsets.UTF_8))) { localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); } catch (IOException e) { plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + locale + ".yml"); From 57caf855002c5534f9e3b4bb64168f50d8f7f3d1 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 10 Sep 2023 11:23:49 -0400 Subject: [PATCH 128/340] Update Spigot --- internal/v1_20_R1/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index cabff6b4..5f004c5b 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -32,7 +32,7 @@ 17 17 - 1.20-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT From 1bebdb56028132151b921cf276c4977278ea8719 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 22 Sep 2023 23:06:05 -0400 Subject: [PATCH 129/340] Add OpenPlayerSaveEvent Closes #156 --- api/pom.xml | 2 +- .../openinv/event/OpenPlayerSaveEvent.java | 74 +++++++++++++++++++ assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- .../com/lishid/openinv/InventoryListener.java | 4 +- .../main/java/com/lishid/openinv/OpenInv.java | 20 +++-- pom.xml | 6 +- 9 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java diff --git a/api/pom.xml b/api/pom.xml index fe0552d0..eeee22d6 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT openinvapi diff --git a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java new file mode 100644 index 00000000..a36a6c96 --- /dev/null +++ b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java @@ -0,0 +1,74 @@ +package com.lishid.openinv.event; + +import com.lishid.openinv.internal.ISpecialInventory; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Event fired before OpenInv saves a player's data. + */ +public class OpenPlayerSaveEvent extends Event implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final Player player; + private final ISpecialInventory inventory; + private boolean cancelled = false; + + public OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) { + this.player = player; + this.inventory = inventory; + } + + /** + * Get the {@link Player} whose data is being saved. + * + * @return player the Player whose data is being saved + */ + public @NotNull Player getPlayer() { + return player; + } + + /** + * Get the {@link ISpecialInventory} that triggered the save by being closed. + * + * @return the special inventory + */ + public @NotNull ISpecialInventory getInventory() { + return inventory; + } + + /** + * Get whether the event is cancelled. + * + * @return true if the event is cancelled + */ + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * Set whether the event is cancelled. + * + * @param cancel whether the event is cancelled + */ + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + +} diff --git a/assembly/pom.xml b/assembly/pom.xml index 54f94a1f..2a690911 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 8e940b68..56acad93 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index 5f004c5b..acb7f214 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT openinvadapter1_20_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 77fb5af1..dbaccf94 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT openinvplugincore diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index 7101fa44..ef159314 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -63,11 +63,11 @@ private void onInventoryClose(@NotNull final InventoryCloseEvent event) { ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory()); if (specialInventory != null) { - this.plugin.handleCloseInventory(event.getPlayer(), specialInventory); + this.plugin.handleCloseInventory(specialInventory); } else { specialInventory = InventoryAccess.getPlayerInventory(event.getInventory()); if (specialInventory != null) { - this.plugin.handleCloseInventory(event.getPlayer(), specialInventory); + this.plugin.handleCloseInventory(specialInventory); } } } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 25b28aa1..9ff7ee5c 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -23,6 +23,7 @@ import com.lishid.openinv.commands.SearchContainerCommand; import com.lishid.openinv.commands.SearchEnchantCommand; import com.lishid.openinv.commands.SearchInvCommand; +import com.lishid.openinv.event.OpenPlayerSaveEvent; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; @@ -544,7 +545,7 @@ private void setPlayerOffline( } } - void handleCloseInventory(@NotNull HumanEntity exViewer, @NotNull ISpecialInventory inventory) { + void handleCloseInventory(@NotNull ISpecialInventory inventory) { Map map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests; UUID key = inventory.getPlayer().getUniqueId(); @Nullable ISpecialInventory loaded = map.get(key); @@ -576,11 +577,18 @@ void handleCloseInventory(@NotNull HumanEntity exViewer, @NotNull ISpecialInvent // Re-fetch from map - prevents duplicate saves on multi-close. ISpecialInventory current = map.remove(key); - if (!disableSaving() - && current != null - && current.getPlayer() instanceof Player player - && !player.isOnline()) { - this.accessor.getPlayerDataManager().inject(player).saveData(); + if (disableSaving() + || current == null + || !(current.getPlayer() instanceof Player player) + || player.isOnline()) { + return; + } + + OpenPlayerSaveEvent event = new OpenPlayerSaveEvent(player, current); + getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.accessor.getPlayerDataManager().inject(player).saveData(); } }); } diff --git a/pom.xml b/pom.xml index f9103624..f7271e0f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT pom @@ -84,13 +84,13 @@ openinvapi com.lishid compile - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT openinvplugincore com.lishid compile - 4.3.2-SNAPSHOT + 4.4.0-SNAPSHOT com.lishid From d636cdd8394a15ca9042b12e461f1a36932b59d3 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 22 Sep 2023 23:14:07 -0400 Subject: [PATCH 130/340] Add support for 1.20.2 --- internal/v1_20_R2/pom.xml | 81 ++ .../internal/v1_20_R2/AnySilentContainer.java | 243 ++++++ .../openinv/internal/v1_20_R2/OpenPlayer.java | 74 ++ .../internal/v1_20_R2/PlayerDataManager.java | 249 ++++++ .../internal/v1_20_R2/SpecialEnderChest.java | 350 ++++++++ .../v1_20_R2/SpecialPlayerInventory.java | 806 ++++++++++++++++++ pom.xml | 1 + 7 files changed, 1804 insertions(+) create mode 100644 internal/v1_20_R2/pom.xml create mode 100644 internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java create mode 100644 internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java create mode 100644 internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java create mode 100644 internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java create mode 100644 internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml new file mode 100644 index 00000000..edfe39ef --- /dev/null +++ b/internal/v1_20_R2/pom.xml @@ -0,0 +1,81 @@ + + + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 4.4.0-SNAPSHOT + + + openinvadapter1_20_R2 + OpenInvAdapter1_20_R2 + + + 17 + 17 + 1.20.2-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot-api + ${spigot.version} + + + spigot + org.spigotmc + provided + ${spigot.version} + remapped-mojang + + + openinvapi + com.lishid + provided + + + openinvplugincore + com.lishid + + + annotations + org.jetbrains + + + + + + + maven-shade-plugin + + + maven-compiler-plugin + + + net.md-5 + specialsource-maven-plugin + + + + + diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java new file mode 100644 index 00000000..c3de0eba --- /dev/null +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_20_R2; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.util.ReflectionHelper; +import java.lang.reflect.Field; +import java.util.logging.Logger; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.ShulkerBox; +import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AnySilentContainer implements IAnySilentContainer { + + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer() { + try { + try { + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); + } + } + + @Override + public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { + org.bukkit.World bukkitWorld = shulkerBox.getWorld(); + if (!(bukkitWorld instanceof CraftWorld)) { + bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); + } + + if (!(bukkitWorld instanceof CraftWorld craftWorld)) { + Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); + OpenInv.getPlugin(OpenInv.class).getLogger().log(java.util.logging.Level.WARNING, exception.getMessage(), exception); + return false; + } + + final ServerLevel world = craftWorld.getHandle(); + final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); + final BlockEntity tile = world.getBlockEntity(blockPosition); + + if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) + || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { + return false; + } + + BlockState blockState = world.getBlockState(blockPosition); + + // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen + AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) + .move(blockPosition) + .deflate(1.0E-6D); + return !world.noCollision(boundingBox); + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + final net.minecraft.world.level.Level level = player.level(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); + + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, Component.translatable(("container.enderchest")))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: do not check if chest is blocked + menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); + + if (menuProvider == null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; + } + + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { + return; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // ServerPlayer#closeContainer cannot be called without entering an + // infinite loop because this method is called during inventory close. + // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent + player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); + // From ServerPlayer#closeContainer + player.doCloseContainer(); + // Regular inventory close will handle the rest - packet sending, etc. + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java new file mode 100644 index 00000000..fc70decb --- /dev/null +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_20_R2; + +import com.mojang.logging.LogUtils; +import java.io.File; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.bukkit.craftbukkit.v1_20_R2.CraftServer; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; + +public class OpenPlayer extends CraftPlayer { + + public OpenPlayer(CraftServer server, ServerPlayer entity) { + super(server, entity); + } + + @Override + public void loadData() { + // See CraftPlayer#loadData + CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); + if (loaded != null) { + getHandle().readAdditionalSaveData(loaded); + } + } + + @Override + public void saveData() { + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + setExtraData(playerData); + + if (!isOnline()) { + // Special case: save old vehicle data + CompoundTag oldData = worldNBTStorage.load(player); + + if (oldData != null && oldData.contains("RootVehicle", 10)) { + // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) + playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + } + + File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + NbtIo.writeCompressed(playerData, file); + File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); + File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(file1, file, file2); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); + } + } + +} diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java new file mode 100644 index 00000000..9d6ec286 --- /dev/null +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_20_R2; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IPlayerDataManager; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.OpenInventoryView; +import com.mojang.authlib.GameProfile; +import java.lang.reflect.Field; +import java.util.logging.Logger; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_20_R2.CraftServer; +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.CraftContainer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerDataManager implements IPlayerDataManager { + + private @Nullable Field bukkitEntity; + + public PlayerDataManager() { + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } + + // Create a profile and entity to load the player data + // See net.minecraft.server.players.PlayerList#canPlayerLogin + // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + + if (worldServer == null) { + return null; + } + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, null); + + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + // Load data. This also reads basic data into the player. + // See CraftPlayer#loadData + CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); + + if (loadedData == null) { + // Exceptions with loading are logged by Mojang. + return null; + } + + // Also read "extra" data. + entity.readAdditionalSaveData(loadedData); + + if (entity.level() == null) { + // Paper: Move player to spawn + entity.spawnIn(null); + } + + // Return the Bukkit entity. + return entity.getBukkitEntity(); + } + + void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + } + + @NotNull + @Override + public Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return player; + } + } + + @Nullable + @Override + public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + + ServerPlayer nmsPlayer = getHandle(player); + + if (nmsPlayer.connection == null) { + return null; + } + + InventoryView view = getView(player, inventory); + + if (view == null) { + return player.openInventory(inventory.getBukkitInventory()); + } + + AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + @Override + public MenuType getType() { + return getContainers(inventory.getBukkitInventory().getSize()); + } + }; + + container.setTitle(Component.literal(view.getTitle())); + container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + + if (container == null) { + return null; + } + + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), + Component.literal(container.getBukkitView().getTitle()))); + nmsPlayer.containerMenu = container; + nmsPlayer.initMenu(container); + + return container.getBukkitView(); + + } + + private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { + if (inventory instanceof SpecialEnderChest) { + return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); + } else if (inventory instanceof SpecialPlayerInventory) { + return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); + } else { + return null; + } + } + + static @NotNull MenuType getContainers(int inventorySize) { + + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> MenuType.GENERIC_9x3; // Default 27-slot inventory + }; + } + + @Override + public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java new file mode 100644 index 00000000..5987e9bf --- /dev/null +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_20_R2; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ContainerListener; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { + + private final CraftInventory inventory; + private ServerPlayer owner; + private NonNullList items; + private boolean playerOnline; + + public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { + super(PlayerDataManager.getHandle(player)); + this.inventory = new CraftInventory(this); + this.owner = PlayerDataManager.getHandle(player); + this.playerOnline = online; + this.items = this.owner.getEnderChestInventory().items; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { + if (this.playerOnline) { + return; + } + + ServerPlayer offlinePlayer = this.owner; + ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + + // Set owner to new player. + this.owner = onlinePlayer; + + // Set player's ender chest contents to our modified contents. + PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); + for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { + onlineEnderChest.setItem(i, this.items.get(i)); + } + + // Set our item array to the new inventory's array. + this.items = onlineEnderChest.items; + + // Add viewers to new inventory. + onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); + + this.playerOnline = true; + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public List getContents() { + return this.items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.owner.getEnderChestInventory().getViewers(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void setActiveChest(EnderChestBlockEntity enderChest) { + this.owner.getEnderChestInventory().setActiveChest(enderChest); + } + + @Override + public boolean isActiveChest(EnderChestBlockEntity enderChest) { + return this.owner.getEnderChestInventory().isActiveChest(enderChest); + } + + @Override + public int getMaxStackSize() { + return this.owner.getEnderChestInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int i) { + this.owner.getEnderChestInventory().setMaxStackSize(i); + } + + @Override + public InventoryHolder getOwner() { + return this.owner.getEnderChestInventory().getOwner(); + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void addListener(ContainerListener listener) { + this.owner.getEnderChestInventory().addListener(listener); + } + + @Override + public void removeListener(ContainerListener listener) { + this.owner.getEnderChestInventory().removeListener(listener); + } + + @Override + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int i, int j) { + ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack addItem(ItemStack itemstack) { + ItemStack localItem = itemstack.copy(); + this.moveItemToOccupiedSlotsWithSameType(localItem); + if (localItem.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.moveItemToEmptySlots(localItem); + return localItem.isEmpty() ? ItemStack.EMPTY : localItem; + } + } + + @Override + public boolean canAddItem(ItemStack itemstack) { + for (ItemStack itemstack1 : this.items) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + private void moveItemToEmptySlots(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (localItem.isEmpty()) { + this.setItem(i, itemstack.copy()); + itemstack.setCount(0); + return; + } + } + } + + private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (ItemStack.isSameItemSameTags(localItem, itemstack)) { + this.moveItemsBetweenStacks(itemstack, localItem); + if (itemstack.isEmpty()) { + return; + } + } + } + } + + private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { + int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); + int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); + if (j > 0) { + itemstack1.grow(j); + itemstack.shrink(j); + this.setChanged(); + } + } + + @Override + public ItemStack removeItemNoUpdate(int i) { + ItemStack itemstack = this.items.get(i); + if (itemstack.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.items.set(i, ItemStack.EMPTY); + return itemstack; + } + } + + @Override + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.setChanged(); + } + + @Override + public int getContainerSize() { + return this.owner.getEnderChestInventory().getContainerSize(); + } + + @Override + public boolean isEmpty() { + return this.items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void startOpen(Player player) { + } + + @Override + public void stopOpen(Player player) { + } + + @Override + public boolean canPlaceItem(int i, ItemStack itemstack) { + return true; + } + + @Override + public void clearContent() { + this.items.clear(); + this.setChanged(); + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountStack(itemstack); + } + + } + + @Override + public List removeAllItems() { + List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clearContent(); + return list; + } + + @Override + public ItemStack removeItemType(Item item, int i) { + ItemStack itemstack = new ItemStack(item, 0); + + for(int j = this.getContainerSize() - 1; j >= 0; --j) { + ItemStack localItem = this.getItem(j); + if (localItem.getItem().equals(item)) { + int k = i - itemstack.getCount(); + ItemStack splitItem = localItem.split(k); + itemstack.grow(splitItem.getCount()); + if (itemstack.getCount() == i) { + break; + } + } + } + + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public String toString() { + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); + } + + @Override + public void fromTag(ListTag listTag) { + for (int i = 0; i < this.getContainerSize(); ++i) { + this.setItem(i, ItemStack.EMPTY); + } + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + if (j < this.getContainerSize()) { + this.setItem(j, ItemStack.of(compoundTag)); + } + } + + } + +} diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java new file mode 100644 index 00000000..3e527ea2 --- /dev/null +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_20_R2; + +import com.google.common.collect.ImmutableList; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.DamageTypeTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { + + private final CraftInventory inventory; + private boolean playerOnline; + private Player player; + private NonNullList items; + private NonNullList armor; + private NonNullList offhand; + private List> compartments; + + public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { + super(PlayerDataManager.getHandle(bukkitPlayer)); + this.inventory = new CraftInventory(this); + this.playerOnline = online; + this.player = super.player; + this.selected = player.getInventory().selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + if (this.playerOnline) { + return; + } + + Player offlinePlayer = this.player; + Player onlinePlayer = PlayerDataManager.getHandle(player); + onlinePlayer.getInventory().transaction.addAll(this.transaction); + + // Set owner to new player. + this.player = onlinePlayer; + + // Set player's inventory contents to our modified contents. + Inventory onlineInventory = onlinePlayer.getInventory(); + for (int i = 0; i < getContainerSize(); ++i) { + onlineInventory.setItem(i, getRawItem(i)); + } + onlineInventory.selected = this.selected; + + // Set our item arrays to the new inventory's arrays. + this.items = onlineInventory.items; + this.armor = onlineInventory.armor; + this.offhand = onlineInventory.offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + + // Add existing viewers to new viewer list. + Inventory offlineInventory = offlinePlayer.getInventory(); + // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. + offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); + onlineInventory.transaction.addAll(offlineInventory.transaction); + + this.playerOnline = true; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } + + private @NotNull ItemStack getRawItem(int i) { + if (i < 0) { + return ItemStack.EMPTY; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + return list.get(i); + } + } + + return ItemStack.EMPTY; + } + + private void setRawItem(int i, @NotNull ItemStack itemStack) { + if (i < 0) { + return; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + list.set(i, itemStack); + } + } + } + + private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + + private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { + if (index < items.size()) { + return new IndexedCompartment(items, getReversedItemSlotNum(index)); + } + + index -= items.size(); + + if (index < armor.size()) { + return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); + } + + index -= armor.size(); + + if (index < offhand.size()) { + return new IndexedCompartment(offhand, index); + } + + index -= offhand.size(); + + return new IndexedCompartment(null, index); + } + + private int getReversedArmorSlotNum(final int i) { + if (i == 0) { + return 3; + } + if (i == 1) { + return 2; + } + if (i == 2) { + return 1; + } + if (i == 3) { + return 0; + } + return i; + } + + private int getReversedItemSlotNum(final int i) { + if (i >= 27) { + return i - 27; + } + return i + 9; + } + + private boolean contains(Predicate predicate) { + return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); + } + + @Override + public List getArmorContents() { + return this.armor; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.player.getInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.player.getInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.player.getInventory().getViewers(); + } + + @Override + public InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + @Override + public int getMaxStackSize() { + return this.player.getInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int size) { + this.player.getInventory().setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return this.player.getBukkitEntity().getLocation(); + } + + @Override + public boolean hasCustomName() { + return false; + } + + @Override + public List getContents() { + return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + + @Override + public ItemStack getSelected() { + return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; + } + + private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + @Override + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getRawItem(i); + if (itemstack1.isEmpty()) { + return itemstack.getCount(); + } + + if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); + } + + if (remains <= 0) { + return itemstack.getCount(); + } + } + + ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); + if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { + remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); + } + + return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; + } + + @Override + public int getFreeSlot() { + for(int i = 0; i < this.items.size(); ++i) { + if (this.items.get(i).isEmpty()) { + return i; + } + } + + return -1; + } + + @Override + public void setPickedItem(ItemStack itemstack) { + int i = this.findSlotMatchingItem(itemstack); + if (isHotbarSlot(i)) { + this.selected = i; + } else if (i == -1) { + this.selected = this.getSuitableHotbarSlot(); + if (!this.items.get(this.selected).isEmpty()) { + int j = this.getFreeSlot(); + if (j != -1) { + this.items.set(j, this.items.get(this.selected)); + } + } + + this.items.set(this.selected, itemstack); + } else { + this.pickSlot(i); + } + + } + + @Override + public void pickSlot(int i) { + this.selected = this.getSuitableHotbarSlot(); + ItemStack itemstack = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(i)); + this.items.set(i, itemstack); + } + + @Override + public int findSlotMatchingItem(ItemStack itemstack) { + for(int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { + return i; + } + } + + return -1; + } + + @Override + public int findSlotMatchingUnusedItem(ItemStack itemStack) { + for(int i = 0; i < this.items.size(); ++i) { + ItemStack localItem = this.items.get(i); + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { + return i; + } + } + + return -1; + } + + @Override + public int getSuitableHotbarSlot() { + int i; + int j; + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (this.items.get(i).isEmpty()) { + return i; + } + } + + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (!this.items.get(i).isEnchanted()) { + return i; + } + } + + return this.selected; + } + + @Override + public void swapPaint(double d0) { + if (d0 > 0.0D) { + d0 = 1.0D; + } + + if (d0 < 0.0D) { + d0 = -1.0D; + } + + this.selected = (int) (this.selected - d0); + + while (this.selected < 0) { + this.selected += 9; + } + + while(this.selected >= 9) { + this.selected -= 9; + } + } + + @Override + public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { + byte b0 = 0; + boolean flag = i == 0; + int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); + j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); + ItemStack itemstack = this.player.containerMenu.getCarried(); + j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); + if (itemstack.isEmpty()) { + this.player.containerMenu.setCarried(ItemStack.EMPTY); + } + + return j; + } + + private int addResource(ItemStack itemstack) { + int i = this.getSlotWithRemainingSpace(itemstack); + if (i == -1) { + i = this.getFreeSlot(); + } + + return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); + } + + private int addResource(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + int j = itemstack.getCount(); + ItemStack localItemStack = this.getRawItem(i); + if (localItemStack.isEmpty()) { + localItemStack = new ItemStack(item, 0); + if (itemstack.hasTag()) { + // hasTag ensures tag not null + //noinspection ConstantConditions + localItemStack.setTag(itemstack.getTag().copy()); + } + + this.setRawItem(i, localItemStack); + } + + int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); + + if (k > this.getMaxStackSize() - localItemStack.getCount()) { + k = this.getMaxStackSize() - localItemStack.getCount(); + } + + if (k != 0) { + j -= k; + localItemStack.grow(k); + localItemStack.setPopTime(5); + } + + return j; + } + + @Override + public int getSlotWithRemainingSpace(ItemStack itemstack) { + if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { + return this.selected; + } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { + return 40; + } else { + for(int i = 0; i < this.items.size(); ++i) { + if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + @Override + public void tick() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (!compartment.get(i).isEmpty()) { + compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i); + } + } + } + + } + + @Override + public boolean add(ItemStack itemStack) { + return this.add(-1, itemStack); + } + + @Override + public boolean add(int i, ItemStack itemStack) { + if (itemStack.isEmpty()) { + return false; + } else { + try { + if (itemStack.isDamaged()) { + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i >= 0) { + this.items.set(i, itemStack.copy()); + this.items.get(i).setPopTime(5); + itemStack.setCount(0); + return true; + } else if (this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + do { + j = itemStack.getCount(); + if (i == -1) { + itemStack.setCount(this.addResource(itemStack)); + } else { + itemStack.setCount(this.addResource(i, itemStack)); + } + } while(!itemStack.isEmpty() && itemStack.getCount() < j); + + if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { + itemStack.setCount(0); + return true; + } else { + return itemStack.getCount() < j; + } + } + } catch (Throwable var6) { + CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); + crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); + crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); + crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); + throw new ReportedException(crashReport); + } + } + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack) { + this.placeItemBackInInventory(itemStack, true); + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { + while(true) { + if (!itemStack.isEmpty()) { + int i = this.getSlotWithRemainingSpace(itemStack); + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i != -1) { + int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); + if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { + ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); + } + continue; + } + + this.player.drop(itemStack, false); + } + + return; + } + } + + @Override + public ItemStack removeItem(int rawIndex, final int j) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null + || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { + return ItemStack.EMPTY; + } + + return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); + } + + @Override + public void removeItem(ItemStack itemStack) { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (compartment.get(i) == itemStack) { + compartment.set(i, ItemStack.EMPTY); + break; + } + } + } + } + + @Override + public ItemStack removeItemNoUpdate(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); + + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + + return removed; + } + + @Override + public void setItem(int rawIndex, final ItemStack itemStack) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + this.player.drop(itemStack, true); + return; + } + + indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); + } + + @Override + public float getDestroySpeed(BlockState blockState) { + return this.items.get(this.selected).getDestroySpeed(blockState); + } + + @Override + public ListTag save(ListTag listTag) { + for (int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)i); + this.items.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.armor.size(); ++i) { + if (!this.armor.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 100)); + this.armor.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.offhand.size(); ++i) { + if (!this.offhand.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 150)); + this.offhand.get(i).save(compoundTag); + listTag.add(compoundTag); + } + } + + return listTag; + } + + @Override + public void load(ListTag listTag) { + this.items.clear(); + this.armor.clear(); + this.offhand.clear(); + + for(int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.of(compoundTag); + if (!itemstack.isEmpty()) { + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); + } + } + } + + } + + @Override + public int getContainerSize() { + return 45; + } + + @Override + public boolean isEmpty() { + return !contains(itemStack -> !itemStack.isEmpty()); + } + + @Override + public ItemStack getItem(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + return indexedCompartment.compartment().get(indexedCompartment.index()); + } + + @Override + public Component getName() { + return this.player.getName(); + } + + @Override + public ItemStack getArmor(int index) { + return this.armor.get(index); + } + + @Override + public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { + if (damage > 0.0F) { + damage /= 4.0F; + if (damage < 1.0F) { + damage = 1.0F; + } + + for (int index : armorIndices) { + ItemStack itemstack = this.armor.get(index); + if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { + itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); + } + } + } + } + + @Override + public void dropAll() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + ItemStack itemstack = compartment.get(i); + if (!itemstack.isEmpty()) { + this.player.drop(itemstack, true, false); + compartment.set(i, ItemStack.EMPTY); + } + } + } + } + + @Override + public void setChanged() { + super.setChanged(); + } + + @Override + public int getTimesChanged() { + return super.getTimesChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public boolean contains(ItemStack itemstack) { + return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem())); + } + + @Override + public boolean contains(TagKey tagKey) { + + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); + } + + @Override + public void replaceWith(Inventory inventory) { + Function getter; + + if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { + getter = specialPlayerInventory::getRawItem; + } else { + getter = inventory::getItem; + } + + for(int i = 0; i < this.getContainerSize(); ++i) { + this.setRawItem(i, getter.apply(i)); + } + + this.selected = inventory.selected; + } + + @Override + public void clearContent() { + for (NonNullList compartment : this.compartments) { + compartment.clear(); + } + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountSimpleStack(itemstack); + } + } + + @Override + public ItemStack removeFromSelected(boolean dropWholeStack) { + ItemStack itemstack = this.getSelected(); + return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); + } + +} diff --git a/pom.xml b/pom.xml index f7271e0f..175c7cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ plugin internal/v1_19_R3 internal/v1_20_R1 + internal/v1_20_R2 assembly From 9d715b61c09cd601f5e8506db19a744cc224cd48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 23:20:13 -0400 Subject: [PATCH 131/340] Bump maven-shade-plugin from 3.4.1 to 3.5.0 (#153) Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.1...maven-shade-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 175c7cf1..dd6a98e4 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ org.apache.maven.plugins - 3.4.1 + 3.5.0 From 21cd52c16696aa5e809314b0fb463600f8083732 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 22 Sep 2023 23:46:43 -0400 Subject: [PATCH 132/340] Fix probable NPE --- .../internal/v1_20_R2/PlayerDataManager.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java index 9d6ec286..0f3d5ce4 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java @@ -21,15 +21,15 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; -import java.lang.reflect.Field; -import java.util.logging.Logger; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.level.Level; @@ -45,6 +45,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.util.logging.Logger; + public class PlayerDataManager implements IPlayerDataManager { private @Nullable Field bukkitEntity; @@ -99,7 +102,18 @@ public PlayerDataManager() { return null; } - ServerPlayer entity = new ServerPlayer(server, worldServer, profile, null); + ClientInformation dummyInfo = new ClientInformation( + "en_us", + 1, // Reduce distance just in case. + ChatVisiblity.HIDDEN, // Don't accept chat. + false, + ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, + ServerPlayer.DEFAULT_MAIN_HAND, + true, + false // Don't list in player list (not that this player is in the list anyway). + ); + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. entity.getAdvancements().stopListening(); From db628786810d5f3f9e1e27357d07f1566a73c42c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:45:00 +0000 Subject: [PATCH 133/340] Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.0 to 3.5.1 (#159) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd6a98e4..5bd966ff 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ org.apache.maven.plugins - 3.5.0 + 3.5.1 From 3cf1aae89b32e0507ff417d1e8e5b9115909028a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 21:52:28 -0400 Subject: [PATCH 134/340] Bump actions/checkout from 3 to 4 (#158) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/draft_release.yml | 2 +- .github/workflows/external_release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9619117..b382ede8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 037c22e8..d9b480f8 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: # Fetch all history - used to assemble changelog. - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/external_release.yml b/.github/workflows/external_release.yml index 94ac2e1a..e748656b 100644 --- a/.github/workflows/external_release.yml +++ b/.github/workflows/external_release.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 From bfcf0233fa4a5b4b932d2a2a1b4e98980d86ab1d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 4 Oct 2023 22:15:35 -0400 Subject: [PATCH 135/340] Fix ender chests not being silent --- .../java/com/lishid/openinv/internal/IAnySilentContainer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 82b629f0..5758b2ae 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -158,7 +158,8 @@ default boolean isAnySilentContainer(@NotNull Block block) { * @return true if the type is a supported container */ default boolean isAnySilentContainer(@NotNull BlockState blockState) { - return blockState instanceof InventoryHolder holder && isAnySilentContainer(holder); + return blockState instanceof InventoryHolder holder && isAnySilentContainer(holder) + || blockState instanceof EnderChest; } /** From 28fc5aea7034d642d64f4fec1aec0df647c38d4a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 4 Oct 2023 22:18:38 -0400 Subject: [PATCH 136/340] Bump version to 4.4.0 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index eeee22d6..aaadf8c9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.0-SNAPSHOT + 4.4.0 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 2a690911..3795f125 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.0-SNAPSHOT + 4.4.0 openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 56acad93..0447d135 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.0-SNAPSHOT + 4.4.0 openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index acb7f214..78c656ab 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.0-SNAPSHOT + 4.4.0 openinvadapter1_20_R1 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index edfe39ef..aa2263a7 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.0-SNAPSHOT + 4.4.0 openinvadapter1_20_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index dbaccf94..179d2f3a 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.0-SNAPSHOT + 4.4.0 openinvplugincore diff --git a/pom.xml b/pom.xml index 5bd966ff..b52364e9 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.0-SNAPSHOT + 4.4.0 pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.0-SNAPSHOT + 4.4.0 openinvplugincore com.lishid compile - 4.4.0-SNAPSHOT + 4.4.0 com.lishid From deed277376a78c728ddc8bde3df007858efd793a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 4 Oct 2023 22:19:02 -0400 Subject: [PATCH 137/340] Bump version to 4.4.1-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index aaadf8c9..569ba295 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.0 + 4.4.1-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 3795f125..a9870520 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.0 + 4.4.1-SNAPSHOT openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 0447d135..5ad65229 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.0 + 4.4.1-SNAPSHOT openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index 78c656ab..e5550f57 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.0 + 4.4.1-SNAPSHOT openinvadapter1_20_R1 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index aa2263a7..e63be778 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.0 + 4.4.1-SNAPSHOT openinvadapter1_20_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index 179d2f3a..f5d9eb22 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.0 + 4.4.1-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index b52364e9..e25c8a9f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.0 + 4.4.1-SNAPSHOT pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.0 + 4.4.1-SNAPSHOT openinvplugincore com.lishid compile - 4.4.0 + 4.4.1-SNAPSHOT com.lishid From faf1d387e9fca963c4b36cb71b8ee9db2600719b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:32:32 +0000 Subject: [PATCH 138/340] Bump org.apache.maven.plugins:maven-dependency-plugin (#161) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e25c8a9f..51c2805b 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ --> maven-dependency-plugin org.apache.maven.plugins - 3.6.0 + 3.6.1 From 73edebff54e50ab3073f68dce42b3e6de929791f Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 12 Nov 2023 12:24:19 -0500 Subject: [PATCH 139/340] Fix loading world on Paper (#165) Unify load process to reduce duplicate code and risk of missed cases --- .../openinv/internal/v1_20_R2/OpenPlayer.java | 6 +- .../internal/v1_20_R2/PlayerDataManager.java | 121 ++++++++++++++---- 2 files changed, 95 insertions(+), 32 deletions(-) diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java index fc70decb..42398ea4 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java @@ -34,11 +34,7 @@ public OpenPlayer(CraftServer server, ServerPlayer entity) { @Override public void loadData() { - // See CraftPlayer#loadData - CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); - if (loaded != null) { - getHandle().readAdditionalSaveData(loaded); - } + PlayerDataManager.loadData(getHandle()); } @Override diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java index 0f3d5ce4..b8a3ec1b 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java @@ -21,7 +21,9 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; +import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -33,31 +35,47 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; +import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R2.CraftServer; +import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; 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.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; +import java.util.UUID; import java.util.logging.Logger; public class PlayerDataManager implements IPlayerDataManager { + private static boolean paper; + + static { + try { + Class.forName("io.papermc.paper.configuration.Configuration"); + paper = true; + } catch (ClassNotFoundException ignored) { + paper = false; + } + } + private @Nullable Field bukkitEntity; public PlayerDataManager() { try { bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); bukkitEntity = null; } @@ -77,7 +95,7 @@ public PlayerDataManager() { if (nmsPlayer == null) { // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); } return nmsPlayer; @@ -90,11 +108,6 @@ public PlayerDataManager() { return null; } - // Create a profile and entity to load the player data - // See net.minecraft.server.players.PlayerList#canPlayerLogin - // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); @@ -102,6 +115,30 @@ public PlayerDataManager() { return null; } + // Create a new ServerPlayer. + ServerPlayer entity = createNewPlayer(server, worldServer, offline); + + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + + // Try to load the player's data. + if (loadData(entity)) { + // If data is loaded successfully, return the Bukkit entity. + return entity.getBukkitEntity(); + } + + return null; + } + + private @NotNull ServerPlayer createNewPlayer( + @NotNull MinecraftServer server, + @NotNull ServerLevel worldServer, + @NotNull final OfflinePlayer offline) { + // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) + // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + ClientInformation dummyInfo = new ClientInformation( "en_us", 1, // Reduce distance just in case. @@ -115,37 +152,66 @@ public PlayerDataManager() { ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); - // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. - entity.getAdvancements().stopListening(); - try { injectPlayer(entity); } catch (IllegalAccessException e) { - e.printStackTrace(); + JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); } - // Load data. This also reads basic data into the player. + return entity; + } + + static boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData - CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player); if (loadedData == null) { // Exceptions with loading are logged by Mojang. - return null; + return false; } + // Read basic data into the player. + player.load(loadedData); // Also read "extra" data. - entity.readAdditionalSaveData(loadedData); + player.readAdditionalSaveData(loadedData); - if (entity.level() == null) { - // Paper: Move player to spawn - entity.spawnIn(null); + if (paper) { + // Paper: world is not loaded by ServerPlayer#load(CompoundTag). + parseWorld(player, loadedData); } - // Return the Bukkit entity. - return entity.getBukkitEntity(); + return true; + } + + private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + // Modern Bukkit world. + bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { + // Legacy Bukkit world. + bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(loadedData.getString("world")); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + return; + } + if (bukkitWorld == null) { + player.spawnIn(null); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } - void injectPlayer(ServerPlayer player) throws IllegalAccessException { + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; } @@ -155,9 +221,8 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException { bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); } - @NotNull @Override - public Player inject(@NotNull Player player) { + public @NotNull Player inject(@NotNull Player player) { try { ServerPlayer nmsPlayer = getHandle(player); if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { @@ -166,14 +231,16 @@ public Player inject(@NotNull Player player) { injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { - e.printStackTrace(); + JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); return player; } } - @Nullable @Override - public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { ServerPlayer nmsPlayer = getHandle(player); From bc7e13f245174088cac4a35515b27f5a45d02e6a Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 12 Nov 2023 12:34:32 -0500 Subject: [PATCH 140/340] Expand preserved old data (#166) * Fix session timestamps being erroneously updated * Fix vehicle possibly not being re-mounted --- .../openinv/internal/v1_19_R3/OpenPlayer.java | 83 +++++++++++++++++-- .../openinv/internal/v1_20_R1/OpenPlayer.java | 83 +++++++++++++++++-- .../openinv/internal/v1_20_R2/OpenPlayer.java | 83 +++++++++++++++++-- 3 files changed, 228 insertions(+), 21 deletions(-) diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java index 33a360e7..5a394b59 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java +++ b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java @@ -16,15 +16,21 @@ package com.lishid.openinv.internal.v1_19_R3; -import java.io.File; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.apache.logging.log4j.LogManager; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; public class OpenPlayer extends CraftPlayer { @@ -52,13 +58,9 @@ public void saveData() { setExtraData(playerData); if (!isOnline()) { - // Special case: save old vehicle data + // Preserve certain data when offline. CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + revertSpecialValues(playerData, oldData); } File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); @@ -71,4 +73,71 @@ public void saveData() { } } + private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + if (oldData == null) { + return; + } + + // Prevent vehicle deletion. + if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { + // See net.minecraft.server.players.PlayerList#save(ServerPlayer) + // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + try { + Tag attach = oldData.get("Attach"); + if (attach != null) { + newData.putUUID("Attach", NbtUtils.loadUUID(attach)); + } + } catch (IllegalArgumentException ignored) { + // Likely will not re-mount successfully, but at least the mount will not be deleted. + } + newData.put("Entity", oldData.getCompound("Entity")); + newData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // Container being null means the server implementation doesn't store this data. + if (oldContainer == null || newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @NotNull CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + } diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java b/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java index 9ebd79b2..6d70e3db 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java +++ b/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java @@ -17,14 +17,20 @@ package com.lishid.openinv.internal.v1_20_R1; import com.mojang.logging.LogUtils; -import java.io.File; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; public class OpenPlayer extends CraftPlayer { @@ -52,13 +58,9 @@ public void saveData() { setExtraData(playerData); if (!isOnline()) { - // Special case: save old vehicle data + // Preserve certain data when offline. CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + revertSpecialValues(playerData, oldData); } File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); @@ -71,4 +73,71 @@ public void saveData() { } } + private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + if (oldData == null) { + return; + } + + // Prevent vehicle deletion. + if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { + // See net.minecraft.server.players.PlayerList#save(ServerPlayer) + // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + try { + Tag attach = oldData.get("Attach"); + if (attach != null) { + newData.putUUID("Attach", NbtUtils.loadUUID(attach)); + } + } catch (IllegalArgumentException ignored) { + // Likely will not re-mount successfully, but at least the mount will not be deleted. + } + newData.put("Entity", oldData.getCompound("Entity")); + newData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // Container being null means the server implementation doesn't store this data. + if (oldContainer == null || newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @NotNull CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + } diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java index 42398ea4..4388d823 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java @@ -17,14 +17,20 @@ package com.lishid.openinv.internal.v1_20_R2; import com.mojang.logging.LogUtils; -import java.io.File; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.bukkit.craftbukkit.v1_20_R2.CraftServer; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; public class OpenPlayer extends CraftPlayer { @@ -48,13 +54,9 @@ public void saveData() { setExtraData(playerData); if (!isOnline()) { - // Special case: save old vehicle data + // Preserve certain data when offline. CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + revertSpecialValues(playerData, oldData); } File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); @@ -67,4 +69,71 @@ public void saveData() { } } + private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + if (oldData == null) { + return; + } + + // Prevent vehicle deletion. + if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { + // See net.minecraft.server.players.PlayerList#save(ServerPlayer) + // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + try { + Tag attach = oldData.get("Attach"); + if (attach != null) { + newData.putUUID("Attach", NbtUtils.loadUUID(attach)); + } + } catch (IllegalArgumentException ignored) { + // Likely will not re-mount successfully, but at least the mount will not be deleted. + } + newData.put("Entity", oldData.getCompound("Entity")); + newData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // Container being null means the server implementation doesn't store this data. + if (oldContainer == null || newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @NotNull CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + } From 5aa959f1acff135005b71f64bdc2b0581c34debf Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 12 Nov 2023 14:29:11 -0500 Subject: [PATCH 141/340] Bump version to 4.4.1 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 569ba295..c5b10d76 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.1-SNAPSHOT + 4.4.1 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index a9870520..2fa13eac 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.1-SNAPSHOT + 4.4.1 openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 5ad65229..865ee9de 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.1-SNAPSHOT + 4.4.1 openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index e5550f57..87ddcfc4 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.1-SNAPSHOT + 4.4.1 openinvadapter1_20_R1 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index e63be778..30e25560 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.1-SNAPSHOT + 4.4.1 openinvadapter1_20_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index f5d9eb22..1669ccdf 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.1-SNAPSHOT + 4.4.1 openinvplugincore diff --git a/pom.xml b/pom.xml index 51c2805b..0034d83a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.1-SNAPSHOT + 4.4.1 pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.1-SNAPSHOT + 4.4.1 openinvplugincore com.lishid compile - 4.4.1-SNAPSHOT + 4.4.1 com.lishid From 1829babe69638b65e00d5a64ffc2be02aac12f2d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 12 Nov 2023 14:29:31 -0500 Subject: [PATCH 142/340] Bump version to 4.4.2-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R1/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index c5b10d76..23ecaaaf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.1 + 4.4.2-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 2fa13eac..a80bd15a 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.1 + 4.4.2-SNAPSHOT openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 865ee9de..da09fe69 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.1 + 4.4.2-SNAPSHOT openinvadapter1_19_R3 diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R1/pom.xml index 87ddcfc4..0ac9f36b 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.1 + 4.4.2-SNAPSHOT openinvadapter1_20_R1 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index 30e25560..66fa0d97 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.1 + 4.4.2-SNAPSHOT openinvadapter1_20_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index 1669ccdf..47f2b9e9 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.1 + 4.4.2-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 0034d83a..b59f446a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.1 + 4.4.2-SNAPSHOT pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.1 + 4.4.2-SNAPSHOT openinvplugincore com.lishid compile - 4.4.1 + 4.4.2-SNAPSHOT com.lishid From c14d777519b871ab783d71a1c2015554de5e9781 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:51:58 +0000 Subject: [PATCH 143/340] Bump org.jetbrains:annotations from 24.0.1 to 24.1.0 (#170) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b59f446a..d55b7657 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ annotations org.jetbrains provided - 24.0.1 + 24.1.0 spigot-api From 79f53e90864b285eb523aff1d93416fd463b1630 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:02:30 -0500 Subject: [PATCH 144/340] Bump actions/github-script from 6 to 7 (#169) Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/automerge_dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml index a79866cd..0117ca41 100644 --- a/.github/workflows/automerge_dependabot.yml +++ b/.github/workflows/automerge_dependabot.yml @@ -15,7 +15,7 @@ jobs: # Note: this is directly from GitHub's example for using data from a triggering workflow: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow - name: 'Download artifact' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ From 859179d1fc91a3ac8666d06817ce36e65f6cb3ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:03:17 -0500 Subject: [PATCH 145/340] Bump actions/setup-java from 3 to 4 (#168) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b382ede8..1220e21a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' From 78354fdf68ed8a798906ad65bcc7f3c664f9b98e Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 29 Dec 2023 10:01:54 -0500 Subject: [PATCH 146/340] Add support for 1.20.4 (#173) --- internal/{v1_20_R1 => v1_20_R3}/pom.xml | 6 +- .../v1_20_R3}/AnySilentContainer.java | 9 +- .../internal/v1_20_R3}/OpenPlayer.java | 24 ++- .../internal/v1_20_R3}/PlayerDataManager.java | 145 ++++++++++++++---- .../internal/v1_20_R3}/SpecialEnderChest.java | 13 +- .../v1_20_R3}/SpecialPlayerInventory.java | 19 +-- .../com/lishid/openinv/InternalAccessor.java | 4 +- pom.xml | 2 +- 8 files changed, 153 insertions(+), 69 deletions(-) rename internal/{v1_20_R1 => v1_20_R3}/pom.xml (94%) rename internal/{v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1 => v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3}/AnySilentContainer.java (99%) rename internal/{v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1 => v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3}/OpenPlayer.java (85%) rename internal/{v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1 => v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3}/PlayerDataManager.java (59%) rename internal/{v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1 => v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3}/SpecialEnderChest.java (98%) rename internal/{v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1 => v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3}/SpecialPlayerInventory.java (99%) diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R3/pom.xml similarity index 94% rename from internal/v1_20_R1/pom.xml rename to internal/v1_20_R3/pom.xml index 0ac9f36b..45af58dc 100644 --- a/internal/v1_20_R1/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -26,13 +26,13 @@ 4.4.2-SNAPSHOT - openinvadapter1_20_R1 - OpenInvAdapter1_20_R1 + openinvadapter1_20_R3 + OpenInvAdapter1_20_R3 17 17 - 1.20.1-R0.1-SNAPSHOT + 1.20.4-R0.1-SNAPSHOT diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java similarity index 99% rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/AnySilentContainer.java rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java index 459f9693..ea492c70 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/AnySilentContainer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java @@ -14,13 +14,11 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R1; +package com.lishid.openinv.internal.v1_20_R3; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.util.ReflectionHelper; -import java.lang.reflect.Field; -import java.util.logging.Logger; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; @@ -49,11 +47,14 @@ import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.util.logging.Logger; + public class AnySilentContainer implements IAnySilentContainer { private @Nullable Field serverPlayerGameModeGameType; diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java similarity index 85% rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 6d70e3db..23ed6bb9 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R1; +package com.lishid.openinv.internal.v1_20_R3; import com.mojang.logging.LogUtils; import net.minecraft.Util; @@ -25,12 +25,13 @@ import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_20_R1.CraftServer; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; public class OpenPlayer extends CraftPlayer { @@ -40,11 +41,7 @@ public OpenPlayer(CraftServer server, ServerPlayer entity) { @Override public void loadData() { - // See CraftPlayer#loadData - CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); - if (loaded != null) { - getHandle().readAdditionalSaveData(loaded); - } + PlayerDataManager.loadData(getHandle()); } @Override @@ -63,11 +60,12 @@ public void saveData() { revertSpecialValues(playerData, oldData); } - File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); NbtIo.writeCompressed(playerData, file); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); - File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(file1, file, file2); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, file, backupFile); } catch (Exception e) { LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/PlayerDataManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java similarity index 59% rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/PlayerDataManager.java rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java index 710e1987..240fafa5 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/PlayerDataManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java @@ -14,47 +14,68 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R1; +package com.lishid.openinv.internal.v1_20_R3; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; -import java.lang.reflect.Field; -import java.util.logging.Logger; +import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_20_R1.CraftServer; -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.CraftContainer; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +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.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.util.UUID; +import java.util.logging.Logger; + public class PlayerDataManager implements IPlayerDataManager { + private static boolean paper; + + static { + try { + Class.forName("io.papermc.paper.configuration.Configuration"); + paper = true; + } catch (ClassNotFoundException ignored) { + paper = false; + } + } + private @Nullable Field bukkitEntity; public PlayerDataManager() { try { bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); + Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); bukkitEntity = null; } @@ -74,7 +95,7 @@ public PlayerDataManager() { if (nmsPlayer == null) { // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); } return nmsPlayer; @@ -87,11 +108,6 @@ public PlayerDataManager() { return null; } - // Create a profile and entity to load the player data - // See net.minecraft.server.players.PlayerList#canPlayerLogin - // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); @@ -99,39 +115,103 @@ public PlayerDataManager() { return null; } - ServerPlayer entity = new ServerPlayer(server, worldServer, profile); + // Create a new ServerPlayer. + ServerPlayer entity = createNewPlayer(server, worldServer, offline); // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. entity.getAdvancements().stopListening(); + // Try to load the player's data. + if (loadData(entity)) { + // If data is loaded successfully, return the Bukkit entity. + return entity.getBukkitEntity(); + } + + return null; + } + + private @NotNull ServerPlayer createNewPlayer( + @NotNull MinecraftServer server, + @NotNull ServerLevel worldServer, + @NotNull final OfflinePlayer offline) { + // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) + // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + + ClientInformation dummyInfo = new ClientInformation( + "en_us", + 1, // Reduce distance just in case. + ChatVisiblity.HIDDEN, // Don't accept chat. + false, + ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, + ServerPlayer.DEFAULT_MAIN_HAND, + true, + false // Don't list in player list (not that this player is in the list anyway). + ); + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); + try { injectPlayer(entity); } catch (IllegalAccessException e) { - e.printStackTrace(); + JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); } - // Load data. This also reads basic data into the player. + return entity; + } + + static boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData - CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player); if (loadedData == null) { // Exceptions with loading are logged by Mojang. - return null; + return false; } + // Read basic data into the player. + player.load(loadedData); // Also read "extra" data. - entity.readAdditionalSaveData(loadedData); + player.readAdditionalSaveData(loadedData); - if (entity.level() == null) { - // Paper: Move player to spawn - entity.spawnIn(null); + if (paper) { + // Paper: world is not loaded by ServerPlayer#load(CompoundTag). + parseWorld(player, loadedData); } - // Return the Bukkit entity. - return entity.getBukkitEntity(); + return true; + } + + private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + // Modern Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + return; + } + if (bukkitWorld == null) { + player.spawnIn(null); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } - void injectPlayer(ServerPlayer player) throws IllegalAccessException { + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; } @@ -141,9 +221,8 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException { bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); } - @NotNull @Override - public Player inject(@NotNull Player player) { + public @NotNull Player inject(@NotNull Player player) { try { ServerPlayer nmsPlayer = getHandle(player); if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { @@ -152,14 +231,16 @@ public Player inject(@NotNull Player player) { injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { - e.printStackTrace(); + JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); return player; } } - @Nullable @Override - public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { ServerPlayer nmsPlayer = getHandle(player); diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialEnderChest.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java similarity index 98% rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialEnderChest.java rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java index 3c66f9cd..941e7979 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialEnderChest.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java @@ -14,12 +14,9 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R1; +package com.lishid.openinv.internal.v1_20_R3; import com.lishid.openinv.internal.ISpecialEnderChest; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -33,13 +30,17 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { private final CraftInventory inventory; diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialPlayerInventory.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java similarity index 99% rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialPlayerInventory.java rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java index aef0914a..2ce13a58 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialPlayerInventory.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java @@ -14,16 +14,10 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R1; +package com.lishid.openinv.internal.v1_20_R3; import com.google.common.collect.ImmutableList; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; @@ -47,13 +41,20 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { private final CraftInventory inventory; diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index 2bf529a3..ff2c716a 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -22,11 +22,12 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; -import java.lang.reflect.Constructor; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.Constructor; + class InternalAccessor { private final @NotNull Plugin plugin; @@ -66,6 +67,7 @@ public String getReleasesLink() { case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10"; case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2"; case "v1_18_R2", "v1_19_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; + case "v1_20_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; default -> "https://github.com/Jikoo/OpenInv/releases"; }; } diff --git a/pom.xml b/pom.xml index d55b7657..6126bc82 100644 --- a/pom.xml +++ b/pom.xml @@ -41,8 +41,8 @@ api plugin internal/v1_19_R3 - internal/v1_20_R1 internal/v1_20_R2 + internal/v1_20_R3 assembly From 9a7623c209bc1525fa7c9d39903764364a3f79f3 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 29 Dec 2023 10:04:28 -0500 Subject: [PATCH 147/340] Bump version to 4.4.2 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 23ecaaaf..13de0875 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.2-SNAPSHOT + 4.4.2 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index a80bd15a..e134b932 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.2-SNAPSHOT + 4.4.2 openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index da09fe69..209a7355 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.2-SNAPSHOT + 4.4.2 openinvadapter1_19_R3 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index 66fa0d97..6375e814 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.2-SNAPSHOT + 4.4.2 openinvadapter1_20_R2 diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 45af58dc..6c90814d 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.2-SNAPSHOT + 4.4.2 openinvadapter1_20_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index 47f2b9e9..b24296bb 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.2-SNAPSHOT + 4.4.2 openinvplugincore diff --git a/pom.xml b/pom.xml index 6126bc82..68ec75fd 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.2-SNAPSHOT + 4.4.2 pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.2-SNAPSHOT + 4.4.2 openinvplugincore com.lishid compile - 4.4.2-SNAPSHOT + 4.4.2 com.lishid From 48d044af8c494802bb8c47ea89fe097fe13d96a6 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 29 Dec 2023 10:05:02 -0500 Subject: [PATCH 148/340] Bump version to 4.4.3-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 13de0875..e4024afe 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.2 + 4.4.3-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index e134b932..5f8e71d2 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.2 + 4.4.3-SNAPSHOT openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 209a7355..3b10d676 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.2 + 4.4.3-SNAPSHOT openinvadapter1_19_R3 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index 6375e814..440c978b 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.2 + 4.4.3-SNAPSHOT openinvadapter1_20_R2 diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 6c90814d..8317b57e 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.2 + 4.4.3-SNAPSHOT openinvadapter1_20_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index b24296bb..e094ccbc 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.2 + 4.4.3-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 68ec75fd..4b770473 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.2 + 4.4.3-SNAPSHOT pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.2 + 4.4.3-SNAPSHOT openinvplugincore com.lishid compile - 4.4.2 + 4.4.3-SNAPSHOT com.lishid From 2621181c5dc08f9472e103d52febbf23fb6e0711 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 18:07:35 +0000 Subject: [PATCH 149/340] Bump org.apache.maven.plugins:maven-compiler-plugin (#178) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b770473..902307de 100644 --- a/pom.xml +++ b/pom.xml @@ -151,7 +151,7 @@ maven-compiler-plugin org.apache.maven.plugins - 3.11.0 + 3.12.1 From 59d358d9076620cd4558119d9857a9644f529aa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 16:26:27 -0500 Subject: [PATCH 150/340] Bump actions/upload-artifact from 3 to 4 (#176) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- .github/workflows/pull_request.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1220e21a..e07fc424 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,13 +31,13 @@ jobs: # Upload artifacts - name: Upload Distributable Jar id: upload-final - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: dist path: ./target/OpenInv.jar - name: Upload API Jar id: upload-api - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: api path: ./api/target/openinvapi*.jar diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 869fb27b..f2dbf2f8 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,7 +16,7 @@ jobs: run: | mkdir -p ./pr echo ${{ github.event.number }} > ./pr/pr_number - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: pr_number path: pr/ From 0ae9982ce5ea8cd7a4f65aae5686b32a3c02b45f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 16:26:41 -0500 Subject: [PATCH 151/340] Bump actions/download-artifact from 3 to 4 (#177) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index d9b480f8..e3f954eb 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -21,7 +21,7 @@ jobs: run: bash ./scripts/set_release_env.sh - name: Download Artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: dist path: dist From c8fee822917841f0a9c7cf265f1d66ec94563512 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 13:02:28 -0500 Subject: [PATCH 152/340] Bump pascalgn/automerge-action from 0.15.6 to 0.16.2 (#179) Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.15.6 to 0.16.2. - [Release notes](https://github.com/pascalgn/automerge-action/releases) - [Commits](https://github.com/pascalgn/automerge-action/compare/v0.15.6...v0.16.2) --- updated-dependencies: - dependency-name: pascalgn/automerge-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/automerge_dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml index 0117ca41..d744a689 100644 --- a/.github/workflows/automerge_dependabot.yml +++ b/.github/workflows/automerge_dependabot.yml @@ -47,7 +47,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" pull-request-number: "${{ env.PR_NUMBER }}" - name: Merge - uses: pascalgn/automerge-action@v0.15.6 + uses: pascalgn/automerge-action@v0.16.2 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "dependencies,java" From c102e6650e57d8d1c1847219b8660e07a0e35f43 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 8 Feb 2024 13:07:23 -0500 Subject: [PATCH 153/340] Fix game type not loading (#180) --- .../com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java | 6 ++++-- .../lishid/openinv/internal/v1_19_R3/PlayerDataManager.java | 6 ++++-- .../lishid/openinv/internal/v1_20_R2/PlayerDataManager.java | 2 ++ .../lishid/openinv/internal/v1_20_R3/PlayerDataManager.java | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java index 5a394b59..347d9cb9 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java +++ b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java @@ -41,9 +41,11 @@ public OpenPlayer(CraftServer server, ServerPlayer entity) { @Override public void loadData() { // See CraftPlayer#loadData - CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle()); + ServerPlayer serverPlayer = getHandle(); + CompoundTag loaded = this.server.getHandle().playerIo.load(serverPlayer); if (loaded != null) { - getHandle().readAdditionalSaveData(loaded); + serverPlayer.readAdditionalSaveData(loaded); + serverPlayer.loadGameTypes(loaded); } } diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java index 1897cd01..ec14a19a 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java +++ b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java @@ -21,8 +21,6 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; -import java.lang.reflect.Field; -import java.util.logging.Logger; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; @@ -47,6 +45,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.util.logging.Logger; + public class PlayerDataManager implements IPlayerDataManager { private @Nullable Field bukkitEntity; @@ -123,6 +124,7 @@ public PlayerDataManager() { // Also read "extra" data. entity.readAdditionalSaveData(loadedData); + entity.loadGameTypes(loadedData); if (entity.level == null) { // Paper: Move player to spawn diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java index b8a3ec1b..f53c0192 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java @@ -177,6 +177,8 @@ static boolean loadData(@NotNull ServerPlayer player) { player.load(loadedData); // Also read "extra" data. player.readAdditionalSaveData(loadedData); + // Game type settings are also loaded separately. + player.loadGameTypes(loadedData); if (paper) { // Paper: world is not loaded by ServerPlayer#load(CompoundTag). diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java index 240fafa5..9d02c87b 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java @@ -177,6 +177,8 @@ static boolean loadData(@NotNull ServerPlayer player) { player.load(loadedData); // Also read "extra" data. player.readAdditionalSaveData(loadedData); + // Game type settings are also loaded separately. + player.loadGameTypes(loadedData); if (paper) { // Paper: world is not loaded by ServerPlayer#load(CompoundTag). From 176272a4b94babbbd46e1fa4214726cf274cbeb5 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 8 Feb 2024 13:11:18 -0500 Subject: [PATCH 154/340] Bump version to 4.4.3 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index e4024afe..3f2fbaef 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.3-SNAPSHOT + 4.4.3 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 5f8e71d2..3989b254 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.3-SNAPSHOT + 4.4.3 openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index 3b10d676..db7f8972 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.3-SNAPSHOT + 4.4.3 openinvadapter1_19_R3 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index 440c978b..2dfbef27 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.3-SNAPSHOT + 4.4.3 openinvadapter1_20_R2 diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 8317b57e..860958aa 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.3-SNAPSHOT + 4.4.3 openinvadapter1_20_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index e094ccbc..ed8d976c 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.3-SNAPSHOT + 4.4.3 openinvplugincore diff --git a/pom.xml b/pom.xml index 902307de..61aa0dd8 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.3-SNAPSHOT + 4.4.3 pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.3-SNAPSHOT + 4.4.3 openinvplugincore com.lishid compile - 4.4.3-SNAPSHOT + 4.4.3 com.lishid From 70c1ecfc09cfbb3d007f98804da98da50431d44c Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 8 Feb 2024 13:11:46 -0500 Subject: [PATCH 155/340] Bump version to 4.4.4-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_19_R3/pom.xml | 2 +- internal/v1_20_R2/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 3f2fbaef..f7343f75 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.3 + 4.4.4-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 3989b254..c47b7ff9 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.3 + 4.4.4-SNAPSHOT openinvassembly diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_19_R3/pom.xml index db7f8972..029f172a 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_19_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.3 + 4.4.4-SNAPSHOT openinvadapter1_19_R3 diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml index 2dfbef27..5bdb0fd8 100644 --- a/internal/v1_20_R2/pom.xml +++ b/internal/v1_20_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.3 + 4.4.4-SNAPSHOT openinvadapter1_20_R2 diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 860958aa..e9320a6b 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.3 + 4.4.4-SNAPSHOT openinvadapter1_20_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index ed8d976c..e8f7e890 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.3 + 4.4.4-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 61aa0dd8..c2cd347b 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.3 + 4.4.4-SNAPSHOT pom @@ -85,13 +85,13 @@ openinvapi com.lishid compile - 4.4.3 + 4.4.4-SNAPSHOT openinvplugincore com.lishid compile - 4.4.3 + 4.4.4-SNAPSHOT com.lishid From 6f8a6e868a7e1df0b3863152fa5d890a8db5075b Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 16 Feb 2024 22:41:01 -0500 Subject: [PATCH 156/340] Improve compatibility with custom NBT (#183) --- .../openinv/internal/v1_19_R3/OpenPlayer.java | 83 ++++++++++++------ .../openinv/internal/v1_20_R2/OpenPlayer.java | 84 ++++++++++++------ .../openinv/internal/v1_20_R3/OpenPlayer.java | 85 +++++++++++++------ 3 files changed, 177 insertions(+), 75 deletions(-) diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java index 347d9cb9..34c0891f 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java +++ b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java @@ -19,7 +19,6 @@ import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; @@ -27,13 +26,48 @@ import org.apache.logging.log4j.LogManager; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Set; public class OpenPlayer extends CraftPlayer { + private static final Set RESET_TAGS = Set.of( + // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + "ActiveEffects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @@ -56,12 +90,13 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); setExtraData(playerData); - if (!isOnline()) { - // Preserve certain data when offline. - CompoundTag oldData = worldNBTStorage.load(player); + if (oldData != null) { + // Revert certain special data values when offline. revertSpecialValues(playerData, oldData); } @@ -75,27 +110,22 @@ public void saveData() { } } - private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { - return; + return new CompoundTag(); } - // Prevent vehicle deletion. - if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { - // See net.minecraft.server.players.PlayerList#save(ServerPlayer) - // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - try { - Tag attach = oldData.get("Attach"); - if (attach != null) { - newData.putUUID("Attach", NbtUtils.loadUUID(attach)); - } - } catch (IllegalArgumentException ignored) { - // Likely will not re-mount successfully, but at least the mount will not be deleted. - } - newData.put("Entity", oldData.getCompound("Entity")); - newData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); @@ -111,8 +141,8 @@ private void copyValue( CompoundTag oldContainer = getTag(source, container, CompoundTag.class); CompoundTag newContainer = getTag(target, container, CompoundTag.class); - // Container being null means the server implementation doesn't store this data. - if (oldContainer == null || newContainer == null) { + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { return; } @@ -121,9 +151,12 @@ private void copyValue( } private @Nullable T getTag( - @NotNull CompoundTag container, + @Nullable CompoundTag container, @NotNull String key, @NotNull Class dataType) { + if (container == null) { + return null; + } Tag value = container.get(key); if (value == null || !dataType.isAssignableFrom(value.getClass())) { return null; diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java index 4388d823..33677e0d 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java @@ -20,20 +20,55 @@ import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.bukkit.craftbukkit.v1_20_R2.CraftServer; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Set; public class OpenPlayer extends CraftPlayer { + private static final Set RESET_TAGS = Set.of( + // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + "ActiveEffects", // Backwards compat: Renamed from 1.19 + "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @@ -50,12 +85,13 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); setExtraData(playerData); - if (!isOnline()) { - // Preserve certain data when offline. - CompoundTag oldData = worldNBTStorage.load(player); + if (oldData != null) { + // Revert certain special data values when offline. revertSpecialValues(playerData, oldData); } @@ -69,27 +105,22 @@ public void saveData() { } } - private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { - return; + return new CompoundTag(); } - // Prevent vehicle deletion. - if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { - // See net.minecraft.server.players.PlayerList#save(ServerPlayer) - // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - try { - Tag attach = oldData.get("Attach"); - if (attach != null) { - newData.putUUID("Attach", NbtUtils.loadUUID(attach)); - } - } catch (IllegalArgumentException ignored) { - // Likely will not re-mount successfully, but at least the mount will not be deleted. - } - newData.put("Entity", oldData.getCompound("Entity")); - newData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); @@ -105,8 +136,8 @@ private void copyValue( CompoundTag oldContainer = getTag(source, container, CompoundTag.class); CompoundTag newContainer = getTag(target, container, CompoundTag.class); - // Container being null means the server implementation doesn't store this data. - if (oldContainer == null || newContainer == null) { + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { return; } @@ -115,9 +146,12 @@ private void copyValue( } private @Nullable T getTag( - @NotNull CompoundTag container, + @Nullable CompoundTag container, @NotNull String key, @NotNull Class dataType) { + if (container == null) { + return null; + } Tag value = container.get(key); if (value == null || !dataType.isAssignableFrom(value.getClass())) { return null; diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 23ed6bb9..08017f9f 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -20,21 +20,56 @@ import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.bukkit.craftbukkit.v1_20_R3.CraftServer; import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Set; public class OpenPlayer extends CraftPlayer { + private static final Set RESET_TAGS = Set.of( + // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + "ActiveEffects", // Backwards compat: Renamed from 1.19 + "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @@ -51,12 +86,13 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); setExtraData(playerData); - if (!isOnline()) { - // Preserve certain data when offline. - CompoundTag oldData = worldNBTStorage.load(player); + if (oldData != null) { + // Revert certain special data values when offline. revertSpecialValues(playerData, oldData); } @@ -71,27 +107,23 @@ public void saveData() { } } - private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { - return; + return new CompoundTag(); } - // Prevent vehicle deletion. - if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { - // See net.minecraft.server.players.PlayerList#save(ServerPlayer) - // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - try { - Tag attach = oldData.get("Attach"); - if (attach != null) { - newData.putUUID("Attach", NbtUtils.loadUUID(attach)); - } - } catch (IllegalArgumentException ignored) { - // Likely will not re-mount successfully, but at least the mount will not be deleted. - } - newData.put("Entity", oldData.getCompound("Entity")); - newData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); @@ -107,8 +139,8 @@ private void copyValue( CompoundTag oldContainer = getTag(source, container, CompoundTag.class); CompoundTag newContainer = getTag(target, container, CompoundTag.class); - // Container being null means the server implementation doesn't store this data. - if (oldContainer == null || newContainer == null) { + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { return; } @@ -117,9 +149,12 @@ private void copyValue( } private @Nullable T getTag( - @NotNull CompoundTag container, + @Nullable CompoundTag container, @NotNull String key, @NotNull Class dataType) { + if (container == null) { + return null; + } Tag value = container.get(key); if (value == null || !dataType.isAssignableFrom(value.getClass())) { return null; From 0d5fb6a0ef59dc1987ed63bfeebc2aab6c75a28d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:40:32 +0000 Subject: [PATCH 157/340] Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.1 to 3.5.2 (#186) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c2cd347b..ca361e01 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ org.apache.maven.plugins - 3.5.1 + 3.5.2 From d887ff8eae2d1e89f37deef7eb403f44639dd430 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:14:49 -0500 Subject: [PATCH 158/340] Bump hmarr/auto-approve-action from 3.2.1 to 4.0.0 (#187) Bumps [hmarr/auto-approve-action](https://github.com/hmarr/auto-approve-action) from 3.2.1 to 4.0.0. - [Release notes](https://github.com/hmarr/auto-approve-action/releases) - [Commits](https://github.com/hmarr/auto-approve-action/compare/v3.2.1...v4.0.0) --- updated-dependencies: - dependency-name: hmarr/auto-approve-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/automerge_dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml index d744a689..83e0734b 100644 --- a/.github/workflows/automerge_dependabot.yml +++ b/.github/workflows/automerge_dependabot.yml @@ -42,7 +42,7 @@ jobs: echo "PR_NUMBER=$(cat pr_number)" >> "$GITHUB_ENV" - name: Approve - uses: hmarr/auto-approve-action@v3.2.1 + uses: hmarr/auto-approve-action@v4.0.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" pull-request-number: "${{ env.PR_NUMBER }}" From 52c90e1cea22f50a6fda98d16bb89f3f1cc1080d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:20:43 +0000 Subject: [PATCH 159/340] Bump org.apache.maven.plugins:maven-assembly-plugin from 3.6.0 to 3.7.1 (#189) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ca361e01..3411d9c5 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ maven-assembly-plugin org.apache.maven.plugins - 3.6.0 + 3.7.1 From c3f7eaebf463aaee3f3be53a645037718bacc8c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:21:51 +0000 Subject: [PATCH 160/340] Bump org.apache.maven.plugins:maven-compiler-plugin (#190) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3411d9c5..521374e5 100644 --- a/pom.xml +++ b/pom.xml @@ -151,7 +151,7 @@ maven-compiler-plugin org.apache.maven.plugins - 3.12.1 + 3.13.0 From b6d61d4cc927bfc7626a2848e4be230286a55d6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:03:00 -0400 Subject: [PATCH 161/340] Bump softprops/action-gh-release from 0.1.15 to 2.0.4 (#191) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 0.1.15 to 2.0.4. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v0.1.15...v2.0.4) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index e3f954eb..07e97a5f 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -28,7 +28,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v0.1.15 + uses: softprops/action-gh-release@v2.0.4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 2ed23b24e5b2ff04b7edcee6a23b5ede393dfd7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 17:11:09 +0000 Subject: [PATCH 162/340] Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.2 to 3.5.3 (#195) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 521374e5..1af16004 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ org.apache.maven.plugins - 3.5.2 + 3.5.3 From 80d3fb72e6e8346a3db602196478e4dd7e80e542 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 7 May 2024 20:59:28 -0400 Subject: [PATCH 163/340] Update to 1.20.5/1.20.6 (#193) * Move to API method for shulker expansion check * Update to 1.20.6 * Drop 1.20.2 support * Fix containment check * Drop 1.19.4 to compile without redoing Actions --- .github/workflows/ci.yml | 2 +- .../openinv/internal/IAnySilentContainer.java | 32 +- .../internal/v1_19_R3/AnySilentContainer.java | 244 ------ .../openinv/internal/v1_19_R3/OpenPlayer.java | 178 ---- .../internal/v1_19_R3/PlayerDataManager.java | 267 ------ .../v1_19_R3/SpecialPlayerInventory.java | 806 ------------------ internal/v1_20_R2/pom.xml | 81 -- .../internal/v1_20_R2/SpecialEnderChest.java | 350 -------- .../internal/v1_20_R3/AnySilentContainer.java | 3 +- .../v1_20_R3/SpecialPlayerInventory.java | 2 +- internal/{v1_19_R3 => v1_20_R4}/pom.xml | 10 +- .../v1_20_R4}/AnySilentContainer.java | 48 +- .../internal/v1_20_R4}/OpenPlayer.java | 25 +- .../internal/v1_20_R4}/PlayerDataManager.java | 18 +- .../internal/v1_20_R4}/SpecialEnderChest.java | 22 +- .../v1_20_R4}/SpecialPlayerInventory.java | 104 +-- .../com/lishid/openinv/InternalAccessor.java | 1 + pom.xml | 5 +- 18 files changed, 112 insertions(+), 2086 deletions(-) delete mode 100644 internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java delete mode 100644 internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java delete mode 100644 internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java delete mode 100644 internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialPlayerInventory.java delete mode 100644 internal/v1_20_R2/pom.xml delete mode 100644 internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java rename internal/{v1_19_R3 => v1_20_R4}/pom.xml (90%) rename internal/{v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2 => v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4}/AnySilentContainer.java (82%) rename internal/{v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2 => v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4}/OpenPlayer.java (86%) rename internal/{v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2 => v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4}/PlayerDataManager.java (95%) rename internal/{v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3 => v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4}/SpecialEnderChest.java (94%) rename internal/{v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2 => v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4}/SpecialPlayerInventory.java (88%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e07fc424..8d26f015 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' cache: 'maven' # Install Spigot dependencies if necessary. diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 5758b2ae..9c53d825 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -23,6 +23,7 @@ import org.bukkit.block.EnderChest; import org.bukkit.block.ShulkerBox; import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; import org.bukkit.block.data.type.Chest; import org.bukkit.entity.Cat; import org.bukkit.entity.Player; @@ -80,11 +81,9 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { return block.getRelative(0, 1, 0).getType().isOccluding(); } - // Shulker boxes require 1/2 a block clear in the direction they open. - if (blockState instanceof ShulkerBox shulker) { - if (isShulkerBlocked(shulker)) { - return true; - } + // Shulker boxes require half a block clear in the direction they open. + if (blockState instanceof ShulkerBox) { + return isShulkerBlocked(block); } if (!(blockState instanceof org.bukkit.block.Chest)) { @@ -124,10 +123,29 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { /** * Check if a {@link ShulkerBox} cannot be opened under ordinary circumstances. * + * @deprecated Use {@link #isShulkerBlocked(Block)}. * @param shulkerBox the shulker box container * @return whether the container is blocked */ - boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox); + @Deprecated(since = "4.4.4", forRemoval = true) + default boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { + return isShulkerBlocked(shulkerBox.getBlock()); + } + + /** + * Check if a shulker box block cannot be opened under ordinary circumstances. + * + * @param shulkerBox the shulker box block + * @return whether the container is blocked + */ + default boolean isShulkerBlocked(@NotNull Block shulkerBox) { + Directional directional = (Directional) shulkerBox.getBlockData(); + BlockFace facing = directional.getFacing(); + BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1); + box.expand(facing, 0.5); + box.shift(facing.getOppositeFace().getDirection()); + return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box); + } /** * Check if a chest cannot be opened under ordinary circumstances. @@ -138,7 +156,7 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { default boolean isChestBlocked(@NotNull Block chest) { org.bukkit.block.Block relative = chest.getRelative(0, 1, 0); return relative.getType().isOccluding() - || chest.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0; + || !chest.getWorld().getNearbyEntities(BoundingBox.of(relative), Cat.class::isInstance).isEmpty(); } /** diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java deleted file mode 100644 index b78bb393..00000000 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/AnySilentContainer.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R3; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.util.ReflectionHelper; -import java.lang.reflect.Field; -import java.util.logging.Level; -import java.util.logging.Logger; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.entity.monster.Shulker; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.ShulkerBoxBlock; -import net.minecraft.world.level.block.TrappedChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class AnySilentContainer implements IAnySilentContainer { - - private @Nullable Field serverPlayerGameModeGameType; - - public AnySilentContainer() { - try { - try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); - this.serverPlayerGameModeGameType.setAccessible(true); - } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); - logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); - logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); - // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. - this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); - } - } catch (SecurityException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to directly write player game mode! SilentContainer will fail."); - logger.log(Level.WARNING, "Error obtaining GameType field", e); - } - } - - @Override - public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { - org.bukkit.World bukkitWorld = shulkerBox.getWorld(); - if (!(bukkitWorld instanceof CraftWorld)) { - bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); - } - - if (!(bukkitWorld instanceof CraftWorld craftWorld)) { - Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); - OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception); - return false; - } - - final ServerLevel world = craftWorld.getHandle(); - final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); - final BlockEntity tile = world.getBlockEntity(blockPosition); - - if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) - || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { - return false; - } - - BlockState blockState = world.getBlockState(blockPosition); - - // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen - AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) - .move(blockPosition) - .deflate(1.0E-6D); - return !world.noCollision(boundingBox); - } - - @Override - public boolean activateContainer( - @NotNull final Player bukkitPlayer, - final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - final ServerLevel level = player.getLevel(); - final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final BlockEntity blockEntity = level.getBlockEntity(blockPos); - - if (blockEntity == null) { - return false; - } - - if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock - PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); - enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); - int rows = enderChest.getContainerSize() / 9; - return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable(("container.enderchest")))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(blockEntity instanceof MenuProvider menuProvider)) { - return false; - } - - BlockState blockState = level.getBlockState(blockPos); - Block block = blockState.getBlock(); - - if (block instanceof ChestBlock chestBlock) { - - // boolean flag: do not check if chest is blocked - menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); - - if (menuProvider == null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - if (block instanceof TrappedChestBlock) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof ShulkerBoxBlock) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BarrelBlock) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { - player.openMenu(menuProvider); - return true; - } - - // SilentChest requires access to setting players' game mode directly. - if (this.serverPlayerGameModeGameType == null) { - return false; - } - - if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { - if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - player.openMenu(menuProvider); - this.forceGameType(player, gameType); - return true; - } - - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { - return; - } - - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); - - // Force game mode change without informing plugins or players. - // Regular game mode set calls GameModeChangeEvent and is cancellable. - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - - // ServerPlayer#closeContainer cannot be called without entering an - // infinite loop because this method is called during inventory close. - // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent - player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); - // From ServerPlayer#closeContainer - player.doCloseContainer(); - // Regular inventory close will handle the rest - packet sending, etc. - - // Revert forced game mode. - this.forceGameType(player, gameType); - } - - private void forceGameType(final ServerPlayer player, final GameType gameMode) { - if (this.serverPlayerGameModeGameType == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - this.serverPlayerGameModeGameType.setAccessible(true); - this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } - -} diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java deleted file mode 100644 index 34c0891f..00000000 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R3; - -import net.minecraft.Util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.storage.PlayerDataStorage; -import org.apache.logging.log4j.LogManager; -import org.bukkit.craftbukkit.v1_19_R3.CraftServer; -import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.Set; - -public class OpenPlayer extends CraftPlayer { - - private static final Set RESET_TAGS = Set.of( - // net.minecraft.world.Entity#saveWithoutId(CompoundTag) - "CustomName", - "CustomNameVisible", - "Silent", - "NoGravity", - "Glowing", - "TicksFrozen", - "HasVisualFire", - "Tags", - "Passengers", - // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - "warden_spawn_tracker", - "enteredNetherPosition", - "SpawnX", - "SpawnY", - "SpawnZ", - "SpawnForced", - "SpawnAngle", - "SpawnDimension", - // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - "ShoulderEntityLeft", - "ShoulderEntityRight", - "LastDeathLocation", - // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - "ActiveEffects", - "SleepingX", - "SleepingY", - "SleepingZ", - "Brain" - ); - - public OpenPlayer(CraftServer server, ServerPlayer entity) { - super(server, entity); - } - - @Override - public void loadData() { - // See CraftPlayer#loadData - ServerPlayer serverPlayer = getHandle(); - CompoundTag loaded = this.server.getHandle().playerIo.load(serverPlayer); - if (loaded != null) { - serverPlayer.readAdditionalSaveData(loaded); - serverPlayer.loadGameTypes(loaded); - } - } - - @Override - public void saveData() { - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); - CompoundTag playerData = getWritableTag(oldData); - playerData = player.saveWithoutId(playerData); - setExtraData(playerData); - - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); - NbtIo.writeCompressed(playerData, file); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); - File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(file1, file, file2); - } catch (Exception e) { - LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); - } - } - - @Contract("null -> new") - private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { - if (oldData == null) { - return new CompoundTag(); - } - - // Copy old data. This is a deep clone, so operating on it should be safe. - oldData = oldData.copy(); - - // Remove vanilla/server data that is not written every time. - oldData.getAllKeys().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); - - return oldData; - } - - private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { - // Revert automatic updates to play timestamps. - copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); - } - - private void copyValue( - @NotNull CompoundTag source, - @NotNull CompoundTag target, - @NotNull String container, - @NotNull String key, - @NotNull Class tagType) { - CompoundTag oldContainer = getTag(source, container, CompoundTag.class); - CompoundTag newContainer = getTag(target, container, CompoundTag.class); - - // New container being null means the server implementation doesn't store this data. - if (newContainer == null) { - return; - } - - // If old tag exists, copy it to new location, removing otherwise. - setTag(newContainer, key, getTag(oldContainer, key, tagType)); - } - - private @Nullable T getTag( - @Nullable CompoundTag container, - @NotNull String key, - @NotNull Class dataType) { - if (container == null) { - return null; - } - Tag value = container.get(key); - if (value == null || !dataType.isAssignableFrom(value.getClass())) { - return null; - } - return dataType.cast(value); - } - - private void setTag( - @NotNull CompoundTag container, - @NotNull String key, - @Nullable T data) { - if (data == null) { - container.remove(key); - } else { - container.put(key, data); - } - } - -} diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java deleted file mode 100644 index ec14a19a..00000000 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/PlayerDataManager.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R3; - -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IPlayerDataManager; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.OpenInventoryView; -import com.mojang.authlib.GameProfile; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.Vec3; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_19_R3.CraftServer; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -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.CraftContainer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.logging.Logger; - -public class PlayerDataManager implements IPlayerDataManager { - - private @Nullable Field bukkitEntity; - - public PlayerDataManager() { - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); - logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded."); - logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); - bukkitEntity = null; - } - } - - public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - ServerPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); - } - - return nmsPlayer; - } - - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - - // Create a profile and entity to load the player data - // See net.minecraft.server.players.PlayerList#canPlayerLogin - // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - ServerLevel worldServer = server.getLevel(Level.OVERWORLD); - - if (worldServer == null) { - return null; - } - - ServerPlayer entity = new ServerPlayer(server, worldServer, profile); - - // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. - entity.getAdvancements().stopListening(); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - // Load data. This also reads basic data into the player. - // See CraftPlayer#loadData - CompoundTag loadedData = server.getPlayerList().playerIo.load(entity); - - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return null; - } - - // Also read "extra" data. - entity.readAdditionalSaveData(loadedData); - entity.loadGameTypes(loadedData); - - if (entity.level == null) { - // Paper: Move player to spawn - // SPIGOT-7340: Cannot call ServerPlayer#spawnIn with a null world - ServerLevel level = null; - Vec3 position = null; - if (entity.getRespawnDimension() != null) { - level = entity.server.getLevel(entity.getRespawnDimension()); - if (level != null && entity.getRespawnPosition() != null) { - position = net.minecraft.world.entity.player.Player.findRespawnPositionAndUseSpawnBlock(level, entity.getRespawnPosition(), entity.getRespawnAngle(), false, false).orElse(null); - } - } - if (level == null || position == null) { - level = ((CraftWorld) server.server.getWorlds().get(0)).getHandle(); - position = Vec3.atCenterOf(level.getSharedSpawnPos()); - } - entity.level = level; - entity.setPos(position); - } - - // Return the Bukkit entity. - return entity.getBukkitEntity(); - } - - void injectPlayer(ServerPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } - - bukkitEntity.setAccessible(true); - - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); - } - - @NotNull - @Override - public Player inject(@NotNull Player player) { - try { - ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { - return openPlayer; - } - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - return player; - } - } - - @Nullable - @Override - public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - - ServerPlayer nmsPlayer = getHandle(player); - - if (nmsPlayer.connection == null) { - return null; - } - - InventoryView view = getView(player, inventory); - - if (view == null) { - return player.openInventory(inventory.getBukkitInventory()); - } - - AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { - @Override - public MenuType getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - - container.setTitle(Component.literal(view.getTitle())); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); - - if (container == null) { - return null; - } - - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - Component.literal(container.getBukkitView().getTitle()))); - nmsPlayer.containerMenu = container; - nmsPlayer.initMenu(container); - - return container.getBukkitView(); - - } - - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - - static @NotNull MenuType getContainers(int inventorySize) { - - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; // PLAYER - case 41, 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> MenuType.GENERIC_9x3; // Default 27-slot inventory - }; - } - - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - -} diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialPlayerInventory.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialPlayerInventory.java deleted file mode 100644 index 52cfec1d..00000000 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialPlayerInventory.java +++ /dev/null @@ -1,806 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_19_R3; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.DamageTypeTags; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private Player player; - private NonNullList items; - private NonNullList armor; - private NonNullList offhand; - private List> compartments; - - public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.selected = player.getInventory().selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - Player offlinePlayer = this.player; - Player onlinePlayer = PlayerDataManager.getHandle(player); - onlinePlayer.getInventory().transaction.addAll(this.transaction); - - // Set owner to new player. - this.player = onlinePlayer; - - // Set player's inventory contents to our modified contents. - Inventory onlineInventory = onlinePlayer.getInventory(); - for (int i = 0; i < getContainerSize(); ++i) { - onlineInventory.setItem(i, getRawItem(i)); - } - onlineInventory.selected = this.selected; - - // Set our item arrays to the new inventory's arrays. - this.items = onlineInventory.items; - this.armor = onlineInventory.armor; - this.offhand = onlineInventory.offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - - // Add existing viewers to new viewer list. - Inventory offlineInventory = offlinePlayer.getInventory(); - // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. - offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); - onlineInventory.transaction.addAll(offlineInventory.transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - private @NotNull ItemStack getRawItem(int i) { - if (i < 0) { - return ItemStack.EMPTY; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - return list.get(i); - } - } - - return ItemStack.EMPTY; - } - - private void setRawItem(int i, @NotNull ItemStack itemStack) { - if (i < 0) { - return; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - list.set(i, itemStack); - } - } - } - - private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - - private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { - if (index < items.size()) { - return new IndexedCompartment(items, getReversedItemSlotNum(index)); - } - - index -= items.size(); - - if (index < armor.size()) { - return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); - } - - index -= armor.size(); - - if (index < offhand.size()) { - return new IndexedCompartment(offhand, index); - } - - index -= offhand.size(); - - return new IndexedCompartment(null, index); - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - private boolean contains(Predicate predicate) { - return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.player.getInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.player.getInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.player.getInventory().getViewers(); - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public int getMaxStackSize() { - return this.player.getInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int size) { - this.player.getInventory().setMaxStackSize(size); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public boolean hasCustomName() { - return false; - } - - @Override - public List getContents() { - return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); - } - - @Override - public ItemStack getSelected() { - return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; - } - - private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getRawItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { - remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); - if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { - remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; - } - - @Override - public int getFreeSlot() { - for(int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void setPickedItem(ItemStack itemstack) { - int i = this.findSlotMatchingItem(itemstack); - if (isHotbarSlot(i)) { - this.selected = i; - } else if (i == -1) { - this.selected = this.getSuitableHotbarSlot(); - if (!this.items.get(this.selected).isEmpty()) { - int j = this.getFreeSlot(); - if (j != -1) { - this.items.set(j, this.items.get(this.selected)); - } - } - - this.items.set(this.selected, itemstack); - } else { - this.pickSlot(i); - } - - } - - @Override - public void pickSlot(int i) { - this.selected = this.getSuitableHotbarSlot(); - ItemStack itemstack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int findSlotMatchingItem(ItemStack itemstack) { - for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { - return i; - } - } - - return -1; - } - - @Override - public int findSlotMatchingUnusedItem(ItemStack itemStack) { - for(int i = 0; i < this.items.size(); ++i) { - ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { - return i; - } - } - - return -1; - } - - @Override - public int getSuitableHotbarSlot() { - int i; - int j; - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (!this.items.get(i).isEnchanted()) { - return i; - } - } - - return this.selected; - } - - @Override - public void swapPaint(double d0) { - if (d0 > 0.0D) { - d0 = 1.0D; - } - - if (d0 < 0.0D) { - d0 = -1.0D; - } - - this.selected = (int) (this.selected - d0); - - while (this.selected < 0) { - this.selected += 9; - } - - while(this.selected >= 9) { - this.selected -= 9; - } - } - - @Override - public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); - j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); - ItemStack itemstack = this.player.containerMenu.getCarried(); - j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); - if (itemstack.isEmpty()) { - this.player.containerMenu.setCarried(ItemStack.EMPTY); - } - - return j; - } - - private int addResource(ItemStack itemstack) { - int i = this.getSlotWithRemainingSpace(itemstack); - if (i == -1) { - i = this.getFreeSlot(); - } - - return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); - } - - private int addResource(int i, ItemStack itemstack) { - Item item = itemstack.getItem(); - int j = itemstack.getCount(); - ItemStack localItemStack = this.getRawItem(i); - if (localItemStack.isEmpty()) { - localItemStack = new ItemStack(item, 0); - if (itemstack.hasTag()) { - // hasTag ensures tag not null - //noinspection ConstantConditions - localItemStack.setTag(itemstack.getTag().copy()); - } - - this.setRawItem(i, localItemStack); - } - - int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); - - if (k > this.getMaxStackSize() - localItemStack.getCount()) { - k = this.getMaxStackSize() - localItemStack.getCount(); - } - - if (k != 0) { - j -= k; - localItemStack.grow(k); - localItemStack.setPopTime(5); - } - - return j; - } - - @Override - public int getSlotWithRemainingSpace(ItemStack itemstack) { - if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { - return this.selected; - } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { - return 40; - } else { - for(int i = 0; i < this.items.size(); ++i) { - if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void tick() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (!compartment.get(i).isEmpty()) { - compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i); - } - } - } - - } - - @Override - public boolean add(ItemStack itemStack) { - return this.add(-1, itemStack); - } - - @Override - public boolean add(int i, ItemStack itemStack) { - if (itemStack.isEmpty()) { - return false; - } else { - try { - if (itemStack.isDamaged()) { - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i >= 0) { - this.items.set(i, itemStack.copy()); - this.items.get(i).setPopTime(5); - itemStack.setCount(0); - return true; - } else if (this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemStack.getCount(); - if (i == -1) { - itemStack.setCount(this.addResource(itemStack)); - } else { - itemStack.setCount(this.addResource(i, itemStack)); - } - } while(!itemStack.isEmpty() && itemStack.getCount() < j); - - if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return itemStack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); - crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); - crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); - crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); - throw new ReportedException(crashReport); - } - } - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack) { - this.placeItemBackInInventory(itemStack, true); - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { - while(true) { - if (!itemStack.isEmpty()) { - int i = this.getSlotWithRemainingSpace(itemStack); - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); - if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { - ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); - } - continue; - } - - this.player.drop(itemStack, false); - } - - return; - } - } - - @Override - public ItemStack removeItem(int rawIndex, final int j) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null - || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { - return ItemStack.EMPTY; - } - - return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); - } - - @Override - public void removeItem(ItemStack itemStack) { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (compartment.get(i) == itemStack) { - compartment.set(i, ItemStack.EMPTY); - break; - } - } - } - } - - @Override - public ItemStack removeItemNoUpdate(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); - - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - - return removed; - } - - @Override - public void setItem(int rawIndex, final ItemStack itemStack) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - this.player.drop(itemStack, true); - return; - } - - indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); - } - - @Override - public float getDestroySpeed(BlockState blockState) { - return this.items.get(this.selected).getDestroySpeed(blockState); - } - - @Override - public ListTag save(ListTag listTag) { - for (int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.offhand.size(); ++i) { - if (!this.offhand.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - return listTag; - } - - @Override - public void load(ListTag listTag) { - this.items.clear(); - this.armor.clear(); - this.offhand.clear(); - - for(int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.of(compoundTag); - if (!itemstack.isEmpty()) { - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } - } - } - - } - - @Override - public int getContainerSize() { - return 45; - } - - @Override - public boolean isEmpty() { - return !contains(itemStack -> !itemStack.isEmpty()); - } - - @Override - public ItemStack getItem(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - return indexedCompartment.compartment().get(indexedCompartment.index()); - } - - @Override - public Component getName() { - return this.player.getName(); - } - - @Override - public ItemStack getArmor(int index) { - return this.armor.get(index); - } - - @Override - public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { - if (damage > 0.0F) { - damage /= 4.0F; - if (damage < 1.0F) { - damage = 1.0F; - } - - for (int index : armorIndices) { - ItemStack itemstack = this.armor.get(index); - if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { - itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); - } - } - } - } - - @Override - public void dropAll() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - ItemStack itemstack = compartment.get(i); - if (!itemstack.isEmpty()) { - this.player.drop(itemstack, true, false); - compartment.set(i, ItemStack.EMPTY); - } - } - } - } - - @Override - public void setChanged() { - super.setChanged(); - } - - @Override - public int getTimesChanged() { - return super.getTimesChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean contains(ItemStack itemstack) { - return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack)); - } - - @Override - public boolean contains(TagKey tagKey) { - - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); - } - - @Override - public void replaceWith(Inventory inventory) { - Function getter; - - if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { - getter = specialPlayerInventory::getRawItem; - } else { - getter = inventory::getItem; - } - - for(int i = 0; i < this.getContainerSize(); ++i) { - this.setRawItem(i, getter.apply(i)); - } - - this.selected = inventory.selected; - } - - @Override - public void clearContent() { - for (NonNullList compartment : this.compartments) { - compartment.clear(); - } - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountSimpleStack(itemstack); - } - } - - @Override - public ItemStack removeFromSelected(boolean dropWholeStack) { - ItemStack itemstack = this.getSelected(); - return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); - } - -} diff --git a/internal/v1_20_R2/pom.xml b/internal/v1_20_R2/pom.xml deleted file mode 100644 index 5bdb0fd8..00000000 --- a/internal/v1_20_R2/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 4.4.4-SNAPSHOT - - - openinvadapter1_20_R2 - OpenInvAdapter1_20_R2 - - - 17 - 17 - 1.20.2-R0.1-SNAPSHOT - - - - - org.spigotmc - spigot-api - ${spigot.version} - - - spigot - org.spigotmc - provided - ${spigot.version} - remapped-mojang - - - openinvapi - com.lishid - provided - - - openinvplugincore - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-shade-plugin - - - maven-compiler-plugin - - - net.md-5 - specialsource-maven-plugin - - - - - diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java deleted file mode 100644 index 5987e9bf..00000000 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialEnderChest.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R2; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.ContainerListener; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R2.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { - - private final CraftInventory inventory; - private ServerPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChestInventory().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); - - // Set owner to new player. - this.owner = onlinePlayer; - - // Set player's ender chest contents to our modified contents. - PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); - for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { - onlineEnderChest.setItem(i, this.items.get(i)); - } - - // Set our item array to the new inventory's array. - this.items = onlineEnderChest.items; - - // Add viewers to new inventory. - onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChestInventory().getViewers(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public void setActiveChest(EnderChestBlockEntity enderChest) { - this.owner.getEnderChestInventory().setActiveChest(enderChest); - } - - @Override - public boolean isActiveChest(EnderChestBlockEntity enderChest) { - return this.owner.getEnderChestInventory().isActiveChest(enderChest); - } - - @Override - public int getMaxStackSize() { - return this.owner.getEnderChestInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChestInventory().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChestInventory().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void addListener(ContainerListener listener) { - this.owner.getEnderChestInventory().addListener(listener); - } - - @Override - public void removeListener(ContainerListener listener) { - this.owner.getEnderChestInventory().removeListener(listener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int i, int j) { - ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack addItem(ItemStack itemstack) { - ItemStack localItem = itemstack.copy(); - this.moveItemToOccupiedSlotsWithSameType(localItem); - if (localItem.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.moveItemToEmptySlots(localItem); - return localItem.isEmpty() ? ItemStack.EMPTY : localItem; - } - } - - @Override - public boolean canAddItem(ItemStack itemstack) { - for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { - return true; - } - } - - return false; - } - - private void moveItemToEmptySlots(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (localItem.isEmpty()) { - this.setItem(i, itemstack.copy()); - itemstack.setCount(0); - return; - } - } - } - - private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameTags(localItem, itemstack)) { - this.moveItemsBetweenStacks(itemstack, localItem); - if (itemstack.isEmpty()) { - return; - } - } - } - } - - private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { - int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); - int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); - if (j > 0) { - itemstack1.grow(j); - itemstack.shrink(j); - this.setChanged(); - } - } - - @Override - public ItemStack removeItemNoUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.items.set(i, ItemStack.EMPTY); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.setChanged(); - } - - @Override - public int getContainerSize() { - return this.owner.getEnderChestInventory().getContainerSize(); - } - - @Override - public boolean isEmpty() { - return this.items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void startOpen(Player player) { - } - - @Override - public void stopOpen(Player player) { - } - - @Override - public boolean canPlaceItem(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clearContent() { - this.items.clear(); - this.setChanged(); - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountStack(itemstack); - } - - } - - @Override - public List removeAllItems() { - List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clearContent(); - return list; - } - - @Override - public ItemStack removeItemType(Item item, int i) { - ItemStack itemstack = new ItemStack(item, 0); - - for(int j = this.getContainerSize() - 1; j >= 0; --j) { - ItemStack localItem = this.getItem(j); - if (localItem.getItem().equals(item)) { - int k = i - itemstack.getCount(); - ItemStack splitItem = localItem.split(k); - itemstack.grow(splitItem.getCount()); - if (itemstack.getCount() == i) { - break; - } - } - } - - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); - } - - @Override - public void fromTag(ListTag listTag) { - for (int i = 0; i < this.getContainerSize(); ++i) { - this.setItem(i, ItemStack.EMPTY); - } - - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.of(compoundTag)); - } - } - - } - -} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java index ea492c70..418abf35 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java @@ -237,7 +237,8 @@ private void forceGameType(final ServerPlayer player, final GameType gameMode) { this.serverPlayerGameModeGameType.setAccessible(true); this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); } } diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java index 2ce13a58..5eac3b1b 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java @@ -758,7 +758,7 @@ public boolean stillValid(Player player) { @Override public boolean contains(ItemStack itemstack) { - return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem())); + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(itemstack.getItem())); } @Override diff --git a/internal/v1_19_R3/pom.xml b/internal/v1_20_R4/pom.xml similarity index 90% rename from internal/v1_19_R3/pom.xml rename to internal/v1_20_R4/pom.xml index 029f172a..1962ae3b 100644 --- a/internal/v1_19_R3/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -26,13 +26,13 @@ 4.4.4-SNAPSHOT - openinvadapter1_19_R3 - OpenInvAdapter1_19_R3 + openinvadapter1_20_R4 + OpenInvAdapter1_20_R4 - 17 - 17 - 1.19.4-R0.1-SNAPSHOT + 21 + 21 + 1.20.6-R0.1-SNAPSHOT diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java similarity index 82% rename from internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java rename to internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java index c3de0eba..7731837d 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/AnySilentContainer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java @@ -14,21 +14,17 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R2; +package com.lishid.openinv.internal.v1_20_R4; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.util.ReflectionHelper; -import java.lang.reflect.Field; -import java.util.logging.Logger; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.entity.monster.Shulker; import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.PlayerEnderChestContainer; @@ -41,19 +37,17 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Statistic; -import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.util.logging.Logger; + public class AnySilentContainer implements IAnySilentContainer { private @Nullable Field serverPlayerGameModeGameType; @@ -78,37 +72,6 @@ public AnySilentContainer() { } } - @Override - public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { - org.bukkit.World bukkitWorld = shulkerBox.getWorld(); - if (!(bukkitWorld instanceof CraftWorld)) { - bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); - } - - if (!(bukkitWorld instanceof CraftWorld craftWorld)) { - Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); - OpenInv.getPlugin(OpenInv.class).getLogger().log(java.util.logging.Level.WARNING, exception.getMessage(), exception); - return false; - } - - final ServerLevel world = craftWorld.getHandle(); - final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); - final BlockEntity tile = world.getBlockEntity(blockPosition); - - if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) - || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { - return false; - } - - BlockState blockState = world.getBlockState(blockPosition); - - // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen - AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) - .move(blockPosition) - .deflate(1.0E-6D); - return !world.noCollision(boundingBox); - } - @Override public boolean activateContainer( @NotNull final Player bukkitPlayer, @@ -236,7 +199,8 @@ private void forceGameType(final ServerPlayer player, final GameType gameMode) { this.serverPlayerGameModeGameType.setAccessible(true); this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); } } diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java similarity index 86% rename from internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java rename to internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java index 33677e0d..8b16586d 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R2; +package com.lishid.openinv.internal.v1_20_R4; import com.mojang.logging.LogUtils; import net.minecraft.Util; @@ -24,13 +24,14 @@ import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_20_R2.CraftServer; -import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.CraftServer; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Set; public class OpenPlayer extends CraftPlayer { @@ -56,10 +57,12 @@ public class OpenPlayer extends CraftPlayer { "SpawnForced", "SpawnAngle", "SpawnDimension", + "raid_omen_position", // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) "ShoulderEntityLeft", "ShoulderEntityRight", "LastDeathLocation", + "current_explosion_impact_pos", // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) "ActiveEffects", // Backwards compat: Renamed from 1.19 "active_effects", @@ -85,7 +88,7 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player).orElse(null); CompoundTag playerData = getWritableTag(oldData); playerData = player.saveWithoutId(playerData); setExtraData(playerData); @@ -95,11 +98,12 @@ public void saveData() { revertSpecialValues(playerData, oldData); } - File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); NbtIo.writeCompressed(playerData, file); - File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat"); - File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(file1, file, file2); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, file, backupFile); } catch (Exception e) { LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } @@ -115,7 +119,8 @@ public void saveData() { oldData = oldData.copy(); // Remove vanilla/server data that is not written every time. - oldData.getAllKeys().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); return oldData; } diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java similarity index 95% rename from internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java rename to internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java index f53c0192..d058c5b8 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/PlayerDataManager.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R2; +package com.lishid.openinv.internal.v1_20_R4; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; @@ -40,11 +40,11 @@ import org.bukkit.OfflinePlayer; import org.bukkit.Server; import org.bukkit.World; -import org.bukkit.craftbukkit.v1_20_R2.CraftServer; -import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; -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.CraftContainer; +import org.bukkit.craftbukkit.v1_20_R4.CraftServer; +import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; +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.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.bukkit.plugin.java.JavaPlugin; @@ -166,7 +166,7 @@ public PlayerDataManager() { static boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData - CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player); + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); if (loadedData == null) { // Exceptions with loading are logged by Mojang. @@ -193,10 +193,10 @@ private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTa World bukkitWorld; if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { // Modern Bukkit world. - bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { // Legacy Bukkit world. - bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(loadedData.getString("world")); + bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); } else { // Vanilla player data. DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialEnderChest.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java similarity index 94% rename from internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialEnderChest.java rename to internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java index 0d52f2d7..712a68af 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/SpecialEnderChest.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java @@ -14,12 +14,10 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_19_R3; +package com.lishid.openinv.internal.v1_20_R4; import com.lishid.openinv.internal.ISpecialEnderChest; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; +import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -33,13 +31,17 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { private final CraftInventory inventory; @@ -197,7 +199,7 @@ public ItemStack addItem(ItemStack itemstack) { @Override public boolean canAddItem(ItemStack itemstack) { for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { return true; } } @@ -219,7 +221,7 @@ private void moveItemToEmptySlots(ItemStack itemstack) { private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { for(int i = 0; i < this.getContainerSize(); ++i) { ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameTags(localItem, itemstack)) { + if (ItemStack.isSameItemSameComponents(localItem, itemstack)) { this.moveItemsBetweenStacks(itemstack, localItem); if (itemstack.isEmpty()) { return; @@ -332,7 +334,7 @@ public String toString() { } @Override - public void fromTag(ListTag listTag) { + public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) { for (int i = 0; i < this.getContainerSize(); ++i) { this.setItem(i, ItemStack.EMPTY); } @@ -341,7 +343,7 @@ public void fromTag(ListTag listTag) { CompoundTag compoundTag = listTag.getCompound(i); int j = compoundTag.getByte("Slot") & 255; if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.of(compoundTag)); + this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag)); } } diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java similarity index 88% rename from internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java rename to internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java index 3e527ea2..a447f63d 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/SpecialPlayerInventory.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java @@ -14,46 +14,44 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R2; +package com.lishid.openinv.internal.v1_20_R4; import com.google.common.collect.ImmutableList; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.core.NonNullList; +import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.DamageTypeTags; import net.minecraft.tags.TagKey; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R2.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { private final CraftInventory inventory; @@ -203,7 +201,8 @@ private int getReversedItemSlotNum(final int i) { return i + 9; } - private boolean contains(Predicate predicate) { + @Override + public boolean contains(Predicate predicate) { return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); } @@ -263,7 +262,7 @@ public ItemStack getSelected() { } private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); } @Override @@ -336,7 +335,7 @@ public void pickSlot(int i) { @Override public int findSlotMatchingItem(ItemStack itemstack) { for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) { return i; } } @@ -348,7 +347,7 @@ public int findSlotMatchingItem(ItemStack itemstack) { public int findSlotMatchingUnusedItem(ItemStack itemStack) { for(int i = 0; i < this.items.size(); ++i) { ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) { return i; } } @@ -379,15 +378,9 @@ public int getSuitableHotbarSlot() { @Override public void swapPaint(double d0) { - if (d0 > 0.0D) { - d0 = 1.0D; - } + int i = (int) Math.signum(d0); - if (d0 < 0.0D) { - d0 = -1.0D; - } - - this.selected = (int) (this.selected - d0); + this.selected -= i; while (this.selected < 0) { this.selected += 9; @@ -423,25 +416,14 @@ private int addResource(ItemStack itemstack) { } private int addResource(int i, ItemStack itemstack) { - Item item = itemstack.getItem(); int j = itemstack.getCount(); ItemStack localItemStack = this.getRawItem(i); if (localItemStack.isEmpty()) { - localItemStack = new ItemStack(item, 0); - if (itemstack.hasTag()) { - // hasTag ensures tag not null - //noinspection ConstantConditions - localItemStack.setTag(itemstack.getTag().copy()); - } - + localItemStack = itemstack.copyWithCount(0); this.setRawItem(i, localItemStack); } - int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); - - if (k > this.getMaxStackSize() - localItemStack.getCount()) { - k = this.getMaxStackSize() - localItemStack.getCount(); - } + int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount()); if (k != 0) { j -= k; @@ -502,7 +484,7 @@ public boolean add(int i, ItemStack itemStack) { this.items.get(i).setPopTime(5); itemStack.setCount(0); return true; - } else if (this.player.getAbilities().instabuild) { + } else if (this.player.hasInfiniteMaterials()) { itemStack.setCount(0); return true; } else { @@ -519,7 +501,7 @@ public boolean add(int i, ItemStack itemStack) { } } while(!itemStack.isEmpty() && itemStack.getCount() < j); - if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { + if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) { itemStack.setCount(0); return true; } else { @@ -630,7 +612,7 @@ public ListTag save(ListTag listTag) { if (!this.items.get(i).isEmpty()) { CompoundTag compoundTag = new CompoundTag(); compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(compoundTag); + this.items.get(i).save(this.player.registryAccess(), compoundTag); listTag.add(compoundTag); } } @@ -639,7 +621,7 @@ public ListTag save(ListTag listTag) { if (!this.armor.get(i).isEmpty()) { CompoundTag compoundTag = new CompoundTag(); compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(compoundTag); + this.armor.get(i).save(this.player.registryAccess(), compoundTag); listTag.add(compoundTag); } } @@ -648,7 +630,7 @@ public ListTag save(ListTag listTag) { if (!this.offhand.get(i).isEmpty()) { CompoundTag compoundTag = new CompoundTag(); compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(compoundTag); + this.offhand.get(i).save(this.player.registryAccess(), compoundTag); listTag.add(compoundTag); } } @@ -665,15 +647,13 @@ public void load(ListTag listTag) { for(int i = 0; i < listTag.size(); ++i) { CompoundTag compoundTag = listTag.getCompound(i); int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.of(compoundTag); - if (!itemstack.isEmpty()) { - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } + ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY); + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); } } @@ -710,23 +690,6 @@ public ItemStack getArmor(int index) { return this.armor.get(index); } - @Override - public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { - if (damage > 0.0F) { - damage /= 4.0F; - if (damage < 1.0F) { - damage = 1.0F; - } - - for (int index : armorIndices) { - ItemStack itemstack = this.armor.get(index); - if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { - itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); - } - } - } - } - @Override public void dropAll() { for (NonNullList compartment : this.compartments) { @@ -757,12 +720,11 @@ public boolean stillValid(Player player) { @Override public boolean contains(ItemStack itemstack) { - return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem())); + return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack)); } @Override public boolean contains(TagKey tagKey) { - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); } diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index ff2c716a..41664721 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -68,6 +68,7 @@ public String getReleasesLink() { case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2"; case "v1_18_R2", "v1_19_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; case "v1_20_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; + case "v1_19_R3", "v1_20_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; default -> "https://github.com/Jikoo/OpenInv/releases"; }; } diff --git a/pom.xml b/pom.xml index 1af16004..a9633685 100644 --- a/pom.xml +++ b/pom.xml @@ -40,9 +40,8 @@ api plugin - internal/v1_19_R3 - internal/v1_20_R2 internal/v1_20_R3 + internal/v1_20_R4 assembly @@ -163,7 +162,7 @@ net.md-5 specialsource-maven-plugin - 1.2.4 + 2.0.3 package From dd82f59516fe73b3423b9706295add6a5a72ff64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 21:00:11 -0400 Subject: [PATCH 164/340] Bump pascalgn/automerge-action from 0.16.2 to 0.16.3 (#196) Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.16.2 to 0.16.3. - [Release notes](https://github.com/pascalgn/automerge-action/releases) - [Commits](https://github.com/pascalgn/automerge-action/compare/v0.16.2...v0.16.3) --- updated-dependencies: - dependency-name: pascalgn/automerge-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/automerge_dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml index 83e0734b..fb0ceb69 100644 --- a/.github/workflows/automerge_dependabot.yml +++ b/.github/workflows/automerge_dependabot.yml @@ -47,7 +47,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" pull-request-number: "${{ env.PR_NUMBER }}" - name: Merge - uses: pascalgn/automerge-action@v0.16.2 + uses: pascalgn/automerge-action@v0.16.3 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "dependencies,java" From 8b71d3549f064d0ccd6ea7e98bd7e323bdcde316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 21:00:32 -0400 Subject: [PATCH 165/340] Bump dsaltares/fetch-gh-release-asset from 1.1.1 to 1.1.2 (#197) Bumps [dsaltares/fetch-gh-release-asset](https://github.com/dsaltares/fetch-gh-release-asset) from 1.1.1 to 1.1.2. - [Release notes](https://github.com/dsaltares/fetch-gh-release-asset/releases) - [Commits](https://github.com/dsaltares/fetch-gh-release-asset/compare/1.1.1...1.1.2) --- updated-dependencies: - dependency-name: dsaltares/fetch-gh-release-asset dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/external_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/external_release.yml b/.github/workflows/external_release.yml index e748656b..87a2d692 100644 --- a/.github/workflows/external_release.yml +++ b/.github/workflows/external_release.yml @@ -14,7 +14,7 @@ jobs: fetch-depth: 0 - name: Fetch Github Release Asset - uses: dsaltares/fetch-gh-release-asset@1.1.1 + uses: dsaltares/fetch-gh-release-asset@1.1.2 with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ github.event.release.id }} From b81eab819154d88ac4b7ffb9af6a1e376878f417 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 13 May 2024 18:10:13 -0400 Subject: [PATCH 166/340] Update to 1.20.6 (#198) * Fix version detection on Paper * Fix offline player changes being overwritten --- .../openinv/internal/v1_20_R4/OpenPlayer.java | 24 ++-- plugin/pom.xml | 29 ++++ .../com/lishid/openinv/InternalAccessor.java | 129 ++++++++++++++---- pom.xml | 9 ++ 4 files changed, 153 insertions(+), 38 deletions(-) diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java index 8b16586d..be7fbb47 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java @@ -36,8 +36,16 @@ public class OpenPlayer extends CraftPlayer { + /** + * List of tags to always reset when saving. + * + * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) + * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + */ private static final Set RESET_TAGS = Set.of( - // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + // Entity#saveWithoutId(CompoundTag) "CustomName", "CustomNameVisible", "Silent", @@ -47,7 +55,7 @@ public class OpenPlayer extends CraftPlayer { "HasVisualFire", "Tags", "Passengers", - // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // ServerPlayer#addAdditionalSaveData(CompoundTag) // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle "warden_spawn_tracker", "enteredNetherPosition", @@ -58,12 +66,12 @@ public class OpenPlayer extends CraftPlayer { "SpawnAngle", "SpawnDimension", "raid_omen_position", - // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + // Player#addAdditionalSaveData(CompoundTag) "ShoulderEntityLeft", "ShoulderEntityRight", "LastDeathLocation", "current_explosion_impact_pos", - // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + // LivingEntity#addAdditionalSaveData(CompoundTag) "ActiveEffects", // Backwards compat: Renamed from 1.19 "active_effects", "SleepingX", @@ -88,7 +96,7 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player).orElse(null); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); CompoundTag playerData = getWritableTag(oldData); playerData = player.saveWithoutId(playerData); setExtraData(playerData); @@ -99,11 +107,11 @@ public void saveData() { } Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, file); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, file, backupFile); + Util.safeReplaceFile(dataFile, tempFile, backupFile); } catch (Exception e) { LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } diff --git a/plugin/pom.xml b/plugin/pom.xml index e8f7e890..f1f3bbac 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -40,6 +40,10 @@ spigot-api org.spigotmc + + planarwrappers + com.github.jikoo + @@ -53,6 +57,31 @@ maven-shade-plugin + + + + + com.lishid:openinv* + + ** + + + + *:* + + + META-INF/MANIFEST.MF + + + + + + com.github.jikoo.planarwrappers + com.github.jikoo.openinv.planarwrappers + + + true + maven-compiler-plugin diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index 41664721..778589c9 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -16,6 +16,8 @@ package com.lishid.openinv; +import com.github.jikoo.planarwrappers.util.version.BukkitVersions; +import com.github.jikoo.planarwrappers.util.version.Version; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialEnderChest; @@ -25,52 +27,119 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.reflect.Constructor; class InternalAccessor { private final @NotNull Plugin plugin; - private final String version; + private final @Nullable String versionPackage; private boolean supported = false; private IPlayerDataManager playerDataManager; private IAnySilentContainer anySilentContainer; InternalAccessor(@NotNull Plugin plugin) { this.plugin = plugin; + this.versionPackage = getVersionPackage(); + checkSupported(); + } - String packageName = plugin.getServer().getClass().getPackage().getName(); - this.version = packageName.substring(packageName.lastIndexOf('.') + 1); - + private void checkSupported() { + if (versionPackage == null) { + return; + } try { - Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory"); - Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest"); + Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialPlayerInventory"); + Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialEnderChest"); this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager"); this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer"); this.supported = InventoryAccess.isUsable(); } catch (Exception ignored) {} } - public String getReleasesLink() { + private @Nullable String getVersionPackage() { + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20, 3))) { // Min supported version: 1.20.3 + return null; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 4))) { // 1.20.3, 1.20.4 + return "v1_20_R3"; + } + if (BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21))) { + return null; + } + if (BukkitVersions.MINECRAFT.greaterThan(Version.of(1, 20, 6))) { + return null; // TODO: use at your own risk flag? + } + // 1.20.5, 1.20.6 + return "v1_20_R4"; + } - return switch (version) { - case "1_4_5", "1_4_6", "v1_4_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3", - "v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", - "v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1" - -> "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)"; - case "v1_13_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; - case "v1_13_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.7"; - case "v1_14_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.1"; - case "v1_16_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; - case "v1_8_R3", "v1_15_R1", "v1_16_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; - case "v1_16_R3" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; - case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10"; - case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2"; - case "v1_18_R2", "v1_19_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; - case "v1_20_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; - case "v1_19_R3", "v1_20_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; - default -> "https://github.com/Jikoo/OpenInv/releases"; - }; + public String getReleasesLink() { + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck. + return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 8, 8))) { // 1.8.8 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 13))) { // 1.4.4+ had versioned packages. + return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 13))) { // 1.13 + return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 14))) { // 1.13.1, 1.13.2 + return "https://github.com/lishid/OpenInv/releases/tag/4.0.7"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14))) { // 1.14 to 1.14.1 had no revision bump. + return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14, 1))) { // 1.14.1 to 1.14.2 had no revision bump. + return "https://github.com/lishid/OpenInv/releases/tag/4.0.1"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 15))) { // 1.14.2 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.1"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 15, 1))) { // 1.15, 1.15.1 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 16))) { // 1.15.2 + return "https://github.com/Jikoo/OpenInv/commit/502f661be39ee85d300851dd571f3da226f12345 (never released)"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 1))) { // 1.16, 1.16.1 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 3))) { // 1.16.2, 1.16.3 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 17))) { // 1.16.4, 1.16.5 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 18, 1))) { // 1.17, 1.18, 1.18.1 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 19))) { // 1.18.2 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19))) { // 1.19 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.0"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19, 1))) { // 1.19.1 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 19, 3))) { // 1.19.2, 1.19.3 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20))) { // 1.19.4 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 1))) { // 1.20, 1.20.1 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 3))) { // 1.20.3 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; + } + return "https://github.com/Jikoo/OpenInv/releases"; } private @NotNull T createObject( @@ -79,7 +148,7 @@ public String getReleasesLink() { @NotNull Object @NotNull ... params) throws ClassCastException, ReflectiveOperationException { // Fetch internal class if it exists. - Class internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className); + Class internalClass = Class.forName("com.lishid.openinv.internal." + this.versionPackage + "." + className); // Quick return: no parameters, no need to fiddle about finding the correct constructor. if (params.length == 0) { @@ -119,7 +188,7 @@ public String getReleasesLink() { @NotNull Player player, boolean online) throws InstantiationException { if (!this.supported) { - throw new IllegalStateException(String.format("Unsupported server version %s!", this.version)); + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } try { return this.createObject(assignableClass, className, player, online); @@ -138,7 +207,7 @@ public String getReleasesLink() { */ public @NotNull IAnySilentContainer getAnySilentContainer() { if (!this.supported) { - throw new IllegalStateException(String.format("Unsupported server version %s!", this.version)); + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } return this.anySilentContainer; } @@ -151,7 +220,7 @@ public String getReleasesLink() { */ public @NotNull IPlayerDataManager getPlayerDataManager() { if (!this.supported) { - throw new IllegalStateException(String.format("Unsupported server version %s!", this.version)); + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } return this.playerDataManager; } @@ -162,7 +231,7 @@ public String getReleasesLink() { * @return the version */ public @NotNull String getVersion() { - return this.version; + return BukkitVersions.MINECRAFT.toString(); } /** diff --git a/pom.xml b/pom.xml index a9633685..d3ade9f4 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,10 @@ spigot-repo https://hub.spigotmc.org/nexus/content/groups/public/ + + jitpack.io + https://jitpack.io + @@ -98,6 +102,11 @@ + + planarwrappers + com.github.jikoo + 3.2.0 + From 7ee6206ffbaa0b4186e2d9727a7e6431b43fb060 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 13 May 2024 18:25:21 -0400 Subject: [PATCH 167/340] Bump version to 4.4.4 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index f7343f75..23f019b9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.4-SNAPSHOT + 4.4.4 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index c47b7ff9..f2612011 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.4-SNAPSHOT + 4.4.4 openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index e9320a6b..95031606 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.4-SNAPSHOT + 4.4.4 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 1962ae3b..b076f419 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.4-SNAPSHOT + 4.4.4 openinvadapter1_20_R4 diff --git a/plugin/pom.xml b/plugin/pom.xml index f1f3bbac..d6eb13d8 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.4-SNAPSHOT + 4.4.4 openinvplugincore diff --git a/pom.xml b/pom.xml index d3ade9f4..d4da4d6e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.4-SNAPSHOT + 4.4.4 pom @@ -88,13 +88,13 @@ openinvapi com.lishid compile - 4.4.4-SNAPSHOT + 4.4.4 openinvplugincore com.lishid compile - 4.4.4-SNAPSHOT + 4.4.4 com.lishid From 8134887666fae86b849dee75ce904370edd0865d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 13 May 2024 18:25:36 -0400 Subject: [PATCH 168/340] Bump version to 4.4.5-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 23f019b9..09c9abe7 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.4 + 4.4.5-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index f2612011..e39e603f 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.4 + 4.4.5-SNAPSHOT openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 95031606..3da6acc1 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.4 + 4.4.5-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index b076f419..75dea89a 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.4 + 4.4.5-SNAPSHOT openinvadapter1_20_R4 diff --git a/plugin/pom.xml b/plugin/pom.xml index d6eb13d8..7e416659 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.4 + 4.4.5-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index d4da4d6e..462a3764 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.4 + 4.4.5-SNAPSHOT pom @@ -88,13 +88,13 @@ openinvapi com.lishid compile - 4.4.4 + 4.4.5-SNAPSHOT openinvplugincore com.lishid compile - 4.4.4 + 4.4.5-SNAPSHOT com.lishid From e543053eff3381fa8ae769f1b1640936feffc4f8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 18 May 2024 17:20:51 -0400 Subject: [PATCH 169/340] Fix saving issue exacerbated by #183 Closes #199 --- .../java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 08017f9f..2861c2bc 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -86,7 +86,7 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.getPlayerData(player.getStringUUID()); CompoundTag playerData = getWritableTag(oldData); playerData = player.saveWithoutId(playerData); setExtraData(playerData); From 96d2d2482a70f89a06d31d931e9412c2aa85ab91 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 18 May 2024 17:21:47 -0400 Subject: [PATCH 170/340] Bump version to 4.4.5 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 09c9abe7..2aba171c 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.5-SNAPSHOT + 4.4.5 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index e39e603f..d5ca1c01 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.5-SNAPSHOT + 4.4.5 openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 3da6acc1..b02a531e 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.5-SNAPSHOT + 4.4.5 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 75dea89a..ce0c32d4 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.5-SNAPSHOT + 4.4.5 openinvadapter1_20_R4 diff --git a/plugin/pom.xml b/plugin/pom.xml index 7e416659..1501d252 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.5-SNAPSHOT + 4.4.5 openinvplugincore diff --git a/pom.xml b/pom.xml index 462a3764..55b1c42f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.5-SNAPSHOT + 4.4.5 pom @@ -88,13 +88,13 @@ openinvapi com.lishid compile - 4.4.5-SNAPSHOT + 4.4.5 openinvplugincore com.lishid compile - 4.4.5-SNAPSHOT + 4.4.5 com.lishid From 2dde9ba28f27b51a04050c2190fcf426bc263304 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 18 May 2024 17:22:15 -0400 Subject: [PATCH 171/340] Bump version to 4.4.6-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 2aba171c..ae7cb8de 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.5 + 4.4.6-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index d5ca1c01..a18b2960 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.5 + 4.4.6-SNAPSHOT openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index b02a531e..97250bfb 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.5 + 4.4.6-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index ce0c32d4..2c28382c 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.5 + 4.4.6-SNAPSHOT openinvadapter1_20_R4 diff --git a/plugin/pom.xml b/plugin/pom.xml index 1501d252..06f13fce 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.5 + 4.4.6-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index 55b1c42f..0bd5b3bf 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.5 + 4.4.6-SNAPSHOT pom @@ -88,13 +88,13 @@ openinvapi com.lishid compile - 4.4.5 + 4.4.6-SNAPSHOT openinvplugincore com.lishid compile - 4.4.5 + 4.4.6-SNAPSHOT com.lishid From 3d81fdd0ca9b00dc2140b039a979ac3dbda9fb35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:58:26 +0000 Subject: [PATCH 172/340] Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.3 to 3.6.0 (#202) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bd5b3bf..512cf000 100644 --- a/pom.xml +++ b/pom.xml @@ -153,7 +153,7 @@ org.apache.maven.plugins - 3.5.3 + 3.6.0 From 8f374ef2a55f84d86ed74b5c0b0e52577c9f4e70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:06:46 -0400 Subject: [PATCH 173/340] Bump com.github.jikoo:planarwrappers from 3.2.0 to 3.2.2 (#201) Bumps [com.github.jikoo:planarwrappers](https://github.com/Jikoo/PlanarWrappers) from 3.2.0 to 3.2.2. - [Commits](https://github.com/Jikoo/PlanarWrappers/compare/3.2.0...3.2.2) --- updated-dependencies: - dependency-name: com.github.jikoo:planarwrappers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 512cf000..d8ad20b3 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ planarwrappers com.github.jikoo - 3.2.0 + 3.2.2 From 282f8630ab0edbf53d518d53ec7a5ee4e70dc37e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:07:48 -0400 Subject: [PATCH 174/340] Bump softprops/action-gh-release from 2.0.4 to 2.0.5 (#203) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.4 to 2.0.5. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.4...v2.0.5) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 07e97a5f..d72eb9df 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -28,7 +28,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.0.4 + uses: softprops/action-gh-release@v2.0.5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 89beeca8531369c71ae9a82452d4587391ccc9fd Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 14 Jun 2024 12:02:11 -0400 Subject: [PATCH 175/340] Update to 1.21 (#206) * Update to 1.21 * Add new InventoryView methods --- internal/v1_21_R1/pom.xml | 81 ++ .../internal/v1_21_R1/AnySilentContainer.java | 207 +++++ .../openinv/internal/v1_21_R1/OpenPlayer.java | 185 +++++ .../internal/v1_21_R1/PlayerDataManager.java | 332 ++++++++ .../internal/v1_21_R1/SpecialEnderChest.java | 352 ++++++++ .../v1_21_R1/SpecialPlayerInventory.java | 768 ++++++++++++++++++ .../com/lishid/openinv/InternalAccessor.java | 22 +- .../main/java/com/lishid/openinv/OpenInv.java | 30 +- .../openinv/internal/OpenInventoryView.java | 30 +- pom.xml | 5 +- 10 files changed, 1977 insertions(+), 35 deletions(-) create mode 100644 internal/v1_21_R1/pom.xml create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml new file mode 100644 index 00000000..165be1a2 --- /dev/null +++ b/internal/v1_21_R1/pom.xml @@ -0,0 +1,81 @@ + + + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 4.4.6-SNAPSHOT + + + openinvadapter1_21_R1 + OpenInvAdapter1_21_R1 + + + 21 + 21 + 1.21-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot-api + ${spigot.version} + + + spigot + org.spigotmc + provided + ${spigot.version} + remapped-mojang + + + openinvapi + com.lishid + provided + + + openinvplugincore + com.lishid + + + annotations + org.jetbrains + + + + + + + maven-shade-plugin + + + maven-compiler-plugin + + + net.md-5 + specialsource-maven-plugin + + + + + diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java new file mode 100644 index 00000000..d03f25aa --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_21_R1; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.util.ReflectionHelper; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.logging.Logger; + +public class AnySilentContainer implements IAnySilentContainer { + + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer() { + try { + try { + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); + } + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + final net.minecraft.world.level.Level level = player.level(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); + + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, Component.translatable(("container.enderchest")))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: do not check if chest is blocked + menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); + + if (menuProvider == null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; + } + + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { + return; + } + + ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // ServerPlayer#closeContainer cannot be called without entering an + // infinite loop because this method is called during inventory close. + // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent + player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); + // From ServerPlayer#closeContainer + player.doCloseContainer(); + // Regular inventory close will handle the rest - packet sending, etc. + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); + logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); + } + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java new file mode 100644 index 00000000..6a10ada3 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_21_R1; + +import com.mojang.logging.LogUtils; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.bukkit.craftbukkit.v1_21_R1.CraftServer; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +public class OpenPlayer extends CraftPlayer { + + /** + * List of tags to always reset when saving. + * + * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) + * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + */ + private static final Set RESET_TAGS = Set.of( + // Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + "raid_omen_position", + // Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + "current_explosion_impact_pos", + // LivingEntity#addAdditionalSaveData(CompoundTag) + "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + + public OpenPlayer(CraftServer server, ServerPlayer entity) { + super(server, entity); + } + + @Override + public void loadData() { + PlayerDataManager.loadData(getHandle()); + } + + @Override + public void saveData() { + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); + setExtraData(playerData); + + if (oldData != null) { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + } + + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, tempFile, backupFile); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); + } + } + + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + if (oldData == null) { + return new CompoundTag(); + } + + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @Nullable CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + if (container == null) { + return null; + } + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java new file mode 100644 index 00000000..a66f3ddf --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_21_R1; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.IPlayerDataManager; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.OpenInventoryView; +import com.mojang.authlib.GameProfile; +import com.mojang.serialization.Dynamic; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.ChatVisiblity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R1.CraftServer; +import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; +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.CraftContainer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.UUID; +import java.util.logging.Logger; + +public class PlayerDataManager implements IPlayerDataManager { + + private static boolean paper; + + static { + try { + Class.forName("io.papermc.paper.configuration.Configuration"); + paper = true; + } catch (ClassNotFoundException ignored) { + paper = false; + } + } + + private @Nullable Field bukkitEntity; + + public PlayerDataManager() { + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger(); + logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } + + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + + if (worldServer == null) { + return null; + } + + // Create a new ServerPlayer. + ServerPlayer entity = createNewPlayer(server, worldServer, offline); + + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + + // Try to load the player's data. + if (loadData(entity)) { + // If data is loaded successfully, return the Bukkit entity. + return entity.getBukkitEntity(); + } + + return null; + } + + private @NotNull ServerPlayer createNewPlayer( + @NotNull MinecraftServer server, + @NotNull ServerLevel worldServer, + @NotNull final OfflinePlayer offline) { + // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) + // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + + ClientInformation dummyInfo = new ClientInformation( + "en_us", + 1, // Reduce distance just in case. + ChatVisiblity.HIDDEN, // Don't accept chat. + false, + ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, + ServerPlayer.DEFAULT_MAIN_HAND, + true, + false // Don't list in player list (not that this player is in the list anyway). + ); + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + } + + return entity; + } + + static boolean loadData(@NotNull ServerPlayer player) { + // See CraftPlayer#loadData + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); + + if (loadedData == null) { + // Exceptions with loading are logged by Mojang. + return false; + } + + // Read basic data into the player. + player.load(loadedData); + // Also read "extra" data. + player.readAdditionalSaveData(loadedData); + // Game type settings are also loaded separately. + player.loadGameTypes(loadedData); + + if (paper) { + // Paper: world is not loaded by ServerPlayer#load(CompoundTag). + parseWorld(player, loadedData); + } + + return true; + } + + private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + // Modern Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + return; + } + if (bukkitWorld == null) { + player.spawnIn(null); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); + } + + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + } + + @Override + public @NotNull Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + return player; + } + } + + @Override + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + + ServerPlayer nmsPlayer = getHandle(player); + + if (nmsPlayer.connection == null) { + return null; + } + + InventoryView view = getView(player, inventory); + + if (view == null) { + return player.openInventory(inventory.getBukkitInventory()); + } + + AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + @Override + public MenuType getType() { + return getContainers(inventory.getBukkitInventory().getSize()); + } + }; + + container.setTitle(Component.literal(view.getTitle())); + container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + + if (container == null) { + return null; + } + + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), + Component.literal(container.getBukkitView().getTitle()))); + nmsPlayer.containerMenu = container; + nmsPlayer.initMenu(container); + + return container.getBukkitView(); + + } + + private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { + if (inventory instanceof SpecialEnderChest) { + return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); + } else if (inventory instanceof SpecialPlayerInventory) { + return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); + } else { + return null; + } + } + + static @NotNull MenuType getContainers(int inventorySize) { + + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> MenuType.GENERIC_9x3; // Default 27-slot inventory + }; + } + + @Override + public int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java new file mode 100644 index 00000000..512f2d95 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_21_R1; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.ContainerListener; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { + + private final CraftInventory inventory; + private ServerPlayer owner; + private NonNullList items; + private boolean playerOnline; + + public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { + super(PlayerDataManager.getHandle(player)); + this.inventory = new CraftInventory(this); + this.owner = PlayerDataManager.getHandle(player); + this.playerOnline = online; + this.items = this.owner.getEnderChestInventory().items; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { + if (this.playerOnline) { + return; + } + + ServerPlayer offlinePlayer = this.owner; + ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + + // Set owner to new player. + this.owner = onlinePlayer; + + // Set player's ender chest contents to our modified contents. + PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); + for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { + onlineEnderChest.setItem(i, this.items.get(i)); + } + + // Set our item array to the new inventory's array. + this.items = onlineEnderChest.items; + + // Add viewers to new inventory. + onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); + + this.playerOnline = true; + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public List getContents() { + return this.items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.owner.getEnderChestInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.owner.getEnderChestInventory().getViewers(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void setActiveChest(EnderChestBlockEntity enderChest) { + this.owner.getEnderChestInventory().setActiveChest(enderChest); + } + + @Override + public boolean isActiveChest(EnderChestBlockEntity enderChest) { + return this.owner.getEnderChestInventory().isActiveChest(enderChest); + } + + @Override + public int getMaxStackSize() { + return this.owner.getEnderChestInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int i) { + this.owner.getEnderChestInventory().setMaxStackSize(i); + } + + @Override + public InventoryHolder getOwner() { + return this.owner.getEnderChestInventory().getOwner(); + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void addListener(ContainerListener listener) { + this.owner.getEnderChestInventory().addListener(listener); + } + + @Override + public void removeListener(ContainerListener listener) { + this.owner.getEnderChestInventory().removeListener(listener); + } + + @Override + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int i, int j) { + ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack addItem(ItemStack itemstack) { + ItemStack localItem = itemstack.copy(); + this.moveItemToOccupiedSlotsWithSameType(localItem); + if (localItem.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.moveItemToEmptySlots(localItem); + return localItem.isEmpty() ? ItemStack.EMPTY : localItem; + } + } + + @Override + public boolean canAddItem(ItemStack itemstack) { + for (ItemStack itemstack1 : this.items) { + if (itemstack1.isEmpty() || ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + return true; + } + } + + return false; + } + + private void moveItemToEmptySlots(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (localItem.isEmpty()) { + this.setItem(i, itemstack.copy()); + itemstack.setCount(0); + return; + } + } + } + + private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { + for(int i = 0; i < this.getContainerSize(); ++i) { + ItemStack localItem = this.getItem(i); + if (ItemStack.isSameItemSameComponents(localItem, itemstack)) { + this.moveItemsBetweenStacks(itemstack, localItem); + if (itemstack.isEmpty()) { + return; + } + } + } + } + + private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { + int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); + int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); + if (j > 0) { + itemstack1.grow(j); + itemstack.shrink(j); + this.setChanged(); + } + } + + @Override + public ItemStack removeItemNoUpdate(int i) { + ItemStack itemstack = this.items.get(i); + if (itemstack.isEmpty()) { + return ItemStack.EMPTY; + } else { + this.items.set(i, ItemStack.EMPTY); + return itemstack; + } + } + + @Override + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.setChanged(); + } + + @Override + public int getContainerSize() { + return this.owner.getEnderChestInventory().getContainerSize(); + } + + @Override + public boolean isEmpty() { + return this.items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public void startOpen(Player player) { + } + + @Override + public void stopOpen(Player player) { + } + + @Override + public boolean canPlaceItem(int i, ItemStack itemstack) { + return true; + } + + @Override + public void clearContent() { + this.items.clear(); + this.setChanged(); + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountStack(itemstack); + } + + } + + @Override + public List removeAllItems() { + List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); + this.clearContent(); + return list; + } + + @Override + public ItemStack removeItemType(Item item, int i) { + ItemStack itemstack = new ItemStack(item, 0); + + for(int j = this.getContainerSize() - 1; j >= 0; --j) { + ItemStack localItem = this.getItem(j); + if (localItem.getItem().equals(item)) { + int k = i - itemstack.getCount(); + ItemStack splitItem = localItem.split(k); + itemstack.grow(splitItem.getCount()); + if (itemstack.getCount() == i) { + break; + } + } + } + + if (!itemstack.isEmpty()) { + this.setChanged(); + } + + return itemstack; + } + + @Override + public String toString() { + return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); + } + + @Override + public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) { + for (int i = 0; i < this.getContainerSize(); ++i) { + this.setItem(i, ItemStack.EMPTY); + } + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + if (j < this.getContainerSize()) { + this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag)); + } + } + + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java new file mode 100644 index 00000000..4bfb7b1c --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_21_R1; + +import com.google.common.collect.ImmutableList; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.NonNullList; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { + + private final CraftInventory inventory; + private boolean playerOnline; + private Player player; + private NonNullList items; + private NonNullList armor; + private NonNullList offhand; + private List> compartments; + + public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { + super(PlayerDataManager.getHandle(bukkitPlayer)); + this.inventory = new CraftInventory(this); + this.playerOnline = online; + this.player = super.player; + this.selected = player.getInventory().selected; + this.items = this.player.getInventory().items; + this.armor = this.player.getInventory().armor; + this.offhand = this.player.getInventory().offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + if (this.playerOnline) { + return; + } + + Player offlinePlayer = this.player; + Player onlinePlayer = PlayerDataManager.getHandle(player); + onlinePlayer.getInventory().transaction.addAll(this.transaction); + + // Set owner to new player. + this.player = onlinePlayer; + + // Set player's inventory contents to our modified contents. + Inventory onlineInventory = onlinePlayer.getInventory(); + for (int i = 0; i < getContainerSize(); ++i) { + onlineInventory.setItem(i, getRawItem(i)); + } + onlineInventory.selected = this.selected; + + // Set our item arrays to the new inventory's arrays. + this.items = onlineInventory.items; + this.armor = onlineInventory.armor; + this.offhand = onlineInventory.offhand; + this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); + + // Add existing viewers to new viewer list. + Inventory offlineInventory = offlinePlayer.getInventory(); + // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. + offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); + onlineInventory.transaction.addAll(offlineInventory.transaction); + + this.playerOnline = true; + } + + @Override + public @NotNull CraftInventory getBukkitInventory() { + return this.inventory; + } + + @Override + public void setPlayerOffline() { + this.playerOnline = false; + } + + @Override + public @NotNull HumanEntity getPlayer() { + return this.player.getBukkitEntity(); + } + + private @NotNull ItemStack getRawItem(int i) { + if (i < 0) { + return ItemStack.EMPTY; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + return list.get(i); + } + } + + return ItemStack.EMPTY; + } + + private void setRawItem(int i, @NotNull ItemStack itemStack) { + if (i < 0) { + return; + } + + NonNullList list; + for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { + list = iterator.next(); + if (i < list.size()) { + list.set(i, itemStack); + } + } + } + + private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} + + private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { + if (index < items.size()) { + return new IndexedCompartment(items, getReversedItemSlotNum(index)); + } + + index -= items.size(); + + if (index < armor.size()) { + return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); + } + + index -= armor.size(); + + if (index < offhand.size()) { + return new IndexedCompartment(offhand, index); + } + + index -= offhand.size(); + + return new IndexedCompartment(null, index); + } + + private int getReversedArmorSlotNum(final int i) { + if (i == 0) { + return 3; + } + if (i == 1) { + return 2; + } + if (i == 2) { + return 1; + } + if (i == 3) { + return 0; + } + return i; + } + + private int getReversedItemSlotNum(final int i) { + if (i >= 27) { + return i - 27; + } + return i + 9; + } + + @Override + public boolean contains(Predicate predicate) { + return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); + } + + @Override + public List getArmorContents() { + return this.armor; + } + + @Override + public void onOpen(CraftHumanEntity who) { + this.player.getInventory().onOpen(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + this.player.getInventory().onClose(who); + } + + @Override + public List getViewers() { + return this.player.getInventory().getViewers(); + } + + @Override + public InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + @Override + public int getMaxStackSize() { + return this.player.getInventory().getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int size) { + this.player.getInventory().setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return this.player.getBukkitEntity().getLocation(); + } + + @Override + public boolean hasCustomName() { + return false; + } + + @Override + public List getContents() { + return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } + + @Override + public ItemStack getSelected() { + return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; + } + + private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + @Override + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getRawItem(i); + if (itemstack1.isEmpty()) { + return itemstack.getCount(); + } + + if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); + } + + if (remains <= 0) { + return itemstack.getCount(); + } + } + + ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); + if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { + remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); + } + + return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; + } + + @Override + public int getFreeSlot() { + for(int i = 0; i < this.items.size(); ++i) { + if (this.items.get(i).isEmpty()) { + return i; + } + } + + return -1; + } + + @Override + public void setPickedItem(ItemStack itemstack) { + int i = this.findSlotMatchingItem(itemstack); + if (isHotbarSlot(i)) { + this.selected = i; + } else if (i == -1) { + this.selected = this.getSuitableHotbarSlot(); + if (!this.items.get(this.selected).isEmpty()) { + int j = this.getFreeSlot(); + if (j != -1) { + this.items.set(j, this.items.get(this.selected)); + } + } + + this.items.set(this.selected, itemstack); + } else { + this.pickSlot(i); + } + + } + + @Override + public void pickSlot(int i) { + this.selected = this.getSuitableHotbarSlot(); + ItemStack itemstack = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(i)); + this.items.set(i, itemstack); + } + + @Override + public int findSlotMatchingItem(ItemStack itemstack) { + for(int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) { + return i; + } + } + + return -1; + } + + @Override + public int findSlotMatchingUnusedItem(ItemStack itemStack) { + for(int i = 0; i < this.items.size(); ++i) { + ItemStack localItem = this.items.get(i); + if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) { + return i; + } + } + + return -1; + } + + @Override + public int getSuitableHotbarSlot() { + int i; + int j; + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (this.items.get(i).isEmpty()) { + return i; + } + } + + for(j = 0; j < 9; ++j) { + i = (this.selected + j) % 9; + if (!this.items.get(i).isEnchanted()) { + return i; + } + } + + return this.selected; + } + + @Override + public void swapPaint(double d0) { + int i = (int) Math.signum(d0); + + this.selected -= i; + + while (this.selected < 0) { + this.selected += 9; + } + + while(this.selected >= 9) { + this.selected -= 9; + } + } + + @Override + public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { + byte b0 = 0; + boolean flag = i == 0; + int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); + j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); + ItemStack itemstack = this.player.containerMenu.getCarried(); + j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); + if (itemstack.isEmpty()) { + this.player.containerMenu.setCarried(ItemStack.EMPTY); + } + + return j; + } + + private int addResource(ItemStack itemstack) { + int i = this.getSlotWithRemainingSpace(itemstack); + if (i == -1) { + i = this.getFreeSlot(); + } + + return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); + } + + private int addResource(int i, ItemStack itemstack) { + int j = itemstack.getCount(); + ItemStack localItemStack = this.getRawItem(i); + if (localItemStack.isEmpty()) { + localItemStack = itemstack.copyWithCount(0); + this.setRawItem(i, localItemStack); + } + + int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount()); + + if (k != 0) { + j -= k; + localItemStack.grow(k); + localItemStack.setPopTime(5); + } + + return j; + } + + @Override + public int getSlotWithRemainingSpace(ItemStack itemstack) { + if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { + return this.selected; + } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { + return 40; + } else { + for(int i = 0; i < this.items.size(); ++i) { + if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + @Override + public void tick() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (!compartment.get(i).isEmpty()) { + compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i); + } + } + } + + } + + @Override + public boolean add(ItemStack itemStack) { + return this.add(-1, itemStack); + } + + @Override + public boolean add(int i, ItemStack itemStack) { + if (itemStack.isEmpty()) { + return false; + } else { + try { + if (itemStack.isDamaged()) { + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i >= 0) { + this.items.set(i, itemStack.copy()); + this.items.get(i).setPopTime(5); + itemStack.setCount(0); + return true; + } else if (this.player.hasInfiniteMaterials()) { + itemStack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + do { + j = itemStack.getCount(); + if (i == -1) { + itemStack.setCount(this.addResource(itemStack)); + } else { + itemStack.setCount(this.addResource(i, itemStack)); + } + } while(!itemStack.isEmpty() && itemStack.getCount() < j); + + if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) { + itemStack.setCount(0); + return true; + } else { + return itemStack.getCount() < j; + } + } + } catch (Throwable var6) { + CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); + crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); + crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); + crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); + throw new ReportedException(crashReport); + } + } + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack) { + this.placeItemBackInInventory(itemStack, true); + } + + @Override + public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { + while(true) { + if (!itemStack.isEmpty()) { + int i = this.getSlotWithRemainingSpace(itemStack); + if (i == -1) { + i = this.getFreeSlot(); + } + + if (i != -1) { + int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); + if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { + ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); + } + continue; + } + + this.player.drop(itemStack, false); + } + + return; + } + } + + @Override + public ItemStack removeItem(int rawIndex, final int j) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null + || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { + return ItemStack.EMPTY; + } + + return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); + } + + @Override + public void removeItem(ItemStack itemStack) { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + if (compartment.get(i) == itemStack) { + compartment.set(i, ItemStack.EMPTY); + break; + } + } + } + } + + @Override + public ItemStack removeItemNoUpdate(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); + + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + + return removed; + } + + @Override + public void setItem(int rawIndex, final ItemStack itemStack) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + this.player.drop(itemStack, true); + return; + } + + indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); + } + + @Override + public float getDestroySpeed(BlockState blockState) { + return this.items.get(this.selected).getDestroySpeed(blockState); + } + + @Override + public ListTag save(ListTag listTag) { + for (int i = 0; i < this.items.size(); ++i) { + if (!this.items.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)i); + this.items.get(i).save(this.player.registryAccess(), compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.armor.size(); ++i) { + if (!this.armor.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 100)); + this.armor.get(i).save(this.player.registryAccess(), compoundTag); + listTag.add(compoundTag); + } + } + + for (int i = 0; i < this.offhand.size(); ++i) { + if (!this.offhand.get(i).isEmpty()) { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putByte("Slot", (byte)(i + 150)); + this.offhand.get(i).save(this.player.registryAccess(), compoundTag); + listTag.add(compoundTag); + } + } + + return listTag; + } + + @Override + public void load(ListTag listTag) { + this.items.clear(); + this.armor.clear(); + this.offhand.clear(); + + for(int i = 0; i < listTag.size(); ++i) { + CompoundTag compoundTag = listTag.getCompound(i); + int j = compoundTag.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY); + if (j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.offhand.size() + 150) { + this.offhand.set(j - 150, itemstack); + } + } + + } + + @Override + public int getContainerSize() { + return 45; + } + + @Override + public boolean isEmpty() { + return !contains(itemStack -> !itemStack.isEmpty()); + } + + @Override + public ItemStack getItem(int rawIndex) { + IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); + + if (indexedCompartment.compartment() == null) { + return ItemStack.EMPTY; + } + + return indexedCompartment.compartment().get(indexedCompartment.index()); + } + + @Override + public Component getName() { + return this.player.getName(); + } + + @Override + public ItemStack getArmor(int index) { + return this.armor.get(index); + } + + @Override + public void dropAll() { + for (NonNullList compartment : this.compartments) { + for (int i = 0; i < compartment.size(); ++i) { + ItemStack itemstack = compartment.get(i); + if (!itemstack.isEmpty()) { + this.player.drop(itemstack, true, false); + compartment.set(i, ItemStack.EMPTY); + } + } + } + } + + @Override + public void setChanged() { + super.setChanged(); + } + + @Override + public int getTimesChanged() { + return super.getTimesChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public boolean contains(ItemStack itemstack) { + return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack)); + } + + @Override + public boolean contains(TagKey tagKey) { + return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); + } + + @Override + public void replaceWith(Inventory inventory) { + Function getter; + + if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { + getter = specialPlayerInventory::getRawItem; + } else { + getter = inventory::getItem; + } + + for(int i = 0; i < this.getContainerSize(); ++i) { + this.setRawItem(i, getter.apply(i)); + } + + this.selected = inventory.selected; + } + + @Override + public void clearContent() { + for (NonNullList compartment : this.compartments) { + compartment.clear(); + } + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : this.items) { + stackedContents.accountSimpleStack(itemstack); + } + } + + @Override + public ItemStack removeFromSelected(boolean dropWholeStack) { + ItemStack itemstack = this.getSelected(); + return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index 778589c9..a04ac0c3 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -59,20 +59,21 @@ private void checkSupported() { } private @Nullable String getVersionPackage() { - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20, 3))) { // Min supported version: 1.20.3 + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20, 4)) // Min supported version: 1.20.4 + || BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 not supported at all. return null; } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 4))) { // 1.20.3, 1.20.4 + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) { // 1.20.4 return "v1_20_R3"; } - if (BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21))) { - return null; + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 6))) { // 1.20.6 + return "v1_20_R4"; } - if (BukkitVersions.MINECRAFT.greaterThan(Version.of(1, 20, 6))) { - return null; // TODO: use at your own risk flag? + if (BukkitVersions.MINECRAFT.greaterThan(Version.of(1, 21))) { + return null; } - // 1.20.5, 1.20.6 - return "v1_20_R4"; + // 1.21 + return "v1_21_R1"; } public String getReleasesLink() { @@ -136,9 +137,12 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 1))) { // 1.20, 1.20.1 return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 3))) { // 1.20.3 + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3 return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 shouldn't be used. + return "https://github.com/Jikoo/OpenInv/releases"; + } return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 9ff7ee5c..6ab067ec 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -33,18 +33,6 @@ import com.lishid.openinv.util.StringMetric; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.logging.Level; -import java.util.stream.Stream; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; @@ -63,6 +51,19 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.stream.Stream; + /** * Open other player's inventory * @@ -136,7 +137,6 @@ public void onEnable() { Class.forName("org.bukkit.entity.Player$Spigot"); isSpigot = true; } catch (ClassNotFoundException e) { - e.printStackTrace(); isSpigot = false; } @@ -318,7 +318,7 @@ public boolean isPlayerLoaded(@NotNull UUID playerUuid) { try { player = future.get(); } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); + getLogger().log(Level.WARNING, e.getMessage(), e); return null; } @@ -510,7 +510,7 @@ public void sendSystemMessage(@NotNull Player player, @NotNull String key) { return; } - player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message)); } /** diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index ea7c438e..c5743a77 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -18,7 +18,6 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.lang.Replacement; -import java.util.Objects; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -26,6 +25,8 @@ import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + public class OpenInventoryView extends InventoryView { private final @NotNull Player player; @@ -68,17 +69,28 @@ public OpenInventoryView( @Override public @NotNull String getTitle() { if (title == null) { - HumanEntity owner = inventory.getPlayer(); - - String localTitle = OpenInv.getPlugin(OpenInv.class) - .getLocalizedMessage( - player, - titleKey, - new Replacement("%player%", owner.getName())); - title = Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix); + title = getOriginalTitle(); } return title; } + @NotNull + @Override + public String getOriginalTitle() { + HumanEntity owner = inventory.getPlayer(); + + String localTitle = OpenInv.getPlugin(OpenInv.class) + .getLocalizedMessage( + player, + titleKey, + new Replacement("%player%", owner.getName())); + return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix); + } + + @Override + public void setTitle(@NotNull String title) { + this.title = title; + } + } diff --git a/pom.xml b/pom.xml index d8ad20b3..a83101f7 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ plugin internal/v1_20_R3 internal/v1_20_R4 + internal/v1_21_R1 assembly @@ -82,7 +83,7 @@ spigot-api org.spigotmc provided - 1.18.2-R0.1-SNAPSHOT + 1.20.4-R0.1-SNAPSHOT openinvapi @@ -105,7 +106,7 @@ planarwrappers com.github.jikoo - 3.2.2 + 3.2.3 From f1bcb496cf7483fbef4989d2c64404f4836c9f68 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 14 Jun 2024 12:05:28 -0400 Subject: [PATCH 176/340] Bump version to 4.4.6 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index ae7cb8de..d7cae654 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.6-SNAPSHOT + 4.4.6 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index a18b2960..d71d5b95 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.6-SNAPSHOT + 4.4.6 openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 97250bfb..ea3375e7 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.6-SNAPSHOT + 4.4.6 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 2c28382c..6c9efcd5 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.6-SNAPSHOT + 4.4.6 openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 165be1a2..14b5ee59 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.6-SNAPSHOT + 4.4.6 openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 06f13fce..86a94cd2 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.6-SNAPSHOT + 4.4.6 openinvplugincore diff --git a/pom.xml b/pom.xml index a83101f7..ee0d02d5 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.6-SNAPSHOT + 4.4.6 pom @@ -89,13 +89,13 @@ openinvapi com.lishid compile - 4.4.6-SNAPSHOT + 4.4.6 openinvplugincore com.lishid compile - 4.4.6-SNAPSHOT + 4.4.6 com.lishid From f72b6749c4f9c9430e3caf5b5575d1d863d116c7 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 14 Jun 2024 12:05:47 -0400 Subject: [PATCH 177/340] Bump version to 4.4.7-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d7cae654..f7de83d4 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.6 + 4.4.7-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index d71d5b95..da58fcd9 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.6 + 4.4.7-SNAPSHOT openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index ea3375e7..b6e36dfd 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.6 + 4.4.7-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 6c9efcd5..cf6cc6dc 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.6 + 4.4.7-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 14b5ee59..4e9a2834 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.6 + 4.4.7-SNAPSHOT openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 86a94cd2..cddcc8b4 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.6 + 4.4.7-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index ee0d02d5..76bda9af 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.6 + 4.4.7-SNAPSHOT pom @@ -89,13 +89,13 @@ openinvapi com.lishid compile - 4.4.6 + 4.4.7-SNAPSHOT openinvplugincore com.lishid compile - 4.4.6 + 4.4.7-SNAPSHOT com.lishid From e97ac3db88b89d83a3a0d59b290f63ea003d070c Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 16 Jun 2024 11:47:09 -0400 Subject: [PATCH 178/340] Fix inventory view construction on 1.21 (#208) --- .../openinv/internal/IAnySilentContainer.java | 2 +- .../internal/v1_20_R3/AnySilentContainer.java | 38 ------------ .../internal/v1_20_R3/PlayerDataManager.java | 18 ++---- .../internal/v1_20_R4/PlayerDataManager.java | 18 ++---- .../internal/v1_21_R1/OpenCraftView.java | 59 +++++++++++++++++++ .../internal/v1_21_R1/PlayerDataManager.java | 38 ++++++------ .../openinv/internal/InventoryViewTitle.java | 46 +++++++++++++++ .../openinv/internal/OpenInventoryView.java | 31 ++-------- 8 files changed, 143 insertions(+), 107 deletions(-) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java create mode 100644 plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 9c53d825..99026bc3 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -95,7 +95,7 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { } BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Chest chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) { + if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) { return false; } diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java index 418abf35..eb6d8fef 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java @@ -21,12 +21,10 @@ import com.lishid.openinv.util.ReflectionHelper; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.entity.monster.Shulker; import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.PlayerEnderChestContainer; @@ -39,15 +37,10 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Statistic; -import org.bukkit.block.ShulkerBox; -import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,37 +72,6 @@ public AnySilentContainer() { } } - @Override - public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { - org.bukkit.World bukkitWorld = shulkerBox.getWorld(); - if (!(bukkitWorld instanceof CraftWorld)) { - bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID()); - } - - if (!(bukkitWorld instanceof CraftWorld craftWorld)) { - Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!"); - OpenInv.getPlugin(OpenInv.class).getLogger().log(java.util.logging.Level.WARNING, exception.getMessage(), exception); - return false; - } - - final ServerLevel world = craftWorld.getHandle(); - final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ()); - final BlockEntity tile = world.getBlockEntity(blockPosition); - - if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) - || shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) { - return false; - } - - BlockState blockState = world.getBlockState(blockPosition); - - // See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen - AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F) - .move(blockPosition) - .deflate(1.0E-6D); - return !world.noCollision(boundingBox); - } - @Override public boolean activateContainer( @NotNull final Player bukkitPlayer, diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java index 9d02c87b..bee0f2ea 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java @@ -19,6 +19,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; @@ -250,12 +251,13 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return null; } - InventoryView view = getView(player, inventory); + InventoryViewTitle title = InventoryViewTitle.of(inventory); - if (view == null) { + if (title == null) { return player.openInventory(inventory.getBukkitInventory()); } + InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(player, inventory)); AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override public MenuType getType() { @@ -263,7 +265,7 @@ public MenuType getType() { } }; - container.setTitle(Component.literal(view.getTitle())); + container.setTitle(Component.literal(view.getOriginalTitle())); container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); if (container == null) { @@ -279,16 +281,6 @@ public MenuType getType() { } - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - static @NotNull MenuType getContainers(int inventorySize) { return switch (inventorySize) { diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java index d058c5b8..1e906f9d 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java @@ -19,6 +19,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; @@ -250,12 +251,13 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return null; } - InventoryView view = getView(player, inventory); + InventoryViewTitle title = InventoryViewTitle.of(inventory); - if (view == null) { + if (title == null) { return player.openInventory(inventory.getBukkitInventory()); } + InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(player, inventory)); AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override public MenuType getType() { @@ -263,7 +265,7 @@ public MenuType getType() { } }; - container.setTitle(Component.literal(view.getTitle())); + container.setTitle(Component.literal(view.getOriginalTitle())); container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); if (container == null) { @@ -279,16 +281,6 @@ public MenuType getType() { } - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - static @NotNull MenuType getContainers(int inventorySize) { return switch (inventorySize) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java new file mode 100644 index 00000000..d2a003ed --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java @@ -0,0 +1,59 @@ +package com.lishid.openinv.internal.v1_21_R1; + +import com.lishid.openinv.internal.ISpecialInventory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftAbstractInventoryView; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; + +class OpenCraftView extends CraftAbstractInventoryView { + + private final @NotNull Player player; + private final @NotNull ISpecialInventory inventory; + private final @NotNull String originalTitle; + private String title = null; + + OpenCraftView(@NotNull Player player, @NotNull ISpecialInventory inventory, @NotNull String originalTitle) { + this.player = player; + this.inventory = inventory; + this.originalTitle = originalTitle; + } + + @Override + public @NotNull Inventory getTopInventory() { + return inventory.getBukkitInventory(); + } + + @Override + public @NotNull Inventory getBottomInventory() { + return player.getInventory(); + } + + @Override + public @NotNull HumanEntity getPlayer() { + return player; + } + + @Override + public @NotNull InventoryType getType() { + return inventory.getBukkitInventory().getType(); + } + + @Override + public @NotNull String getTitle() { + return title == null ? originalTitle : title; + } + + @Override + public @NotNull String getOriginalTitle() { + return originalTitle; + } + + @Override + public void setTitle(@NotNull String title) { + this.title = title; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java index a66f3ddf..7709679e 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java @@ -16,9 +16,11 @@ package com.lishid.openinv.internal.v1_21_R1; +import com.github.jikoo.planarwrappers.function.TriFunction; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; @@ -58,6 +60,7 @@ public class PlayerDataManager implements IPlayerDataManager { private static boolean paper; + private static TriFunction viewProvider; static { try { @@ -66,6 +69,12 @@ public class PlayerDataManager implements IPlayerDataManager { } catch (ClassNotFoundException ignored) { paper = false; } + try { + Class.forName(Bukkit.getServer().getClass().getPackageName() + ".inventory.CraftAbstractInventoryView"); + viewProvider = OpenCraftView::new; + } catch (ClassNotFoundException ignored) { + viewProvider = OpenInventoryView::new; + } } private @Nullable Field bukkitEntity; @@ -250,28 +259,33 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return null; } - InventoryView view = getView(player, inventory); + InventoryViewTitle viewTitle = InventoryViewTitle.of(inventory); - if (view == null) { + if (viewTitle == null) { return player.openInventory(inventory.getBukkitInventory()); } + String originalTitle = viewTitle.getTitle(player, inventory); + InventoryView view = viewProvider.apply(player, inventory, originalTitle); + Component title = Component.literal(originalTitle); AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override public MenuType getType() { return getContainers(inventory.getBukkitInventory().getSize()); } }; + container.setTitle(title); - container.setTitle(Component.literal(view.getTitle())); container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); if (container == null) { return null; } - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - Component.literal(container.getBukkitView().getTitle()))); + // Note: Reusing component prevents plugins from changing title during InventoryOpenEvent, but there's not much + // we can do about that - we can't call InventoryView#getTitle on older versions without causing an + // IncompatibleClassChangeError due to the fact that InventoryView is now an interface. + nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), title)); nmsPlayer.containerMenu = container; nmsPlayer.initMenu(container); @@ -279,23 +293,13 @@ public MenuType getType() { } - private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) { - if (inventory instanceof SpecialEnderChest) { - return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest"); - } else if (inventory instanceof SpecialPlayerInventory) { - return new OpenInventoryView(player, inventory, "container.player", "'s Inventory"); - } else { - return null; - } - } - static @NotNull MenuType getContainers(int inventorySize) { return switch (inventorySize) { case 9 -> MenuType.GENERIC_9x1; case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; // PLAYER - case 41, 45 -> MenuType.GENERIC_9x5; + case 36 -> MenuType.GENERIC_9x4; + case 41, 45 -> MenuType.GENERIC_9x5; // PLAYER case 54 -> MenuType.GENERIC_9x6; default -> MenuType.GENERIC_9x3; // Default 27-slot inventory }; diff --git a/plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java b/plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java new file mode 100644 index 00000000..7bff29a1 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java @@ -0,0 +1,46 @@ +package com.lishid.openinv.internal; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.util.lang.Replacement; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public enum InventoryViewTitle { + + PLAYER_INVENTORY("container.player", "'s Inventory"), + ENDER_CHEST("container.enderchest", "'s Ender Chest"); + + private final String localizationKey; + private final String defaultSuffix; + + InventoryViewTitle(String localizationKey, String defaultSuffix) { + this.localizationKey = localizationKey; + this.defaultSuffix = defaultSuffix; + } + + public @NotNull String getTitle(@NotNull Player viewer, @NotNull ISpecialInventory inventory) { + HumanEntity owner = inventory.getPlayer(); + + String localTitle = OpenInv.getPlugin(OpenInv.class) + .getLocalizedMessage( + viewer, + localizationKey, + new Replacement("%player%", owner.getName())); + return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + defaultSuffix); + } + + public static @Nullable InventoryViewTitle of(@NotNull ISpecialInventory inventory) { + if (inventory instanceof ISpecialPlayerInventory) { + return PLAYER_INVENTORY; + } else if (inventory instanceof ISpecialEnderChest) { + return ENDER_CHEST; + } else { + return null; + } + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java index c5743a77..618cdc01 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java @@ -16,8 +16,6 @@ package com.lishid.openinv.internal; -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.util.lang.Replacement; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -25,25 +23,20 @@ import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; -import java.util.Objects; - public class OpenInventoryView extends InventoryView { private final @NotNull Player player; private final @NotNull ISpecialInventory inventory; - private final @NotNull String titleKey; - private final @NotNull String titleDefaultSuffix; + private final @NotNull String originalTitle; private String title; public OpenInventoryView( @NotNull Player player, @NotNull ISpecialInventory inventory, - @NotNull String titleKey, - @NotNull String titleDefaultSuffix) { + @NotNull String originalTitle) { this.player = player; this.inventory = inventory; - this.titleKey = titleKey; - this.titleDefaultSuffix = titleDefaultSuffix; + this.originalTitle = originalTitle; } @Override @@ -68,24 +61,12 @@ public OpenInventoryView( @Override public @NotNull String getTitle() { - if (title == null) { - title = getOriginalTitle(); - } - - return title; + return title == null ? originalTitle : title; } - @NotNull @Override - public String getOriginalTitle() { - HumanEntity owner = inventory.getPlayer(); - - String localTitle = OpenInv.getPlugin(OpenInv.class) - .getLocalizedMessage( - player, - titleKey, - new Replacement("%player%", owner.getName())); - return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix); + public @NotNull String getOriginalTitle() { + return originalTitle; } @Override From 7a07ee0b3d823ec23fd9e148b38a6cd6e9a54bb1 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 16 Jun 2024 11:53:21 -0400 Subject: [PATCH 179/340] Bump version to 4.4.7 for release --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index f7de83d4..5b83d3cd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.7-SNAPSHOT + 4.4.7 openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index da58fcd9..982874f4 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.7-SNAPSHOT + 4.4.7 openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index b6e36dfd..155f3511 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.7-SNAPSHOT + 4.4.7 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index cf6cc6dc..4579d2ca 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.7-SNAPSHOT + 4.4.7 openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 4e9a2834..9ba3febf 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.7-SNAPSHOT + 4.4.7 openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index cddcc8b4..0b3353f0 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.7-SNAPSHOT + 4.4.7 openinvplugincore diff --git a/pom.xml b/pom.xml index 76bda9af..e9b1c14d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.7-SNAPSHOT + 4.4.7 pom @@ -89,13 +89,13 @@ openinvapi com.lishid compile - 4.4.7-SNAPSHOT + 4.4.7 openinvplugincore com.lishid compile - 4.4.7-SNAPSHOT + 4.4.7 com.lishid From 6325b353dc46f3c77a650f026c67fc9016f27574 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 16 Jun 2024 11:53:43 -0400 Subject: [PATCH 180/340] Bump version to 4.4.8-SNAPSHOT for development --- api/pom.xml | 2 +- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 5b83d3cd..729663db 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.7 + 4.4.8-SNAPSHOT openinvapi diff --git a/assembly/pom.xml b/assembly/pom.xml index 982874f4..ad00efb5 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.7 + 4.4.8-SNAPSHOT openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 155f3511..025b8efd 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.7 + 4.4.8-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 4579d2ca..6d47ee7a 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.7 + 4.4.8-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 9ba3febf..22582fb1 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.7 + 4.4.8-SNAPSHOT openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 0b3353f0..3eae70a6 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.7 + 4.4.8-SNAPSHOT openinvplugincore diff --git a/pom.xml b/pom.xml index e9b1c14d..3e9393cc 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.7 + 4.4.8-SNAPSHOT pom @@ -89,13 +89,13 @@ openinvapi com.lishid compile - 4.4.7 + 4.4.8-SNAPSHOT openinvplugincore com.lishid compile - 4.4.7 + 4.4.8-SNAPSHOT com.lishid From dfdef394cedfa0f29c45c02c0bdc215caf5b9201 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:56:16 +0000 Subject: [PATCH 181/340] Bump org.apache.maven.plugins:maven-dependency-plugin (#211) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3e9393cc..8ffa96fc 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ --> maven-dependency-plugin org.apache.maven.plugins - 3.6.1 + 3.7.1 From 7cca23f7b18c2f6a0fafc362646b8b8481c61c2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:55:41 -0400 Subject: [PATCH 182/340] Bump softprops/action-gh-release from 2.0.5 to 2.0.6 (#212) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.5 to 2.0.6. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.5...v2.0.6) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index d72eb9df..364186a6 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -28,7 +28,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.0.5 + uses: softprops/action-gh-release@v2.0.6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 09b3f838163b5859d61645415a327402fbc28e09 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Jul 2024 10:11:44 -0400 Subject: [PATCH 183/340] Improve player inventory handling (#209) This is a from-scratch rewrite of the SpecialPlayerInventory. * Prettify layout * Reorganized contents * Uses client-side placeholders in nonstandard slots to clarify usage * Add cursor and crafting slots * Remove duplicate slots from own inventory * Reimplement shift-clicking, improve drag support * Prioritize equipping gear with quick-move * Use client translations for title * Update off-hand when in other containers * Modifications to the off-hand would not be sent to clients while they had another container open. Updates occurred immediately on container exit. --- .../internal/v1_20_R3/PlayerDataManager.java | 28 - .../internal/v1_20_R4/PlayerDataManager.java | 28 - .../internal/v1_21_R1/PlayerDataManager.java | 35 +- .../v1_21_R1/SpecialPlayerInventory.java | 768 ------------------ .../v1_21_R1/inventory/ContainerSlot.java | 69 ++ .../inventory/ContainerSlotCrafting.java | 130 +++ .../ContainerSlotCraftingResult.java | 47 ++ .../inventory/ContainerSlotCursor.java | 146 ++++ .../v1_21_R1/inventory/ContainerSlotDrop.java | 95 +++ .../inventory/ContainerSlotEmpty.java | 162 ++++ .../inventory/ContainerSlotEquipment.java | 120 +++ .../v1_21_R1/inventory/ContainerSlotList.java | 58 ++ .../inventory/ContainerSlotOffHand.java | 47 ++ .../inventory/MenuSlotPlaceholder.java | 80 ++ .../v1_21_R1/inventory/OpenInventory.java | 339 ++++++++ .../v1_21_R1/inventory/OpenInventoryMenu.java | 471 +++++++++++ .../inventory/OpenPlayerInventory.java | 166 ++++ .../com/lishid/openinv/InternalAccessor.java | 21 +- .../com/lishid/openinv/InventoryListener.java | 104 +-- .../openinv/LegacyInventoryListener.java | 214 +++++ .../main/java/com/lishid/openinv/OpenInv.java | 35 +- .../openinv/internal/IPlayerDataManager.java | 12 - pom.xml | 4 +- 23 files changed, 2182 insertions(+), 997 deletions(-) delete mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java create mode 100644 plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java index bee0f2ea..17ed8e4e 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java @@ -293,32 +293,4 @@ public MenuType getType() { }; } - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - } diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java index 1e906f9d..430fa975 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java @@ -293,32 +293,4 @@ public MenuType getType() { }; } - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java index 7709679e..87cd3627 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java @@ -20,6 +20,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; import com.mojang.authlib.GameProfile; @@ -252,6 +253,9 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { @Override public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + if (inventory instanceof ISpecialPlayerInventory) { + return player.openInventory(inventory.getBukkitInventory()); + } ServerPlayer nmsPlayer = getHandle(player); @@ -294,43 +298,14 @@ public MenuType getType() { } static @NotNull MenuType getContainers(int inventorySize) { - return switch (inventorySize) { case 9 -> MenuType.GENERIC_9x1; case 18 -> MenuType.GENERIC_9x2; case 36 -> MenuType.GENERIC_9x4; - case 41, 45 -> MenuType.GENERIC_9x5; // PLAYER + case 41, 45 -> MenuType.GENERIC_9x5; case 54 -> MenuType.GENERIC_9x6; default -> MenuType.GENERIC_9x3; // Default 27-slot inventory }; } - @Override - public int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java deleted file mode 100644 index 4bfb7b1c..00000000 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialPlayerInventory.java +++ /dev/null @@ -1,768 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_21_R1; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.core.NonNullList; -import net.minecraft.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private Player player; - private NonNullList items; - private NonNullList armor; - private NonNullList offhand; - private List> compartments; - - public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.selected = player.getInventory().selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - Player offlinePlayer = this.player; - Player onlinePlayer = PlayerDataManager.getHandle(player); - onlinePlayer.getInventory().transaction.addAll(this.transaction); - - // Set owner to new player. - this.player = onlinePlayer; - - // Set player's inventory contents to our modified contents. - Inventory onlineInventory = onlinePlayer.getInventory(); - for (int i = 0; i < getContainerSize(); ++i) { - onlineInventory.setItem(i, getRawItem(i)); - } - onlineInventory.selected = this.selected; - - // Set our item arrays to the new inventory's arrays. - this.items = onlineInventory.items; - this.armor = onlineInventory.armor; - this.offhand = onlineInventory.offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - - // Add existing viewers to new viewer list. - Inventory offlineInventory = offlinePlayer.getInventory(); - // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. - offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); - onlineInventory.transaction.addAll(offlineInventory.transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - private @NotNull ItemStack getRawItem(int i) { - if (i < 0) { - return ItemStack.EMPTY; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - return list.get(i); - } - } - - return ItemStack.EMPTY; - } - - private void setRawItem(int i, @NotNull ItemStack itemStack) { - if (i < 0) { - return; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - list.set(i, itemStack); - } - } - } - - private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - - private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { - if (index < items.size()) { - return new IndexedCompartment(items, getReversedItemSlotNum(index)); - } - - index -= items.size(); - - if (index < armor.size()) { - return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); - } - - index -= armor.size(); - - if (index < offhand.size()) { - return new IndexedCompartment(offhand, index); - } - - index -= offhand.size(); - - return new IndexedCompartment(null, index); - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - @Override - public boolean contains(Predicate predicate) { - return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.player.getInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.player.getInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.player.getInventory().getViewers(); - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public int getMaxStackSize() { - return this.player.getInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int size) { - this.player.getInventory().setMaxStackSize(size); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public boolean hasCustomName() { - return false; - } - - @Override - public List getContents() { - return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); - } - - @Override - public ItemStack getSelected() { - return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; - } - - private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getRawItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { - remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); - if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { - remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; - } - - @Override - public int getFreeSlot() { - for(int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void setPickedItem(ItemStack itemstack) { - int i = this.findSlotMatchingItem(itemstack); - if (isHotbarSlot(i)) { - this.selected = i; - } else if (i == -1) { - this.selected = this.getSuitableHotbarSlot(); - if (!this.items.get(this.selected).isEmpty()) { - int j = this.getFreeSlot(); - if (j != -1) { - this.items.set(j, this.items.get(this.selected)); - } - } - - this.items.set(this.selected, itemstack); - } else { - this.pickSlot(i); - } - - } - - @Override - public void pickSlot(int i) { - this.selected = this.getSuitableHotbarSlot(); - ItemStack itemstack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int findSlotMatchingItem(ItemStack itemstack) { - for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) { - return i; - } - } - - return -1; - } - - @Override - public int findSlotMatchingUnusedItem(ItemStack itemStack) { - for(int i = 0; i < this.items.size(); ++i) { - ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) { - return i; - } - } - - return -1; - } - - @Override - public int getSuitableHotbarSlot() { - int i; - int j; - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (!this.items.get(i).isEnchanted()) { - return i; - } - } - - return this.selected; - } - - @Override - public void swapPaint(double d0) { - int i = (int) Math.signum(d0); - - this.selected -= i; - - while (this.selected < 0) { - this.selected += 9; - } - - while(this.selected >= 9) { - this.selected -= 9; - } - } - - @Override - public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); - j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); - ItemStack itemstack = this.player.containerMenu.getCarried(); - j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); - if (itemstack.isEmpty()) { - this.player.containerMenu.setCarried(ItemStack.EMPTY); - } - - return j; - } - - private int addResource(ItemStack itemstack) { - int i = this.getSlotWithRemainingSpace(itemstack); - if (i == -1) { - i = this.getFreeSlot(); - } - - return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); - } - - private int addResource(int i, ItemStack itemstack) { - int j = itemstack.getCount(); - ItemStack localItemStack = this.getRawItem(i); - if (localItemStack.isEmpty()) { - localItemStack = itemstack.copyWithCount(0); - this.setRawItem(i, localItemStack); - } - - int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount()); - - if (k != 0) { - j -= k; - localItemStack.grow(k); - localItemStack.setPopTime(5); - } - - return j; - } - - @Override - public int getSlotWithRemainingSpace(ItemStack itemstack) { - if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { - return this.selected; - } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { - return 40; - } else { - for(int i = 0; i < this.items.size(); ++i) { - if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void tick() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (!compartment.get(i).isEmpty()) { - compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i); - } - } - } - - } - - @Override - public boolean add(ItemStack itemStack) { - return this.add(-1, itemStack); - } - - @Override - public boolean add(int i, ItemStack itemStack) { - if (itemStack.isEmpty()) { - return false; - } else { - try { - if (itemStack.isDamaged()) { - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i >= 0) { - this.items.set(i, itemStack.copy()); - this.items.get(i).setPopTime(5); - itemStack.setCount(0); - return true; - } else if (this.player.hasInfiniteMaterials()) { - itemStack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemStack.getCount(); - if (i == -1) { - itemStack.setCount(this.addResource(itemStack)); - } else { - itemStack.setCount(this.addResource(i, itemStack)); - } - } while(!itemStack.isEmpty() && itemStack.getCount() < j); - - if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) { - itemStack.setCount(0); - return true; - } else { - return itemStack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); - crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); - crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); - crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); - throw new ReportedException(crashReport); - } - } - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack) { - this.placeItemBackInInventory(itemStack, true); - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { - while(true) { - if (!itemStack.isEmpty()) { - int i = this.getSlotWithRemainingSpace(itemStack); - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); - if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { - ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); - } - continue; - } - - this.player.drop(itemStack, false); - } - - return; - } - } - - @Override - public ItemStack removeItem(int rawIndex, final int j) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null - || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { - return ItemStack.EMPTY; - } - - return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); - } - - @Override - public void removeItem(ItemStack itemStack) { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (compartment.get(i) == itemStack) { - compartment.set(i, ItemStack.EMPTY); - break; - } - } - } - } - - @Override - public ItemStack removeItemNoUpdate(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); - - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - - return removed; - } - - @Override - public void setItem(int rawIndex, final ItemStack itemStack) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - this.player.drop(itemStack, true); - return; - } - - indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); - } - - @Override - public float getDestroySpeed(BlockState blockState) { - return this.items.get(this.selected).getDestroySpeed(blockState); - } - - @Override - public ListTag save(ListTag listTag) { - for (int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(this.player.registryAccess(), compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(this.player.registryAccess(), compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.offhand.size(); ++i) { - if (!this.offhand.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(this.player.registryAccess(), compoundTag); - listTag.add(compoundTag); - } - } - - return listTag; - } - - @Override - public void load(ListTag listTag) { - this.items.clear(); - this.armor.clear(); - this.offhand.clear(); - - for(int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY); - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } - } - - } - - @Override - public int getContainerSize() { - return 45; - } - - @Override - public boolean isEmpty() { - return !contains(itemStack -> !itemStack.isEmpty()); - } - - @Override - public ItemStack getItem(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - return indexedCompartment.compartment().get(indexedCompartment.index()); - } - - @Override - public Component getName() { - return this.player.getName(); - } - - @Override - public ItemStack getArmor(int index) { - return this.armor.get(index); - } - - @Override - public void dropAll() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - ItemStack itemstack = compartment.get(i); - if (!itemstack.isEmpty()) { - this.player.drop(itemstack, true, false); - compartment.set(i, ItemStack.EMPTY); - } - } - } - } - - @Override - public void setChanged() { - super.setChanged(); - } - - @Override - public int getTimesChanged() { - return super.getTimesChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean contains(ItemStack itemstack) { - return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack)); - } - - @Override - public boolean contains(TagKey tagKey) { - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); - } - - @Override - public void replaceWith(Inventory inventory) { - Function getter; - - if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { - getter = specialPlayerInventory::getRawItem; - } else { - getter = inventory::getItem; - } - - for(int i = 0; i < this.getContainerSize(); ++i) { - this.setRawItem(i, getter.apply(i)); - } - - this.selected = inventory.selected; - } - - @Override - public void clearContent() { - for (NonNullList compartment : this.compartments) { - compartment.clear(); - } - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountSimpleStack(itemstack); - } - } - - @Override - public ItemStack removeFromSelected(boolean dropWholeStack) { - ItemStack itemstack = this.getSelected(); - return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); - } - -} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java new file mode 100644 index 00000000..8c003095 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java @@ -0,0 +1,69 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate slot reordering. + */ +interface ContainerSlot { + + /** + * Update internal holder. + * + * @param holder the new holder + */ + void setHolder(@NotNull ServerPlayer holder); + + /** + * Get the current item. May be fake for display purposes! + * + * @return the current item + */ + ItemStack get(); + + /** + * Remove the current item. Item removed is real. + * + * @return the current item + */ + ItemStack remove(); + + /** + * Remove some of the current item. Item removed is real. + * + * @return the current item + */ + ItemStack removePartial(int amount); + + /** + * Set the current item. If slot is currently not usable, will drop item instead. + * + * @param itemStack the item to set + */ + void set(ItemStack itemStack); + + /** + * Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will + * impose any specific restrictions to insertion or removal. + * + * @param container the backing container + * @param index the slot of the backing container represented + * @param x clientside x dimension from top left of inventory, not used + * @param y clientside y dimension from top left of inventory, not used + * @return a menu slot + */ + Slot asMenuSlot(Container container, int index, int x, int y); + + /** + * Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather + * than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}. + * + * @return the closes Bukkit slot type + */ + org.bukkit.event.inventory.InventoryType.SlotType getSlotType(); + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java new file mode 100644 index 00000000..02462dce --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java @@ -0,0 +1,130 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * A slot in a survival crafting inventory. Unavailable when not online in a survival mode. + */ +class ContainerSlotCrafting implements ContainerSlot { + + private final int index; + private ServerPlayer holder; + private List items; + + ContainerSlotCrafting(@NotNull ServerPlayer holder, int index) { + setHolder(holder); + this.index = index; + } + + private boolean isAvailable() { + return isAvailable(holder); + } + + static boolean isAvailable(@NotNull ServerPlayer holder) { + // Player must be online and not in creative - since the creative client is (semi-)authoritative, + // it ignores changes without extra help, and will delete the item as a result. + // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. + return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + // Note: CraftingContainer#getItems is immutable! Be careful with updates. + this.items = holder.inventoryMenu.getCraftSlots().getContents(); + } + + @Override + public ItemStack get() { + return isAvailable() ? items.get(index) : ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + if (!this.isAvailable()) { + return ItemStack.EMPTY; + } + ItemStack removed = items.remove(index); + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); + return removed; + } + + @Override + public ItemStack removePartial(int amount) { + if (!this.isAvailable()) { + return ItemStack.EMPTY; + } + ItemStack removed = ContainerHelper.removeItem(items, index, amount); + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); + return removed; + } + + @Override + public void set(ItemStack itemStack) { + if (isAvailable()) { + items.set(index, itemStack); + holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); + } else { + holder.drop(itemStack, false); + } + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new SlotCrafting(container, index, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE; + } + + class SlotCrafting extends MenuSlotPlaceholder { + + private SlotCrafting(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + ItemStack getOrDefault() { + return isAvailable() ? items.get(ContainerSlotCrafting.this.index) : survivalOnly(holder); + } + + @Override + public boolean mayPickup(Player player) { + return isAvailable(); + } + + @Override + public boolean mayPlace(ItemStack itemStack) { + return isAvailable(); + } + + @Override + public boolean hasItem() { + return isAvailable() && super.hasItem(); + } + + @Override + public boolean isFake() { + return !isAvailable(); + } + + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java new file mode 100644 index 00000000..b43b3185 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java @@ -0,0 +1,47 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot allowing viewing of the crafting result. + * + *

Unmodifiable because I said so. Use your own crafting grid.

+ */ +class ContainerSlotCraftingResult extends ContainerSlotEmpty { + + ContainerSlotCraftingResult(@NotNull ServerPlayer holder) { + super(holder); + } + + @Override + public ItemStack get() { + InventoryMenu inventoryMenu = holder.inventoryMenu; + return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new ContainerSlotEmpty.SlotEmpty(container, index, x, y) { + @Override + ItemStack getOrDefault() { + if (!ContainerSlotCrafting.isAvailable(holder)) { + return survivalOnly(holder); + } + InventoryMenu inventoryMenu = holder.inventoryMenu; + return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); + } + }; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.RESULT; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java new file mode 100644 index 00000000..f570fa08 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java @@ -0,0 +1,146 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Unit; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.entity.BannerPattern; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.entity.BannerPatterns; +import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode. + */ +class ContainerSlotCursor implements ContainerSlot { + + private static final ItemStack PLACEHOLDER; + + static { + PLACEHOLDER = new ItemStack(Items.WHITE_BANNER); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT); + BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT); + BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER); + PLACEHOLDER.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); + PLACEHOLDER.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + } + + private @NotNull ServerPlayer holder; + + ContainerSlotCursor(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + ItemStack carried = holder.containerMenu.getCarried(); + holder.containerMenu.setCarried(ItemStack.EMPTY); + return carried; + } + + @Override + public ItemStack removePartial(int amount) { + ItemStack carried = holder.containerMenu.getCarried(); + if (!carried.isEmpty() && carried.getCount() >= amount) { + ItemStack value = carried.split(amount); + if (carried.isEmpty()) { + holder.containerMenu.setCarried(ItemStack.EMPTY); + } + return value; + } + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + if (isAvailable()) { + holder.containerMenu.setCarried(itemStack); + } else { + holder.drop(itemStack, false); + } + } + + private boolean isAvailable() { + // Player must be online and not in creative - since the creative client is (semi-)authoritative, + // it ignores changes without extra help, and will delete the item as a result. + // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. + return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new SlotCursor(container, index, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + // As close as possible to "not real" + return InventoryType.SlotType.OUTSIDE; + } + + class SlotCursor extends MenuSlotPlaceholder { + + private SlotCursor(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + ItemStack getOrDefault() { + if (!isAvailable()) { + return survivalOnly(holder); + } + ItemStack carried = holder.containerMenu.getCarried(); + return carried.isEmpty() ? PLACEHOLDER : carried; + } + + @Override + public boolean mayPickup(Player player) { + return isAvailable(); + } + + @Override + public boolean mayPlace(ItemStack itemStack) { + return isAvailable(); + } + + @Override + public boolean hasItem() { + return isAvailable() && super.hasItem(); + } + + @Override + public boolean isFake() { + return true; + } + + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java new file mode 100644 index 00000000..f2d41e58 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java @@ -0,0 +1,95 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A fake slot used to drop items. Unavailable offline. + */ +class ContainerSlotDrop implements ContainerSlot { + + private static final ItemStack DROP; + private ServerPlayer holder; + + static { + DROP = new ItemStack(Items.DROPPER); + // Note: translatable component, not keybind component! We want the text identifying the keybind, not the key. + DROP.set(DataComponents.CUSTOM_NAME, Component.translatable("key.drop").withStyle(style -> style.withItalic(false))); + } + + ContainerSlotDrop(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack removePartial(int amount) { + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + holder.drop(itemStack, true); + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new SlotDrop(container, index, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + // Behaves like dropping an item outside the screen, just by the target player. + return InventoryType.SlotType.OUTSIDE; + } + + class SlotDrop extends MenuSlotPlaceholder { + + private SlotDrop(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + ItemStack getOrDefault() { + return holder.connection != null && !holder.connection.isDisconnected() ? DROP : OFFLINE; + } + + @Override + public boolean mayPlace(ItemStack itemStack) { + return holder.connection != null && !holder.connection.isDisconnected(); + } + + @Override + public boolean hasItem() { + return false; + } + + @Override + public boolean isFake() { + return true; + } + + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java new file mode 100644 index 00000000..265999b3 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java @@ -0,0 +1,162 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Unit; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * A fake slot used to fill unused spaces in the inventory. + */ +class ContainerSlotEmpty implements ContainerSlot { + + private static final ItemStack PLACEHOLDER; + + static { + PLACEHOLDER = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); + PLACEHOLDER.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + } + + @NotNull ServerPlayer holder; + + ContainerSlotEmpty(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack removePartial(int amount) { + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + this.holder.drop(itemStack, false); + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new SlotEmpty(container, index, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.OUTSIDE; + } + + static class SlotEmpty extends MenuSlotPlaceholder { + + SlotEmpty(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + ItemStack getOrDefault() { + return PLACEHOLDER; + } + + public void onQuickCraft(ItemStack var0, ItemStack var1) {} + + public void onTake(Player var0, ItemStack var1) {} + + @Override + public boolean mayPlace(ItemStack var0) { + return false; + } + + public ItemStack getItem() { + return ItemStack.EMPTY; + } + + @Override + public boolean hasItem() { + return false; + } + + public void setByPlayer(ItemStack newStack) {} + + public void setByPlayer(ItemStack newStack, ItemStack oldStack) {} + + public void set(ItemStack var0) {} + + public void setChanged() {} + + public int getMaxStackSize() { + return 0; + } + + public int getMaxStackSize(ItemStack itemStack) { + return 0; + } + + public ItemStack remove(int amount) { + return ItemStack.EMPTY; + } + + @Override + public boolean mayPickup(Player var0) { + return false; + } + + public boolean isActive() { + return false; + } + + public Optional tryRemove(int var0, int var1, Player var2) { + return Optional.empty(); + } + + public ItemStack safeTake(int var0, int var1, Player var2) { + return ItemStack.EMPTY; + } + + public ItemStack safeInsert(ItemStack itemStack) { + return itemStack; + } + + public ItemStack safeInsert(ItemStack itemStack, int amount) { + return itemStack; + } + + @Override + public boolean allowModification(Player var0) { + return false; + } + + public int getContainerSlot() { + return this.slot; + } + + public boolean isHighlightable() { + return false; + } + + @Override + public boolean isFake() { + return true; + } + + } +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java new file mode 100644 index 00000000..57646fb7 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java @@ -0,0 +1,120 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Unit; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.DyedItemColor; +import net.minecraft.world.level.block.entity.BannerPattern; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.entity.BannerPatterns; +import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * A slot for equipment that displays placeholders if empty. + */ +class ContainerSlotEquipment extends ContainerSlotList { + + private static final ItemStack HELMET; + private static final ItemStack CHESTPLATE; + private static final ItemStack LEGGINGS; + private static final ItemStack BOOTS; + private static final ItemStack SHIELD; + + static { + HELMET = new ItemStack(Items.LEATHER_HELMET); + // Inventory-background-grey-ish + DyedItemColor color = new DyedItemColor(0xC8C8C8, false); + HELMET.set(DataComponents.DYED_COLOR, color); + HELMET.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + + CHESTPLATE = new ItemStack(Items.LEATHER_CHESTPLATE); + CHESTPLATE.set(DataComponents.DYED_COLOR, color); + CHESTPLATE.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + + LEGGINGS = new ItemStack(Items.LEATHER_LEGGINGS); + LEGGINGS.set(DataComponents.DYED_COLOR, color); + LEGGINGS.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + + BOOTS = new ItemStack(Items.LEATHER_BOOTS); + BOOTS.set(DataComponents.DYED_COLOR, color); + BOOTS.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + + SHIELD = new ItemStack(Items.SHIELD); + SHIELD.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL); + BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT); + BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT); + BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT); + BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT); + SHIELD.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); + SHIELD.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + } + + private final ItemStack placeholder; + private final EquipmentSlot equipmentSlot; + + ContainerSlotEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { + super(holder, index, InventoryType.SlotType.ARMOR); + placeholder = switch (equipmentSlot) { + case HEAD -> HELMET; + case CHEST -> CHESTPLATE; + case LEGS -> LEGGINGS; + case FEET -> BOOTS; + default -> SHIELD; + }; + this.equipmentSlot = equipmentSlot; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.items = holder.getInventory().armor; + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new SlotEquipment(container, index, x, y); + } + + class SlotEquipment extends MenuSlotPlaceholder { + + SlotEquipment(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + ItemStack getOrDefault() { + ItemStack itemStack = getItem(); + if (!itemStack.isEmpty()) { + return itemStack; + } + return placeholder; + } + + EquipmentSlot getEquipmentSlot() { + return equipmentSlot; + } + + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java new file mode 100644 index 00000000..15c0e069 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java @@ -0,0 +1,58 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; + +import java.util.List; + +/** + * A normal slot backed by an item list. + */ +abstract class ContainerSlotList implements ContainerSlot { + + private final int index; + private final InventoryType.SlotType slotType; + List items; + + ContainerSlotList(ServerPlayer holder, int index, InventoryType.SlotType slotType) { + this.index = index; + this.slotType = slotType; + setHolder(holder); + } + + @Override + public ItemStack get() { + return items.get(index); + } + + @Override + public ItemStack remove() { + ItemStack removed = items.remove(index); + return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed; + } + + @Override + public ItemStack removePartial(int amount) { + return ContainerHelper.removeItem(items, index, amount); + } + + @Override + public void set(ItemStack itemStack) { + items.set(index, itemStack); + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new Slot(container, index, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return slotType; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java new file mode 100644 index 00000000..2243fc18 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java @@ -0,0 +1,47 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.Slot; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot for equipment that updates held items if necessary. + */ +class ContainerSlotOffHand extends ContainerSlotEquipment { + + private ServerPlayer holder; + + public ContainerSlotOffHand(ServerPlayer holder, int localIndex) { + super(holder, localIndex, EquipmentSlot.OFFHAND); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.items = holder.getInventory().offhand; + this.holder = holder; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.QUICKBAR; + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new SlotEquipment(container, index, x, y) { + @Override + public void setChanged() { + if (holder.connection != null + && !holder.connection.isDisconnected() + && holder.containerMenu != holder.inventoryMenu) { + holder.resendItemInHands(); + } + } + + }; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java new file mode 100644 index 00000000..898c6086 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java @@ -0,0 +1,80 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; +import org.jetbrains.annotations.NotNull; + +/** + * An implementation of a slot as used by a menu that may have fake placeholder items. + * + *

Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.

+ */ +abstract class MenuSlotPlaceholder extends Slot { + + static final ItemStack OFFLINE; + private static final ItemStack CREATIVE; + private static final ItemStack SPECTATOR; + + static { + // Barrier: "Not available - Offline" + OFFLINE = new ItemStack(Items.BARRIER); + OFFLINE.set(DataComponents.CUSTOM_NAME, + Component.translatable("options.narrator.notavailable") + .withStyle(style -> style.withItalic(false)) + .append(Component.literal(" - ")) + .append(Component.translatable("gui.socialInteractions.status_offline"))); + // Barrier: "Not available - Creative Mode" + CREATIVE = new ItemStack(Items.BARRIER); + CREATIVE.set( + DataComponents.CUSTOM_NAME, + Component.translatable("options.narrator.notavailable") + .withStyle(style -> style.withItalic(false)) + .append(" - ") + .append(GameType.CREATIVE.getLongDisplayName())); + // Barrier: "Not available - Spectator Mode" + SPECTATOR = new ItemStack(Items.BARRIER); + SPECTATOR.set( + DataComponents.CUSTOM_NAME, + Component.translatable("options.narrator.notavailable") + .withStyle(style -> style.withItalic(false)) + .append(" - ") + .append(GameType.SPECTATOR.getLongDisplayName())); + } + + public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { + if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { + return OFFLINE; + } + + GameType gameType = serverPlayer.gameMode.getGameModeForPlayer(); + return switch (gameType) { + case CREATIVE -> CREATIVE; + case SPECTATOR -> SPECTATOR; + // Just in case, fall through to creating new items. + // This is a lot less good - inventory syncher will create copies frequently. + default -> { + ItemStack itemStack = new ItemStack(Items.BARRIER); + itemStack.set( + DataComponents.CUSTOM_NAME, + Component.translatable("options.narrator.notavailable") + .withStyle(style -> style.withItalic(false)) + .append(" - ") + .append(gameType.getLongDisplayName())); + yield itemStack; + } + }; + } + + MenuSlotPlaceholder(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + abstract ItemStack getOrDefault(); + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java new file mode 100644 index 00000000..a96513ac --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java @@ -0,0 +1,339 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.v1_21_R1.PlayerDataManager; +import net.minecraft.core.NonNullList; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.Nameable; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class OpenInventory implements Container, Nameable, MenuProvider, ISpecialPlayerInventory { + + private final List slots; + private final int size; + private ServerPlayer owner; + private int maxStackSize = 99; + private CraftInventory bukkitEntity; + public List transaction = new ArrayList<>(); + + public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { + owner = PlayerDataManager.getHandle(bukkitPlayer); + + // Get total size, rounding up to nearest 9 for client compatibility. + int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; + size = ((int) Math.ceil(rawSize / 9.0)) * 9; + + slots = NonNullList.withSize(size, new ContainerSlotEmpty(owner)); + setupSlots(); + } + + private void setupSlots() { + // Top of inventory: Regular contents. + int nextIndex = addMainInventory(); + + // If inventory is expected size, we can arrange slots to be pretty. + Inventory ownerInv = owner.getInventory(); + if (ownerInv.items.size() == 36 + && ownerInv.armor.size() == 4 + && ownerInv.offhand.size() == 1 + && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) { + // Armor slots: Bottom left. + addArmor(36); + // Off-hand: Below chestplate. + addOffHand(46); + // Drop slot: Bottom right. + slots.set(53, new ContainerSlotDrop(owner)); + // Cursor slot: Above drop. + slots.set(44, new ContainerSlotCursor(owner)); + + // Crafting is displayed in the bottom right corner. + // As we're using the pretty view, this is a 3x2. + addCrafting(41, true); + return; + } + + // Otherwise we'll just add elements linearly. + nextIndex = addArmor(nextIndex); + nextIndex = addOffHand(nextIndex); + nextIndex = addCrafting(nextIndex, false); + slots.set(nextIndex, new ContainerSlotCursor(owner)); + // Drop slot last. + slots.set(slots.size() - 1, new ContainerSlotDrop(owner)); + } + + private int addMainInventory() { + int listSize = owner.getInventory().items.size(); + // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, + // so everything else needs to move up a row. + int hotbarDiff = listSize - 9; + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + InventoryType.SlotType type; + int invIndex; + if (localIndex < hotbarDiff) { + invIndex = localIndex + 9; + type = InventoryType.SlotType.CONTAINER; + } else { + type = InventoryType.SlotType.QUICKBAR; + invIndex = localIndex - hotbarDiff; + } + + slots.set(localIndex, new ContainerSlotList(owner, invIndex, type) { + @Override + public void setHolder(@NotNull ServerPlayer holder) { + items = holder.getInventory().items; + } + }); + } + return listSize; + } + + private int addArmor(int startIndex) { + int listSize = owner.getInventory().armor.size(); + + for (int i = 0; i < listSize; ++i) { + // Armor slots go bottom to top; boots are slot 0, helmet is slot 3. + // Since we have to display horizontally due to space restrictions, + // making the left side the "top" is more user-friendly. + int armorIndex; + EquipmentSlot slot; + switch (i) { + case 3 -> { + armorIndex = 0; + slot = EquipmentSlot.FEET; + } + case 2 -> { + armorIndex = 1; + slot = EquipmentSlot.LEGS; + } + case 1 -> { + armorIndex = 2; + slot = EquipmentSlot.CHEST; + } + case 0 -> { + armorIndex = 3; + slot = EquipmentSlot.HEAD; + } + default -> { + // In the event that new armor slots are added, they can be placed at the end. + armorIndex = i; + slot = EquipmentSlot.MAINHAND; + } + } + + slots.set(startIndex + i, new ContainerSlotEquipment(owner, armorIndex, slot)); + } + + return startIndex + listSize; + } + + private int addOffHand(int startIndex) { + int listSize = owner.getInventory().offhand.size(); + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + slots.set(startIndex + localIndex, new ContainerSlotOffHand(owner, localIndex)); + } + return startIndex + listSize; + } + + private int addCrafting(int startIndex, boolean pretty) { + int listSize = owner.inventoryMenu.getCraftSlots().getContents().size(); + pretty &= listSize == 4; + + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + // Pretty display is a 2x2 rather than linear. + // If index is in top row, grid is not 2x2, or pretty is disabled, just use current index. + // Otherwise, subtract 2 and add 9 to start in the same position on the next row. + int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); + + slots.set(modIndex, new ContainerSlotCrafting(owner, localIndex)); + } + + if (pretty) { + slots.set(startIndex + 2, new ContainerSlotEmpty(owner) { + private static final ItemStack PLACEHOLDER = new ItemStack(Items.CRAFTING_TABLE); + static { + PLACEHOLDER.set(DataComponents.CUSTOM_NAME, Component.translatable("container.crafting").withStyle(style -> style.withItalic(false))); + } + + @Override + public Slot asMenuSlot(Container container, int index, int x, int y) { + return new ContainerSlotEmpty.SlotEmpty(container, index, x, y) { + @Override + ItemStack getOrDefault() { + return PLACEHOLDER; + } + }; + } + }); + slots.set(startIndex + 11, new ContainerSlotCraftingResult(owner)); + } + + return startIndex + listSize; + } + + public Slot getMenuSlot(int index, int x, int y) { + return slots.get(index).asMenuSlot(this, index, x, y); + } + + public InventoryType.SlotType getSlotType(int index) { + return slots.get(index).getSlotType(); + } + + public ServerPlayer getOwnerHandle() { + return owner; + } + + @Override + public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { + if (bukkitEntity == null) { + bukkitEntity = new OpenPlayerInventory(this); + } + return bukkitEntity; + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + ServerPlayer newOwner = PlayerDataManager.getHandle(player); + // Only transfer regular inventory - crafting and cursor slots are transient. + newOwner.getInventory().replaceWith(owner.getInventory()); + owner = newOwner; + // Update slots to point to new inventory. + slots.forEach(slot -> slot.setHolder(newOwner)); + } + + @Override + public void setPlayerOffline() {} + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return getOwner(); + } + + @Override + public int getContainerSize() { + return size; + } + + @Override + public boolean isEmpty() { + return slots.stream().map(ContainerSlot::get).allMatch(ItemStack::isEmpty); + } + + @Override + public ItemStack getItem(int index) { + return slots.get(index).get(); + } + + @Override + public ItemStack removeItem(int index, int amount) { + return slots.get(index).removePartial(amount); + } + + @Override + public ItemStack removeItemNoUpdate(int index) { + return slots.get(index).remove(); + } + + @Override + public void setItem(int index, ItemStack itemStack) { + slots.get(index).set(itemStack); + } + + @Override + public int getMaxStackSize() { + return maxStackSize; + } + + @Override + public void setMaxStackSize(int maxStackSize) { + this.maxStackSize = maxStackSize; + } + + @Override + public void setChanged() {} + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public List getContents() { + NonNullList contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); + for (int i = 0; i < getContainerSize(); ++i) { + contents.set(i, getItem(i)); + } + return contents; + } + + @Override + public void onOpen(CraftHumanEntity viewer) { + transaction.add(viewer); + } + + @Override + public void onClose(CraftHumanEntity viewer) { + transaction.remove(viewer); + } + + @Override + public List getViewers() { + return transaction; + } + + @Override + public org.bukkit.entity.Player getOwner() { + return owner.getBukkitEntity(); + } + + @Override + public Location getLocation() { + return owner.getBukkitEntity().getLocation(); + } + + @Override + public void clearContent() { + owner.getInventory().clearContent(); + owner.inventoryMenu.getCraftSlots().clearContent(); + owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots()); + owner.containerMenu.setCarried(ItemStack.EMPTY); + } + + @Override + public Component getName() { + return Component.translatable("key.categories.inventory").append(" - ").append(owner.getName()); + } + + @Override + public Component getDisplayName() { + return getName(); + } + + @Override + public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenInventoryMenu(this, serverPlayer, i); + } + return null; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java new file mode 100644 index 00000000..e7e70afd --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -0,0 +1,471 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import com.google.common.base.Suppliers; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.ContainerListener; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.DataSlot; +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.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class OpenInventoryMenu extends AbstractContainerMenu { + + private final OpenInventory inventory; + private final ServerPlayer viewer; + private final int topSize; + private final int offset; + private CraftInventoryView bukkitEntity; + + protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) { + super(getMenuType(inventory, viewer), i); + this.inventory = inventory; + this.viewer = viewer; + + int upperRows; + boolean ownInv = inventory.getOwnerHandle().equals(viewer); + if (ownInv) { + // Disallow duplicate access to own main inventory contents. + offset = viewer.getInventory().items.size(); + upperRows = ((int) Math.ceil((inventory.getContainerSize() - offset) / 9.0)); + } else { + offset = 0; + upperRows = inventory.getContainerSize() / 9; + } + + // View's upper inventory - our container + for (int row = 0; row < upperRows; ++row) { + for (int col = 0; col < 9; ++col) { + // x and y for client purposes, but hey, we're thorough here. + // Adapted from net.minecraft.world.inventory.ChestMenu + int x = 8 + col * 18; + int y = 18 + row * 18; + int index = offset + row * 9 + col; + + // Guard against weird inventory sizes. + if (index >= inventory.getContainerSize()) { + addSlot(new ContainerSlotEmpty.SlotEmpty(inventory, index, x, y)); + continue; + } + + Slot slot = inventory.getMenuSlot(index, x, y); + + // Only allow access to own gear slots and drop slot (though really, just click outside the inventory). + if (ownInv && !(slot instanceof ContainerSlotEquipment.SlotEquipment || slot instanceof ContainerSlotDrop.SlotDrop)) { + slot = new ContainerSlotEmpty.SlotEmpty(inventory, index, x, y); + } + addSlot(slot); + } + } + + // View's lower inventory - viewer inventory + int playerInvPad = (upperRows - 4) * 18; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + row * 18 + 103; + addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); + } + } + // Hotbar + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + 161; + addSlot(new Slot(viewer.getInventory(), col, x, y)); + } + + this.topSize = slots.size() - 36; + } + + static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + int size = inventory.getContainerSize(); + if (inventory.getOwnerHandle().equals(viewer)) { + size -= viewer.getInventory().items.size(); + size = ((int) Math.ceil(size / 9.0)) * 9; + } + + return switch (size) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; + case 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + // Default to 27-slot inventory. + default -> MenuType.GENERIC_9x3; + }; + } + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), new OpenPlayerInventory(inventory), this) { + @Override + public org.bukkit.inventory.ItemStack getItem(int index) { + if (index < 0) { + return null; + } + + Slot slot = slots.get(index); + return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY); + } + + @Override + public boolean isInTop(int rawSlot) { + return rawSlot < topSize; + } + + @NotNull + @Override + public InventoryType.SlotType getSlotType(int slot) { + if (slot < 0) { + return InventoryType.SlotType.OUTSIDE; + } + if (slot >= topSize) { + return super.getSlotType(offset + slot); + } + return inventory.getSlotType(offset + slot); + } + }; + } + return bukkitEntity; + } + + // + // Back at it again, overriding a bunch of methods because we need access to 2 fields. + private @Nullable ContainerSynchronizer synchronizer; + private final List dataSlots = new ArrayList<>(); + private final IntList remoteDataSlots = new IntArrayList(); + private final List containerListeners = new ArrayList<>(); + private ItemStack remoteCarried = ItemStack.EMPTY; + private boolean suppressRemoteUpdates; + + protected Slot addSlot(Slot slot) { + slot.index = this.slots.size(); + this.slots.add(slot); + this.lastSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(ItemStack.EMPTY); + return slot; + } + + protected DataSlot addDataSlot(DataSlot dataSlot) { + this.dataSlots.add(dataSlot); + this.remoteDataSlots.add(0); + return dataSlot; + } + + protected void addDataSlots(ContainerData containerData) { + for (int i = 0; i < containerData.getCount(); i++) { + this.addDataSlot(DataSlot.forContainer(containerData, i)); + } + } + + public void addSlotListener(ContainerListener containerListener) { + if (!this.containerListeners.contains(containerListener)) { + this.containerListeners.add(containerListener); + this.broadcastChanges(); + } + } + + public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { + this.synchronizer = containerSynchronizer; + this.sendAllDataToRemote(); + } + + public void sendAllDataToRemote() { + for (int index = 0; index < slots.size(); ++index) { + Slot slot = slots.get(index); + this.remoteSlots.set(index, (slot instanceof MenuSlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); + } + + remoteCarried = getCarried().copy(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); + } + + if (this.synchronizer != null) { + this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); + } + } + + public void broadcastCarriedItem() { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + + public void removeSlotListener(ContainerListener containerListener) { + this.containerListeners.remove(containerListener); + } + + public void broadcastChanges() { + for (int index = 0; index < this.slots.size(); ++index) { + Slot slot = this.slots.get(index); + ItemStack itemstack = slot instanceof MenuSlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + Supplier supplier = Suppliers.memoize(itemstack::copy); + this.triggerSlotListeners(index, itemstack, supplier); + this.synchronizeSlotToRemote(index, itemstack, supplier); + } + + this.synchronizeCarriedToRemote(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot dataSlot = this.dataSlots.get(index); + int j = dataSlot.get(); + if (dataSlot.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, j); + } + + this.synchronizeDataSlotToRemote(index, j); + } + } + + public void broadcastFullState() { + for (int index = 0; index < this.slots.size(); ++index) { + ItemStack itemstack = this.slots.get(index).getItem(); + this.triggerSlotListeners(index, itemstack, itemstack::copy); + } + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot containerproperty = this.dataSlots.get(index); + if (containerproperty.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, containerproperty.get()); + } + } + + this.sendAllDataToRemote(); + } + + private void updateDataSlotListeners(int i, int j) { + for (ContainerListener containerListener : this.containerListeners) { + containerListener.dataChanged(this, i, j); + } + } + + private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + ItemStack itemStack1 = this.lastSlots.get(index); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemStack2 = supplier.get(); + this.lastSlots.set(index, itemStack2); + + for (ContainerListener containerListener : this.containerListeners) { + containerListener.slotChanged(this, index, itemStack2); + } + } + } + + private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { + if (!this.suppressRemoteUpdates) { + ItemStack itemStack1 = this.remoteSlots.get(i); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemstack2 = supplier.get(); + this.remoteSlots.set(i, itemstack2); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, i, itemstack2); + } + } + } + } + + private void synchronizeDataSlotToRemote(int index, int value) { + if (!this.suppressRemoteUpdates) { + int existing = this.remoteDataSlots.getInt(index); + if (existing != value) { + this.remoteDataSlots.set(index, value); + if (this.synchronizer != null) { + this.synchronizer.sendDataChange(this, index, value); + } + } + } + } + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + } + + public void setRemoteCarried(ItemStack itemstack) { + this.remoteCarried = itemstack.copy(); + } + + public void suppressRemoteUpdates() { + this.suppressRemoteUpdates = true; + } + + public void resumeRemoteUpdates() { + this.suppressRemoteUpdates = false; + } + // + + @Override + public ItemStack quickMoveStack(Player player, int index) { + // See ChestMenu and InventoryMenu + Slot slot = this.slots.get(index); + + if (!slot.hasItem() || slot.isFake()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack originalStack = itemStack.copy(); + + if (index < topSize) { + // If we're moving top to bottom, do a normal transfer. + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else { + EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack); + boolean movedGear = switch (equipmentSlot) { + // If this is gear, try to move it to the correct slot first. + case OFFHAND, FEET, LEGS, CHEST, HEAD -> { + // Locate the correct slot in the contents following the main inventory. + for (int extra = inventory.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { + Slot extraSlot = getSlot(extra); + if (extraSlot instanceof ContainerSlotEquipment.SlotEquipment equipSlot + && equipSlot.getEquipmentSlot() == equipmentSlot) { + // If we've found a matching slot, try to move to it. + // If this succeeds, even partially, we will not attempt to move to other slots. + // Otherwise, armor is already occupied, so we'll fall through to main inventory. + yield this.moveItemStackTo(itemStack, extra, extra + 1, false); + } + } + yield false; + } + // Non-gear gets no special treatment. + default -> false; + }; + + // If main inventory is not available, there's nowhere else to move. + if (offset != 0) { + if (!movedGear) { + return ItemStack.EMPTY; + } + } else { + // If we didn't move to a gear slot, try to move to a main inventory slot. + if (!movedGear && !this.moveItemStackTo(itemStack, 0, inventory.getOwnerHandle().getInventory().items.size(), true)) { + return ItemStack.EMPTY; + } + } + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return originalStack; + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + /** + * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake + * slots and respects {@link Slot#hasItem()}. + * + * @param itemStack the stack to quick-move + * @param rangeLow the start of the range of slots that can be moved to, inclusive + * @param rangeHigh the end of the range of slots that can be moved to, exclusive + * @param topDown whether to start at the top of the range or bottom + * @return whether the stack was modified as a result of being quick-moved + */ + protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { + boolean modified = false; + boolean stackable = itemStack.isStackable(); + Slot firstEmpty = null; + + for (int index = topDown ? rangeHigh - 1 : rangeLow; + !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); + index += topDown ? -1 : 1 + ) { + Slot slot = slots.get(index); + // If the slot cannot be added to, check the next slot. + if (slot.isFake() || !slot.mayPlace(itemStack)) { + continue; + } + + if (slot.hasItem()) { + // If the item isn't stackable, check the next slot. + if (!stackable) { + continue; + } + // Otherwise, add as many as we can from our stack to the slot. + modified = addToExistingStack(itemStack, slot); + } else { + // If this is the first empty slot, keep track of it for later use. + if (firstEmpty == null) { + firstEmpty = slot; + } + // If the item isn't stackable, we've located the slot we're adding it to, so we're done. + if (!stackable) { + break; + } + } + } + + // If the item hasn't been fully added yet, add as many as we can to the first open slot. + if (!itemStack.isEmpty() && firstEmpty != null) { + firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); + firstEmpty.setChanged(); + modified = true; + } + + return modified; + } + + private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { + ItemStack existing = slot.getItem(); + + // If the items aren't the same, we can't add our item. + if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { + return false; + } + + int total = existing.getCount() + itemStack.getCount(); + int max = slot.getMaxStackSize(existing); + + // If the existing item can accept the entirety of our item, we're done! + if (total <= max) { + itemStack.setCount(0); + existing.setCount(total); + slot.setChanged(); + return true; + } + + // Otherwise, add as many as we can. + itemStack.shrink(max - existing.getCount()); + existing.setCount(max); + slot.setChanged(); + return true; + } + + @Override + public boolean canDragTo(Slot slot) { + return !(slot instanceof ContainerSlotDrop.SlotDrop || slot instanceof ContainerSlotEmpty.SlotEmpty); + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java new file mode 100644 index 00000000..e7b54edb --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java @@ -0,0 +1,166 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import net.minecraft.world.entity.player.Inventory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { + + public OpenPlayerInventory(@NotNull OpenInventory inventory) { + super(inventory); + } + + @Override + public OpenInventory getInventory() { + return (OpenInventory) super.getInventory(); + } + + @Override + public @NotNull Player getHolder() { + return getInventory().getOwner(); + } + + @Override + public @NotNull ItemStack[] getArmorContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor); + } + + @Override + public void setArmorContents(@Nullable ItemStack[] items) { + for (int index = 0; index < items.length; ++index) { + getInventory().getOwnerHandle().getInventory().armor.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull ItemStack[] getExtraContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand); + } + + @Override + public void setExtraContents(@Nullable ItemStack[] items) { + for (int index = 0; index < items.length; ++index) { + getInventory().getOwnerHandle().getInventory().offhand.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @Nullable ItemStack getHelmet() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(3)); + } + + @Override + public void setHelmet(@Nullable ItemStack helmet) { + getInventory().getOwnerHandle().getInventory().armor.set(3, CraftItemStack.asNMSCopy(helmet)); + } + + @Override + public @Nullable ItemStack getChestplate() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(2)); + } + + @Override + public void setChestplate(@Nullable ItemStack chestplate) { + getInventory().getOwnerHandle().getInventory().armor.set(2, CraftItemStack.asNMSCopy(chestplate)); + } + + @Override + public @Nullable ItemStack getLeggings() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(1)); + } + + @Override + public void setLeggings(@Nullable ItemStack leggings) { + getInventory().getOwnerHandle().getInventory().armor.set(1, CraftItemStack.asNMSCopy(leggings)); + } + + @Override + public @Nullable ItemStack getBoots() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(0)); + } + + @Override + public void setBoots(@Nullable ItemStack boots) { + getInventory().getOwnerHandle().getInventory().armor.set(0, CraftItemStack.asNMSCopy(boots)); + } + + @Override + public @NotNull ItemStack getItemInMainHand() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return CraftItemStack.asCraftMirror(internal.getItem(internal.selected)); + } + + @Override + public void setItemInMainHand(@Nullable ItemStack item) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item)); + } + + @Override + public @NotNull ItemStack getItemInOffHand() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.get(0)); + } + + @Override + public void setItemInOffHand(@Nullable ItemStack item) { + getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); + } + + @Override + public @NotNull ItemStack getItemInHand() { + return getItemInMainHand(); + } + + @Override + public void setItemInHand(@Nullable ItemStack stack) { + setItemInMainHand(stack); + } + + @Override + public int getHeldItemSlot() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return internal.items.size() - 9 + internal.selected; + } + + @Override + public void setHeldItemSlot(int slot) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + if (slot < internal.items.size()) { + slot += internal.items.size() - 9; + } + internal.selected = slot; + } + + @Override + public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) { + return switch (slot) { + case HAND -> getItemInMainHand(); + case OFF_HAND -> getItemInOffHand(); + case FEET -> getBoots(); + case LEGS -> getLeggings(); + case CHEST -> getChestplate(); + case HEAD -> getHelmet(); + default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); + }; + } + + @Override + public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) { + switch (slot) { + case HAND -> setItemInMainHand(item); + case OFF_HAND -> setItemInOffHand(item); + case FEET -> setBoots(item); + case LEGS -> setLeggings(item); + case CHEST -> setChestplate(item); + case HEAD -> setHelmet(item); + default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); + } + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index a04ac0c3..c01c6abb 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -50,7 +50,12 @@ private void checkSupported() { return; } try { - Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialPlayerInventory"); + try { + Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".inventory.OpenInventory"); + } catch (ClassNotFoundException e) { + Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialPlayerInventory"); + } + Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".inventory.OpenInventory"); Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialEnderChest"); this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager"); this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer"); @@ -189,13 +194,12 @@ public String getReleasesLink() { private @NotNull T createSpecialInventory( @NotNull Class assignableClass, @NotNull String className, - @NotNull Player player, - boolean online) throws InstantiationException { + @NotNull Object @NotNull ... params) throws InstantiationException { if (!this.supported) { throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } try { - return this.createObject(assignableClass, className, player, online); + return this.createObject(assignableClass, className, params); } catch (Exception original) { InstantiationException exception = new InstantiationException(String.format("Unable to create a new %s", className)); exception.initCause(original.fillInStackTrace()); @@ -264,12 +268,15 @@ public ISpecialEnderChest newSpecialEnderChest(final Player player, final boolea * Creates an instance of the ISpecialPlayerInventory implementation for the given Player.. * * @param player the Player - * @param online true if the Player is online * @return the ISpecialPlayerInventory created * @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated */ - public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player, final boolean online) throws InstantiationException { - return this.createSpecialInventory(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, online); + public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player) throws InstantiationException { + try { + return this.createSpecialInventory(ISpecialPlayerInventory.class, "inventory.OpenInventory", player); + } catch (InstantiationException ignored) { + return this.createSpecialInventory(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, player.isOnline()); + } } } diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index ef159314..38426d6f 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -20,26 +20,19 @@ import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import org.bukkit.GameMode; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.inventory.InventoryInteractEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.InventoryView; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * Listener for inventory-related events to prevent modification of inventories where not allowed. @@ -74,101 +67,20 @@ private void onInventoryClose(@NotNull final InventoryCloseEvent event) { @EventHandler(priority = EventPriority.LOWEST) private void onInventoryClick(@NotNull final InventoryClickEvent event) { - if (handleInventoryInteract(event)) { - return; - } - - // Safe cast - has to be a player to be the holder of a special player inventory. - Player player = (Player) event.getWhoClicked(); - - if (event.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY) { - // All own-inventory interactions require updates to display properly. - // Update in same tick after event completion. - this.plugin.getServer().getScheduler().runTask(this.plugin, player::updateInventory); - return; - } - - // Extra handling for MOVE_TO_OTHER_INVENTORY - apparently Mojang no longer removes the item from the target - // inventory prior to adding it to existing stacks. - ItemStack currentItem = event.getCurrentItem(); - if (currentItem == null) { - // Other plugin doing some sort of handling (would be NOTHING for null item otherwise), ignore. - return; - } - - ItemStack clone = currentItem.clone(); - event.setCurrentItem(null); - - // Complete add action in same tick after event completion. - this.plugin.getServer().getScheduler().runTask(this.plugin, () -> { - player.getInventory().addItem(clone); - player.updateInventory(); - }); + handleInventoryInteract(event); } @EventHandler(priority = EventPriority.LOWEST) private void onInventoryDrag(@NotNull final InventoryDragEvent event) { - if (handleInventoryInteract(event)) { - return; - } - - InventoryView view = event.getView(); - int topSize = view.getTopInventory().getSize(); - - // Get bottom inventory active slots as player inventory slots. - Set slots = event.getRawSlots().stream() - .filter(slot -> slot >= topSize) - .map(slot -> plugin.convertToPlayerSlot(view, slot)).collect(Collectors.toSet()); - - int overlapLosses = 0; - - // Count overlapping slots. - for (Map.Entry newItem : event.getNewItems().entrySet()) { - int rawSlot = newItem.getKey(); - - // Skip bottom inventory slots. - if (rawSlot >= topSize) { - continue; - } - - int convertedSlot = plugin.convertToPlayerSlot(view, rawSlot); - - if (slots.contains(convertedSlot)) { - overlapLosses += getCountDiff(view.getItem(rawSlot), newItem.getValue()); - } - } - - // Allow no overlap to proceed as usual. - if (overlapLosses < 1) { - return; - } - - ItemStack cursor = event.getCursor(); - if (cursor != null) { - cursor.setAmount(cursor.getAmount() + overlapLosses); - } else { - cursor = event.getOldCursor().clone(); - cursor.setAmount(overlapLosses); - } - - event.setCursor(cursor); - } - - private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result) { - if (original == null || original.getType() != result.getType()) { - return result.getAmount(); - } - - return result.getAmount() - original.getAmount(); + handleInventoryInteract(event); } /** * Handle common InventoryInteractEvent functions. * * @param event the InventoryInteractEvent - * @return true unless the top inventory is the holder's own inventory */ - private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) { + private void handleInventoryInteract(@NotNull final InventoryInteractEvent event) { HumanEntity entity = event.getWhoClicked(); // Un-cancel spectator interactions. @@ -177,7 +89,7 @@ private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent ev } if (event.isCancelled()) { - return true; + return; } Inventory inventory = event.getView().getTopInventory(); @@ -188,24 +100,20 @@ private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent ev if (!Permissions.EDITENDER.hasPermission(entity)) { event.setCancelled(true); } - return true; + return; } ISpecialPlayerInventory playerInventory = InventoryAccess.getPlayerInventory(inventory); // Ignore inventories other than special player inventories. if (playerInventory == null) { - return true; + return; } // Disallow player inventory interaction for users without edit permission. if (!Permissions.EDITINV.hasPermission(entity)) { event.setCancelled(true); - return true; } - - // Only specially handle actions in the player's own inventory. - return !event.getWhoClicked().equals(playerInventory.getPlayer()); } } diff --git a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java b/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java new file mode 100644 index 00000000..7586fc9c --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java @@ -0,0 +1,214 @@ +package com.lishid.openinv; + +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.util.InventoryAccess; +import com.lishid.openinv.util.Permissions; +import org.bukkit.GameMode; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +record LegacyInventoryListener(OpenInv plugin) implements Listener { + + @EventHandler + private void onInventoryClose(@NotNull final InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player player)) { + return; + } + + InventoryHolder holder = event.getInventory().getHolder(); + if (this.plugin.getSilentContainerStatus(player) + && holder != null + && this.plugin.getAnySilentContainer().isAnySilentContainer(holder)) { + this.plugin.getAnySilentContainer().deactivateContainer(player); + } + + ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory()); + if (specialInventory != null) { + this.plugin.handleCloseInventory(specialInventory); + } else { + specialInventory = InventoryAccess.getPlayerInventory(event.getInventory()); + if (specialInventory != null) { + this.plugin.handleCloseInventory(specialInventory); + } + } + } + + @EventHandler(priority = EventPriority.LOWEST) + private void onInventoryClick(@NotNull final InventoryClickEvent event) { + if (handleInventoryInteract(event)) { + return; + } + + // Safe cast - has to be a player to be the holder of a special player inventory. + Player player = (Player) event.getWhoClicked(); + + if (event.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY) { + return; + } + + // Extra handling for MOVE_TO_OTHER_INVENTORY - apparently Mojang no longer removes the item from the target + // inventory prior to adding it to existing stacks. + ItemStack currentItem = event.getCurrentItem(); + if (currentItem == null) { + // Other plugin doing some sort of handling (would be NOTHING for null item otherwise), ignore. + return; + } + + ItemStack clone = currentItem.clone(); + event.setCurrentItem(null); + + // Complete add action in same tick after event completion. + this.plugin.getServer().getScheduler().runTask(this.plugin, () -> { + player.getInventory().addItem(clone); + }); + } + + @EventHandler(priority = EventPriority.LOWEST) + private void onInventoryDrag(@NotNull final InventoryDragEvent event) { + if (handleInventoryInteract(event)) { + return; + } + + InventoryView view = event.getView(); + int topSize = view.getTopInventory().getSize(); + + // Get bottom inventory active slots as player inventory slots. + Set slots = event.getRawSlots().stream() + .filter(slot -> slot >= topSize) + .map(slot -> convertToPlayerSlot(view, slot)).collect(Collectors.toSet()); + + int overlapLosses = 0; + + // Count overlapping slots. + for (Map.Entry newItem : event.getNewItems().entrySet()) { + int rawSlot = newItem.getKey(); + + // Skip bottom inventory slots. + if (rawSlot >= topSize) { + continue; + } + + int convertedSlot = convertToPlayerSlot(view, rawSlot); + + if (slots.contains(convertedSlot)) { + overlapLosses += getCountDiff(view.getItem(rawSlot), newItem.getValue()); + } + } + + // Allow no overlap to proceed as usual. + if (overlapLosses < 1) { + return; + } + + ItemStack cursor = event.getCursor(); + if (cursor != null) { + cursor.setAmount(cursor.getAmount() + overlapLosses); + } else { + cursor = event.getOldCursor().clone(); + cursor.setAmount(overlapLosses); + } + + event.setCursor(cursor); + } + + private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result) { + if (original == null || original.getType() != result.getType()) { + return result.getAmount(); + } + + return result.getAmount() - original.getAmount(); + } + + /** + * Handle common InventoryInteractEvent functions. + * + * @param event the InventoryInteractEvent + * @return true unless the top inventory is the holder's own inventory + */ + private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) { + HumanEntity entity = event.getWhoClicked(); + + // Un-cancel spectator interactions. + if (Permissions.SPECTATE.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) { + event.setCancelled(false); + } + + if (event.isCancelled()) { + return true; + } + + Inventory inventory = event.getView().getTopInventory(); + + // Is the inventory a special ender chest? + if (InventoryAccess.isEnderChest(inventory)) { + // Disallow ender chest interaction for users without edit permission. + if (!Permissions.EDITENDER.hasPermission(entity)) { + event.setCancelled(true); + } + return true; + } + + ISpecialPlayerInventory playerInventory = InventoryAccess.getPlayerInventory(inventory); + + // Ignore inventories other than special player inventories. + if (playerInventory == null) { + return true; + } + + // Disallow player inventory interaction for users without edit permission. + if (!Permissions.EDITINV.hasPermission(entity)) { + event.setCancelled(true); + return true; + } + + // Only specially handle actions in the player's own inventory. + return !event.getWhoClicked().equals(playerInventory.getPlayer()); + } + + private static int convertToPlayerSlot(InventoryView view, int rawSlot) { + int topSize = view.getTopInventory().getSize(); + if (topSize <= rawSlot) { + // Slot is not inside special inventory, use Bukkit logic. + return view.convertSlot(rawSlot); + } + + // Main inventory, slots 0-26 -> 9-35 + if (rawSlot < 27) { + return rawSlot + 9; + } + // Hotbar, slots 27-35 -> 0-8 + if (rawSlot < 36) { + return rawSlot - 27; + } + // Armor, slots 36-39 -> 39-36 + if (rawSlot < 40) { + return 36 + (39 - rawSlot); + } + // Off-hand + if (rawSlot == 40) { + return 40; + } + // Drop slots, "out of inventory" + return -1; + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 6ab067ec..38bea972 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -16,6 +16,8 @@ package com.lishid.openinv; +import com.github.jikoo.planarwrappers.util.version.BukkitVersions; +import com.github.jikoo.planarwrappers.util.version.Version; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.lishid.openinv.commands.ContainerSettingCommand; @@ -146,7 +148,11 @@ public void onEnable() { new ConfigUpdater(this).checkForUpdates(); // Register listeners - pm.registerEvents(new PlayerListener(this), this); + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { + pm.registerEvents(new LegacyInventoryListener(this), this); + } else { + pm.registerEvents(new PlayerListener(this), this); + } pm.registerEvents(new InventoryListener(this), this); // Register commands to their executors @@ -272,7 +278,7 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final return this.inventories.get(key); } - ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online); + ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player); this.inventories.put(key, inv); return inv; } @@ -444,20 +450,6 @@ private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInve && !Objects.equals(viewer.getWorld(), player.getWorld())); } - /** - * Convert a raw slot number into a player inventory slot number. - * - *

Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular - * player inventory slot number. - * - * @param view the open inventory view - * @param rawSlot the raw slot in the view - * @return the converted slot number - */ - int convertToPlayerSlot(InventoryView view, int rawSlot) { - return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot); - } - public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) { return this.languageManager.getValue(key, getLocale(sender)); } @@ -600,8 +592,8 @@ void handleCloseInventory(@NotNull ISpecialInventory inventory) { * @throws IllegalStateException if the server version is unsupported */ void setPlayerOnline(@NotNull Player player) { - setPlayerOnline(inventories, player, player::updateInventory); - setPlayerOnline(enderChests, player, null); + setPlayerOnline(inventories, player); + setPlayerOnline(enderChests, player); if (player.hasPlayedBefore()) { return; @@ -645,8 +637,7 @@ void setPlayerOnline(@NotNull Player player) { private void setPlayerOnline( @NotNull Map map, - @NotNull Player player, - @Nullable Runnable task) { + @NotNull Player player) { ISpecialInventory inventory = map.get(player.getUniqueId()); if (inventory == null) { @@ -663,10 +654,6 @@ private void setPlayerOnline( !Permissions.OPENONLINE.hasPermission(viewer) || !Permissions.CROSSWORLD.hasPermission(viewer) && !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld())); - - if (task != null) { - getServer().getScheduler().runTask(this, task); - } } static void ejectViewers(@NotNull ISpecialInventory inventory, @NotNull Predicate<@NotNull HumanEntity> predicate) { diff --git a/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java b/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java index ca669324..37bb1bf0 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java @@ -51,16 +51,4 @@ public interface IPlayerDataManager { */ @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); - /** - * Convert a raw slot number into a player inventory slot number. - * - *

Note that this method is specifically for converting an ISpecialPlayerInventory slot number into a regular - * player inventory slot number. - * - * @param view the open inventory view - * @param rawSlot the raw slot in the view - * @return the converted slot number - */ - int convertToPlayerSlot(InventoryView view, int rawSlot); - } diff --git a/pom.xml b/pom.xml index 8ffa96fc..10662f02 100644 --- a/pom.xml +++ b/pom.xml @@ -40,9 +40,9 @@ api plugin - internal/v1_20_R3 - internal/v1_20_R4 internal/v1_21_R1 + internal/v1_20_R4 + internal/v1_20_R3 assembly From e39dbc0044e8b4734427e6d34b626113a962a8c4 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Jul 2024 08:43:34 -0400 Subject: [PATCH 184/340] Improve customizability, add resource pack (#215) --- .github/workflows/ci.yml | 9 +- .../internal/v1_21_R1/PlayerDataManager.java | 53 +++-- .../inventory/ContainerSlotCrafting.java | 2 +- .../ContainerSlotCraftingResult.java | 6 +- .../inventory/ContainerSlotCursor.java | 34 +--- .../v1_21_R1/inventory/ContainerSlotDrop.java | 14 +- .../inventory/ContainerSlotEquipment.java | 68 +------ ....java => ContainerSlotUninteractable.java} | 18 +- .../inventory/MenuSlotPlaceholder.java | 60 ------ .../v1_21_R1/inventory/OpenInventory.java | 42 ++-- .../v1_21_R1/inventory/OpenInventoryMenu.java | 6 +- .../inventory/PlaceholderManager.java | 181 ++++++++++++++++++ .../com/lishid/openinv/InternalAccessor.java | 23 +++ .../main/java/com/lishid/openinv/OpenInv.java | 6 +- .../openinv/internal/PlaceholderParser.java | 9 + .../minecraft/models/item/crafting_table.json | 11 ++ .../assets/minecraft/models/item/dropper.json | 11 ++ .../minecraft/models/item/leather_boots.json | 75 ++++++++ .../models/item/leather_chestplate.json | 75 ++++++++ .../minecraft/models/item/leather_helmet.json | 75 ++++++++ .../models/item/leather_leggings.json | 75 ++++++++ .../assets/minecraft/models/item/shield.json | 58 ++++++ .../minecraft/models/item/white_banner.json | 11 ++ .../models/item/white_stained_glass_pane.json | 14 ++ .../openinv/models/item/crafting_output.json | 24 +++ .../assets/openinv/models/item/cursor.json | 6 + .../assets/openinv/models/item/drop.json | 6 + .../openinv/models/item/empty_boots.json | 6 + .../openinv/models/item/empty_chestplate.json | 6 + .../openinv/models/item/empty_helmet.json | 6 + .../openinv/models/item/empty_leggings.json | 6 + .../openinv/models/item/empty_shield.json | 6 + .../openinv/models/item/not_a_slot.json | 11 ++ .../openinv/textures/item/crafting_output.png | Bin 0 -> 191 bytes .../assets/openinv/textures/item/cursor.png | Bin 0 -> 128 bytes .../assets/openinv/textures/item/drop.png | Bin 0 -> 146 bytes .../openinv/textures/item/not_a_slot.png | Bin 0 -> 89 bytes resource-pack/pack.mcmeta | 6 + 38 files changed, 803 insertions(+), 216 deletions(-) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotEmpty.java => ContainerSlotUninteractable.java} (85%) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java create mode 100644 plugin/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java create mode 100644 resource-pack/assets/minecraft/models/item/crafting_table.json create mode 100644 resource-pack/assets/minecraft/models/item/dropper.json create mode 100644 resource-pack/assets/minecraft/models/item/leather_boots.json create mode 100644 resource-pack/assets/minecraft/models/item/leather_chestplate.json create mode 100644 resource-pack/assets/minecraft/models/item/leather_helmet.json create mode 100644 resource-pack/assets/minecraft/models/item/leather_leggings.json create mode 100644 resource-pack/assets/minecraft/models/item/shield.json create mode 100644 resource-pack/assets/minecraft/models/item/white_banner.json create mode 100644 resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json create mode 100644 resource-pack/assets/openinv/models/item/crafting_output.json create mode 100644 resource-pack/assets/openinv/models/item/cursor.json create mode 100644 resource-pack/assets/openinv/models/item/drop.json create mode 100644 resource-pack/assets/openinv/models/item/empty_boots.json create mode 100644 resource-pack/assets/openinv/models/item/empty_chestplate.json create mode 100644 resource-pack/assets/openinv/models/item/empty_helmet.json create mode 100644 resource-pack/assets/openinv/models/item/empty_leggings.json create mode 100644 resource-pack/assets/openinv/models/item/empty_shield.json create mode 100644 resource-pack/assets/openinv/models/item/not_a_slot.json create mode 100644 resource-pack/assets/openinv/textures/item/crafting_output.png create mode 100644 resource-pack/assets/openinv/textures/item/cursor.png create mode 100644 resource-pack/assets/openinv/textures/item/drop.png create mode 100644 resource-pack/assets/openinv/textures/item/not_a_slot.png create mode 100644 resource-pack/pack.mcmeta diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d26f015..168cc55e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: OpenInv CI on: push: branches: - - '**' + - 'master' tags-ignore: - '**' # Enable running CI via other Actions, i.e. for drafting releases and handling PRs. @@ -41,3 +41,10 @@ jobs: with: name: api path: ./api/target/openinvapi*.jar + - name: Build resource pack + id: upload-resource-pack + uses: actions/upload-artifact@v4 + with: + name: openinv-legibility-pack + path: ./resource-pack/ + compression-level: 9 diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java index 87cd3627..4660d80a 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java @@ -20,9 +20,9 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; +import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -252,27 +252,50 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { } @Override - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - if (inventory instanceof ISpecialPlayerInventory) { - return player.openInventory(inventory.getBukkitInventory()); + public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory) { + ServerPlayer player = getHandle(bukkitPlayer); + + if (player.connection == null) { + return null; } - ServerPlayer nmsPlayer = getHandle(player); + if (inventory instanceof OpenInventory container) { + // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) + AbstractContainerMenu menu = container.createMenu(player.nextContainerCounter(), player.getInventory(), player); - if (nmsPlayer.connection == null) { - return null; + // Should never happen, player is a ServerPlayer with an active connection. + if (menu == null) { + return null; + } + + // Set up title (the whole reason we're not just using Player#openInventory(Inventory)). + // Title can only be set once for a menu, and is set during the open process. + menu.setTitle(container.getTitle(player)); + + menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); + + // Menu is null if event is cancelled. + if (menu == null) { + return null; + } + + player.containerMenu = menu; + player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); + player.initMenu(menu); + + return menu.getBukkitView(); } InventoryViewTitle viewTitle = InventoryViewTitle.of(inventory); if (viewTitle == null) { - return player.openInventory(inventory.getBukkitInventory()); + return bukkitPlayer.openInventory(inventory.getBukkitInventory()); } - String originalTitle = viewTitle.getTitle(player, inventory); - InventoryView view = viewProvider.apply(player, inventory, originalTitle); + String originalTitle = viewTitle.getTitle(bukkitPlayer, inventory); + InventoryView view = viewProvider.apply(bukkitPlayer, inventory, originalTitle); Component title = Component.literal(originalTitle); - AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { + AbstractContainerMenu container = new CraftContainer(view, player, player.nextContainerCounter()) { @Override public MenuType getType() { return getContainers(inventory.getBukkitInventory().getSize()); @@ -280,7 +303,7 @@ public MenuType getType() { }; container.setTitle(title); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); + container = CraftEventFactory.callInventoryOpenEvent(player, container); if (container == null) { return null; @@ -289,9 +312,9 @@ public MenuType getType() { // Note: Reusing component prevents plugins from changing title during InventoryOpenEvent, but there's not much // we can do about that - we can't call InventoryView#getTitle on older versions without causing an // IncompatibleClassChangeError due to the fact that InventoryView is now an interface. - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), title)); - nmsPlayer.containerMenu = container; - nmsPlayer.initMenu(container); + player.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), title)); + player.containerMenu = container; + player.initMenu(container); return container.getBukkitView(); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java index 02462dce..f3f241cf 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java @@ -102,7 +102,7 @@ private SlotCrafting(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { - return isAvailable() ? items.get(ContainerSlotCrafting.this.index) : survivalOnly(holder); + return isAvailable() ? items.get(ContainerSlotCrafting.this.index) : PlaceholderManager.survivalOnly(holder); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java index b43b3185..55a8224b 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java @@ -13,7 +13,7 @@ * *

Unmodifiable because I said so. Use your own crafting grid.

*/ -class ContainerSlotCraftingResult extends ContainerSlotEmpty { +class ContainerSlotCraftingResult extends ContainerSlotUninteractable { ContainerSlotCraftingResult(@NotNull ServerPlayer holder) { super(holder); @@ -27,11 +27,11 @@ public ItemStack get() { @Override public Slot asMenuSlot(Container container, int index, int x, int y) { - return new ContainerSlotEmpty.SlotEmpty(container, index, x, y) { + return new ContainerSlotUninteractable.SlotEmpty(container, index, x, y) { @Override ItemStack getOrDefault() { if (!ContainerSlotCrafting.isAvailable(holder)) { - return survivalOnly(holder); + return PlaceholderManager.survivalOnly(holder); } InventoryMenu inventoryMenu = holder.inventoryMenu; return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java index f570fa08..65a9e33c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java @@ -1,48 +1,18 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.Registries; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Unit; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.entity.BannerPattern; -import net.minecraft.world.level.block.entity.BannerPatternLayers; -import net.minecraft.world.level.block.entity.BannerPatterns; -import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; -import java.util.List; - /** * A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode. */ class ContainerSlotCursor implements ContainerSlot { - private static final ItemStack PLACEHOLDER; - - static { - PLACEHOLDER = new ItemStack(Items.WHITE_BANNER); - RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); - Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); - BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT); - BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT); - BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER); - PLACEHOLDER.set(DataComponents.BANNER_PATTERNS, - new BannerPatternLayers(List.of( - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); - PLACEHOLDER.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - } - private @NotNull ServerPlayer holder; ContainerSlotCursor(@NotNull ServerPlayer holder) { @@ -115,10 +85,10 @@ private SlotCursor(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { if (!isAvailable()) { - return survivalOnly(holder); + return PlaceholderManager.survivalOnly(holder); } ItemStack carried = holder.containerMenu.getCarried(); - return carried.isEmpty() ? PLACEHOLDER : carried; + return carried.isEmpty() ? PlaceholderManager.cursor : carried; } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java index f2d41e58..355ccd1f 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java @@ -1,12 +1,9 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import net.minecraft.core.component.DataComponents; -import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -15,15 +12,8 @@ */ class ContainerSlotDrop implements ContainerSlot { - private static final ItemStack DROP; private ServerPlayer holder; - static { - DROP = new ItemStack(Items.DROPPER); - // Note: translatable component, not keybind component! We want the text identifying the keybind, not the key. - DROP.set(DataComponents.CUSTOM_NAME, Component.translatable("key.drop").withStyle(style -> style.withItalic(false))); - } - ContainerSlotDrop(@NotNull ServerPlayer holder) { this.holder = holder; } @@ -72,7 +62,9 @@ private SlotDrop(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { - return holder.connection != null && !holder.connection.isDisconnected() ? DROP : OFFLINE; + return holder.connection != null && !holder.connection.isDisconnected() + ? PlaceholderManager.drop + : PlaceholderManager.blockedOffline; } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java index 57646fb7..0d6cfa19 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java @@ -1,87 +1,29 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.Registries; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Unit; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.component.DyedItemColor; -import net.minecraft.world.level.block.entity.BannerPattern; -import net.minecraft.world.level.block.entity.BannerPatternLayers; -import net.minecraft.world.level.block.entity.BannerPatterns; -import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; -import java.util.List; - /** * A slot for equipment that displays placeholders if empty. */ class ContainerSlotEquipment extends ContainerSlotList { - private static final ItemStack HELMET; - private static final ItemStack CHESTPLATE; - private static final ItemStack LEGGINGS; - private static final ItemStack BOOTS; - private static final ItemStack SHIELD; - - static { - HELMET = new ItemStack(Items.LEATHER_HELMET); - // Inventory-background-grey-ish - DyedItemColor color = new DyedItemColor(0xC8C8C8, false); - HELMET.set(DataComponents.DYED_COLOR, color); - HELMET.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - - CHESTPLATE = new ItemStack(Items.LEATHER_CHESTPLATE); - CHESTPLATE.set(DataComponents.DYED_COLOR, color); - CHESTPLATE.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - - LEGGINGS = new ItemStack(Items.LEATHER_LEGGINGS); - LEGGINGS.set(DataComponents.DYED_COLOR, color); - LEGGINGS.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - - BOOTS = new ItemStack(Items.LEATHER_BOOTS); - BOOTS.set(DataComponents.DYED_COLOR, color); - BOOTS.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - - SHIELD = new ItemStack(Items.SHIELD); - SHIELD.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); - RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); - Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); - BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL); - BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT); - BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT); - BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT); - BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT); - SHIELD.set(DataComponents.BANNER_PATTERNS, - new BannerPatternLayers(List.of( - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); - SHIELD.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - } - private final ItemStack placeholder; private final EquipmentSlot equipmentSlot; ContainerSlotEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { super(holder, index, InventoryType.SlotType.ARMOR); placeholder = switch (equipmentSlot) { - case HEAD -> HELMET; - case CHEST -> CHESTPLATE; - case LEGS -> LEGGINGS; - case FEET -> BOOTS; - default -> SHIELD; + case HEAD -> PlaceholderManager.emptyHelmet; + case CHEST -> PlaceholderManager.emptyChestplate; + case LEGS -> PlaceholderManager.emptyLeggings; + case FEET -> PlaceholderManager.emptyBoots; + default -> PlaceholderManager.emptyOffHand; }; this.equipmentSlot = equipmentSlot; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java similarity index 85% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java index 265999b3..57b1e53b 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEmpty.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java @@ -1,33 +1,23 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import net.minecraft.core.component.DataComponents; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Unit; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; import java.util.Optional; /** - * A fake slot used to fill unused spaces in the inventory. + * A fake slot used to fill spaces in the inventory that can't be interacted with. */ -class ContainerSlotEmpty implements ContainerSlot { - - private static final ItemStack PLACEHOLDER; - - static { - PLACEHOLDER = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); - PLACEHOLDER.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - } +class ContainerSlotUninteractable implements ContainerSlot { @NotNull ServerPlayer holder; - ContainerSlotEmpty(@NotNull ServerPlayer holder) { + ContainerSlotUninteractable(@NotNull ServerPlayer holder) { this.holder = holder; } @@ -74,7 +64,7 @@ static class SlotEmpty extends MenuSlotPlaceholder { @Override ItemStack getOrDefault() { - return PLACEHOLDER; + return PlaceholderManager.notSlot; } public void onQuickCraft(ItemStack var0, ItemStack var1) {} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java index 898c6086..b29afdc5 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java @@ -1,14 +1,8 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import net.minecraft.core.component.DataComponents; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.GameType; -import org.jetbrains.annotations.NotNull; /** * An implementation of a slot as used by a menu that may have fake placeholder items. @@ -17,60 +11,6 @@ */ abstract class MenuSlotPlaceholder extends Slot { - static final ItemStack OFFLINE; - private static final ItemStack CREATIVE; - private static final ItemStack SPECTATOR; - - static { - // Barrier: "Not available - Offline" - OFFLINE = new ItemStack(Items.BARRIER); - OFFLINE.set(DataComponents.CUSTOM_NAME, - Component.translatable("options.narrator.notavailable") - .withStyle(style -> style.withItalic(false)) - .append(Component.literal(" - ")) - .append(Component.translatable("gui.socialInteractions.status_offline"))); - // Barrier: "Not available - Creative Mode" - CREATIVE = new ItemStack(Items.BARRIER); - CREATIVE.set( - DataComponents.CUSTOM_NAME, - Component.translatable("options.narrator.notavailable") - .withStyle(style -> style.withItalic(false)) - .append(" - ") - .append(GameType.CREATIVE.getLongDisplayName())); - // Barrier: "Not available - Spectator Mode" - SPECTATOR = new ItemStack(Items.BARRIER); - SPECTATOR.set( - DataComponents.CUSTOM_NAME, - Component.translatable("options.narrator.notavailable") - .withStyle(style -> style.withItalic(false)) - .append(" - ") - .append(GameType.SPECTATOR.getLongDisplayName())); - } - - public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { - if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { - return OFFLINE; - } - - GameType gameType = serverPlayer.gameMode.getGameModeForPlayer(); - return switch (gameType) { - case CREATIVE -> CREATIVE; - case SPECTATOR -> SPECTATOR; - // Just in case, fall through to creating new items. - // This is a lot less good - inventory syncher will create copies frequently. - default -> { - ItemStack itemStack = new ItemStack(Items.BARRIER); - itemStack.set( - DataComponents.CUSTOM_NAME, - Component.translatable("options.narrator.notavailable") - .withStyle(style -> style.withItalic(false)) - .append(" - ") - .append(gameType.getLongDisplayName())); - yield itemStack; - } - }; - } - MenuSlotPlaceholder(Container container, int index, int x, int y) { super(container, index, x, y); } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java index a96513ac..9d6e438f 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java @@ -2,9 +2,11 @@ import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.v1_21_R1.PlayerDataManager; +import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; -import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.MenuProvider; @@ -15,7 +17,6 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; import org.bukkit.Location; import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; @@ -43,7 +44,7 @@ public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; size = ((int) Math.ceil(rawSize / 9.0)) * 9; - slots = NonNullList.withSize(size, new ContainerSlotEmpty(owner)); + slots = NonNullList.withSize(size, new ContainerSlotUninteractable(owner)); setupSlots(); } @@ -168,18 +169,13 @@ private int addCrafting(int startIndex, boolean pretty) { } if (pretty) { - slots.set(startIndex + 2, new ContainerSlotEmpty(owner) { - private static final ItemStack PLACEHOLDER = new ItemStack(Items.CRAFTING_TABLE); - static { - PLACEHOLDER.set(DataComponents.CUSTOM_NAME, Component.translatable("container.crafting").withStyle(style -> style.withItalic(false))); - } - + slots.set(startIndex + 2, new ContainerSlotUninteractable(owner) { @Override public Slot asMenuSlot(Container container, int index, int x, int y) { - return new ContainerSlotEmpty.SlotEmpty(container, index, x, y) { + return new ContainerSlotUninteractable.SlotEmpty(container, index, x, y) { @Override ItemStack getOrDefault() { - return PLACEHOLDER; + return PlaceholderManager.craftingOutput; } }; } @@ -202,6 +198,28 @@ public ServerPlayer getOwnerHandle() { return owner; } + public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { + MutableComponent component = Component.empty(); + // Prefix for use with custom bitmap image fonts. + if (viewer == owner) { + component.append( + Component.translatableWithFallback("openinv.container.inventory.self", "") + .withStyle(style -> style + .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withColor(ChatFormatting.WHITE))); + } else { + component.append( + Component.translatableWithFallback("openinv.container.inventory.other", "") + .withStyle(style -> style + .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withColor(ChatFormatting.WHITE))); + } + // Normal title: "Inventory - OwnerName" + component.append(Component.translatable("container.inventory")) + .append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName())); + return component; + } + @Override public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { if (bukkitEntity == null) { @@ -320,7 +338,7 @@ public void clearContent() { @Override public Component getName() { - return Component.translatable("key.categories.inventory").append(" - ").append(owner.getName()); + return getTitle(null); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java index e7e70afd..54b194da 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -59,7 +59,7 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) // Guard against weird inventory sizes. if (index >= inventory.getContainerSize()) { - addSlot(new ContainerSlotEmpty.SlotEmpty(inventory, index, x, y)); + addSlot(new ContainerSlotUninteractable.SlotEmpty(inventory, index, x, y)); continue; } @@ -67,7 +67,7 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) // Only allow access to own gear slots and drop slot (though really, just click outside the inventory). if (ownInv && !(slot instanceof ContainerSlotEquipment.SlotEquipment || slot instanceof ContainerSlotDrop.SlotDrop)) { - slot = new ContainerSlotEmpty.SlotEmpty(inventory, index, x, y); + slot = new ContainerSlotUninteractable.SlotEmpty(inventory, index, x, y); } addSlot(slot); } @@ -465,7 +465,7 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { @Override public boolean canDragTo(Slot slot) { - return !(slot instanceof ContainerSlotDrop.SlotDrop || slot instanceof ContainerSlotEmpty.SlotEmpty); + return !(slot instanceof ContainerSlotDrop.SlotDrop || slot instanceof ContainerSlotUninteractable.SlotEmpty); } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java new file mode 100644 index 00000000..9a594460 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java @@ -0,0 +1,181 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import com.lishid.openinv.internal.PlaceholderParser; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Unit; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.CustomModelData; +import net.minecraft.world.item.component.DyedItemColor; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.entity.BannerPattern; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.entity.BannerPatterns; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.List; +import java.util.Optional; + +public class PlaceholderManager implements PlaceholderParser { + + private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); + static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); + static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); + static @NotNull ItemStack cursor = defaultCursor(); + static @NotNull ItemStack drop = defaultDrop(); + static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); + static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); + static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); + static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); + static @NotNull ItemStack emptyOffHand = getEmptyShield(); + static @NotNull ItemStack notSlot = defaultNotSlot(); + static @NotNull ItemStack blockedOffline = defaultBlockedOffline(); + + static { + for (GameType type : GameType.values()) { + // Barrier: "Not available - Creative" etc. + ItemStack typeItem = new ItemStack(Items.BARRIER); + typeItem.set( + DataComponents.ITEM_NAME, + Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName())); + BLOCKED_GAME_TYPE.put(type, typeItem); + } + } + + @Override + public void load(@NotNull ConfigurationSection section) throws Exception { + craftingOutput = parse(section, "crafting-output", craftingOutput); + cursor = parse(section, "cursor", cursor); + drop = parse(section, "drop", drop); + emptyHelmet = parse(section, "empty-helmet", emptyHelmet); + emptyChestplate = parse(section, "empty-chestplate", emptyChestplate); + emptyLeggings = parse(section, "empty-leggings", emptyLeggings); + emptyBoots = parse(section, "empty-boots", emptyBoots); + emptyOffHand = parse(section, "empty-off-hand", emptyOffHand); + notSlot = parse(section, "not-a-slot", notSlot); + blockedOffline = parse(section, "blocked.offline", blockedOffline); + BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE))); + BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); + } + + private @NotNull ItemStack parse( + @NotNull ConfigurationSection section, + @NotNull String path, + @NotNull ItemStack defaultStack) throws Exception { + String itemText = section.getString(path); + + if (itemText == null) { + return defaultStack; + } + + CompoundTag compoundTag = TagParser.parseTag(itemText); + Optional parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag); + return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); + } + + static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { + if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { + return blockedOffline; + } + + return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY); + } + + private static ItemStack defaultCraftingOutput() { + // Crafting table: "Crafting" + ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE); + itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting")); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack defaultCursor() { + // Cursor-like banner with no tooltip + ItemStack itemStack = new ItemStack(Items.WHITE_BANNER); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT); + BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT); + BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER); + itemStack.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + return itemStack; + } + + private static ItemStack defaultDrop() { + // Dropper: "Drop Selected Item" + ItemStack itemStack = new ItemStack(Items.DROPPER); + // Note: translatable component, not keybind component! We want the text identifying the keybind, not the key. + itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop")); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack getEmptyArmor(ItemLike item) { + // Inventory-background-grey-ish leather armor with no tooltip + ItemStack itemStack = new ItemStack(item); + DyedItemColor color = new DyedItemColor(0xC8C8C8, false); + itemStack.set(DataComponents.DYED_COLOR, color); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack getEmptyShield() { + ItemStack itemStack = new ItemStack(Items.SHIELD); + itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL); + BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT); + BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT); + BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT); + BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT); + itemStack.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack defaultNotSlot() { + // White pane with no tooltip + ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack defaultBlockedOffline() { + // Barrier: "Not available - Offline" + ItemStack itemStack = new ItemStack(Items.BARRIER); + itemStack.set(DataComponents.ITEM_NAME, + Component.translatable("options.narrator.notavailable") + .append(Component.literal(" - ")) + .append(Component.translatable("gui.socialInteractions.status_offline"))); + return itemStack; + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java index c01c6abb..63c2a067 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java @@ -23,13 +23,16 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.PlaceholderParser; import com.lishid.openinv.util.InventoryAccess; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Constructor; +import java.util.logging.Level; class InternalAccessor { @@ -38,6 +41,7 @@ class InternalAccessor { private boolean supported = false; private IPlayerDataManager playerDataManager; private IAnySilentContainer anySilentContainer; + private @Nullable PlaceholderParser placeholderParser; InternalAccessor(@NotNull Plugin plugin) { this.plugin = plugin; @@ -52,6 +56,7 @@ private void checkSupported() { try { try { Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".inventory.OpenInventory"); + this.placeholderParser = createObject(PlaceholderParser.class, "inventory.PlaceholderManager"); } catch (ClassNotFoundException e) { Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialPlayerInventory"); } @@ -233,6 +238,24 @@ public String getReleasesLink() { return this.playerDataManager; } + /** + * Reload internal features. + */ + void reload() { + if (placeholderParser == null) { + return; + } + + ConfigurationSection placeholders = plugin.getConfig().getConfigurationSection("placeholders"); + if (placeholders != null) { + try { + placeholderParser.load(placeholders); + } catch (Exception e) { + plugin.getLogger().log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + } + /** * Gets the server implementation version. * diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 38bea972..a42f2adf 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -86,6 +86,9 @@ public class OpenInv extends JavaPlugin implements IOpenInv { public void reloadConfig() { super.reloadConfig(); this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; + if (this.accessor != null && this.accessor.isSupported()) { + this.accessor.reload(); + } } @Override @@ -133,7 +136,6 @@ public void onEnable() { this.accessor = new InternalAccessor(this); this.languageManager = new LanguageManager(this, "en_us"); - this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; try { Class.forName("org.bukkit.entity.Player$Spigot"); @@ -144,6 +146,8 @@ public void onEnable() { // Version check if (isSpigot && this.accessor.isSupported()) { + reloadConfig(); + // Update existing configuration. May require internal access. new ConfigUpdater(this).checkForUpdates(); diff --git a/plugin/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java b/plugin/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java new file mode 100644 index 00000000..05a8c17b --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java @@ -0,0 +1,9 @@ +package com.lishid.openinv.internal; + +import org.bukkit.configuration.ConfigurationSection; + +public interface PlaceholderParser { + + void load(ConfigurationSection section) throws Exception; + +} diff --git a/resource-pack/assets/minecraft/models/item/crafting_table.json b/resource-pack/assets/minecraft/models/item/crafting_table.json new file mode 100644 index 00000000..230469c5 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/crafting_table.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:block/crafting_table", + "overrides": [ + { + "model": "openinv:item/crafting_output", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/assets/minecraft/models/item/dropper.json b/resource-pack/assets/minecraft/models/item/dropper.json new file mode 100644 index 00000000..0c8bb744 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/dropper.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:block/dropper", + "overrides": [ + { + "model": "openinv:item/drop", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/assets/minecraft/models/item/leather_boots.json b/resource-pack/assets/minecraft/models/item/leather_boots.json new file mode 100644 index 00000000..f9cd4073 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/leather_boots.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_boots", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_boots_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_boots_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_boots_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_boots_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_boots_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_boots_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_boots_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_boots_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_boots_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_boots_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_boots", + "layer1": "minecraft:item/leather_boots_overlay" + } +} diff --git a/resource-pack/assets/minecraft/models/item/leather_chestplate.json b/resource-pack/assets/minecraft/models/item/leather_chestplate.json new file mode 100644 index 00000000..d6dc8c5f --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/leather_chestplate.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_chestplate", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_chestplate_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_chestplate_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_chestplate_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_chestplate_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_chestplate_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_chestplate_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_chestplate_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_chestplate_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_chestplate_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_chestplate_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_chestplate", + "layer1": "minecraft:item/leather_chestplate_overlay" + } +} diff --git a/resource-pack/assets/minecraft/models/item/leather_helmet.json b/resource-pack/assets/minecraft/models/item/leather_helmet.json new file mode 100644 index 00000000..236ae610 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/leather_helmet.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_helmet", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_helmet_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_helmet_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_helmet_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_helmet_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_helmet_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_helmet_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_helmet_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_helmet_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_helmet_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_helmet_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_helmet", + "layer1": "minecraft:item/leather_helmet_overlay" + } +} diff --git a/resource-pack/assets/minecraft/models/item/leather_leggings.json b/resource-pack/assets/minecraft/models/item/leather_leggings.json new file mode 100644 index 00000000..eb9ddc89 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/leather_leggings.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_leggings", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_leggings_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_leggings_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_leggings_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_leggings_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_leggings_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_leggings_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_leggings_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_leggings_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_leggings_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_leggings_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_leggings", + "layer1": "minecraft:item/leather_leggings_overlay" + } +} diff --git a/resource-pack/assets/minecraft/models/item/shield.json b/resource-pack/assets/minecraft/models/item/shield.json new file mode 100644 index 00000000..5ea7eddd --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/shield.json @@ -0,0 +1,58 @@ +{ + "parent": "builtin/entity", + "gui_light": "front", + "textures": { + "particle": "block/dark_oak_planks" + }, + "display": { + "thirdperson_righthand": { + "rotation": [ 0, 90, 0 ], + "translation": [ 10, 6, -4 ], + "scale": [ 1, 1, 1 ] + }, + "thirdperson_lefthand": { + "rotation": [ 0, 90, 0 ], + "translation": [ 10, 6, 12 ], + "scale": [ 1, 1, 1 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 180, 5 ], + "translation": [ -10, 2, -10 ], + "scale": [ 1.25, 1.25, 1.25 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 180, 5 ], + "translation": [ 10, 0, -10 ], + "scale": [ 1.25, 1.25, 1.25 ] + }, + "gui": { + "rotation": [ 15, -25, -5 ], + "translation": [ 2, 3, 0 ], + "scale": [ 0.65, 0.65, 0.65 ] + }, + "fixed": { + "rotation": [ 0, 180, 0 ], + "translation": [ -4.5, 4.5, -5], + "scale":[ 0.55, 0.55, 0.55] + }, + "ground": { + "rotation": [ 0, 0, 0 ], + "translation": [ 2, 4, 2], + "scale":[ 0.25, 0.25, 0.25] + } + }, + "overrides": [ + { + "model": "openinv:item/empty_shield", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "predicate": { + "blocking": 1 + }, + "model": "item/shield_blocking" + } + ] +} diff --git a/resource-pack/assets/minecraft/models/item/white_banner.json b/resource-pack/assets/minecraft/models/item/white_banner.json new file mode 100644 index 00000000..bc6fadb8 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/white_banner.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:item/template_banner", + "overrides": [ + { + "model": "openinv:item/cursor", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json b/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json new file mode 100644 index 00000000..66dbd7d2 --- /dev/null +++ b/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json @@ -0,0 +1,14 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/white_stained_glass_pane" + }, + "overrides": [ + { + "model": "openinv:item/not_a_slot", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/assets/openinv/models/item/crafting_output.json b/resource-pack/assets/openinv/models/item/crafting_output.json new file mode 100644 index 00000000..6c167cdc --- /dev/null +++ b/resource-pack/assets/openinv/models/item/crafting_output.json @@ -0,0 +1,24 @@ +{ + "texture_size": [ 16, 32 ], + "textures": { + "layer0": "openinv:item/crafting_output" + }, + "elements": [ + { + "from": [ 0, -16, -16 ], + "to": [ 16, 16, -16 ], + "faces": { + "south": { + "uv": [ 0, 0, 16, 16 ], + "texture": "#layer0" + } + } + } + ], + "gui_light": "front", + "display": { + "gui": { + "scale": [ 1.125, 1.125, 1 ] + } + } +} diff --git a/resource-pack/assets/openinv/models/item/cursor.json b/resource-pack/assets/openinv/models/item/cursor.json new file mode 100644 index 00000000..6c033d83 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/cursor.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "openinv:item/cursor" + } +} diff --git a/resource-pack/assets/openinv/models/item/drop.json b/resource-pack/assets/openinv/models/item/drop.json new file mode 100644 index 00000000..b4782491 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/drop.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "openinv:item/drop" + } +} diff --git a/resource-pack/assets/openinv/models/item/empty_boots.json b/resource-pack/assets/openinv/models/item/empty_boots.json new file mode 100644 index 00000000..5b3ddf27 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/empty_boots.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/empty_armor_slot_boots" + } +} diff --git a/resource-pack/assets/openinv/models/item/empty_chestplate.json b/resource-pack/assets/openinv/models/item/empty_chestplate.json new file mode 100644 index 00000000..4003d627 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/empty_chestplate.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/empty_armor_slot_chestplate" + } +} diff --git a/resource-pack/assets/openinv/models/item/empty_helmet.json b/resource-pack/assets/openinv/models/item/empty_helmet.json new file mode 100644 index 00000000..dda818a6 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/empty_helmet.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/empty_armor_slot_helmet" + } +} diff --git a/resource-pack/assets/openinv/models/item/empty_leggings.json b/resource-pack/assets/openinv/models/item/empty_leggings.json new file mode 100644 index 00000000..c74e7e2a --- /dev/null +++ b/resource-pack/assets/openinv/models/item/empty_leggings.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/empty_armor_slot_leggings" + } +} diff --git a/resource-pack/assets/openinv/models/item/empty_shield.json b/resource-pack/assets/openinv/models/item/empty_shield.json new file mode 100644 index 00000000..6e6b21c1 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/empty_shield.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/empty_armor_slot_shield" + } +} diff --git a/resource-pack/assets/openinv/models/item/not_a_slot.json b/resource-pack/assets/openinv/models/item/not_a_slot.json new file mode 100644 index 00000000..a4955524 --- /dev/null +++ b/resource-pack/assets/openinv/models/item/not_a_slot.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "openinv:item/not_a_slot" + }, + "display": { + "gui": { + "scale": [ 1.125, -1.125, -1.125 ] + } + } +} diff --git a/resource-pack/assets/openinv/textures/item/crafting_output.png b/resource-pack/assets/openinv/textures/item/crafting_output.png new file mode 100644 index 0000000000000000000000000000000000000000..7590bb455342071c41f4fed540d68087cbd6fd60 GIT binary patch literal 191 zcmV;w06_nVP)?-F7)Ed+85aQ5TE)N^qfJLr zG278rZSRZ?0QBAL-K3FrtPyl&0BhZu#ql4jNB^wdo7p>SDz7@_T`i<>$?C9C2;rD0 trSxugzV9=loc%&NkrO%5F-j@j0uQ2*X%pggHkJSY002ovPDHLkV1hf9O`8A! literal 0 HcmV?d00001 diff --git a/resource-pack/assets/openinv/textures/item/cursor.png b/resource-pack/assets/openinv/textures/item/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..af54bc59b0b7ba216271cb03a13d1cc9508f150b GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`?w&4=Ar_~T6Be*?@bdokUlXO4 zCD9aYe6vjkbj&R1U>xPotIy-ku{YQhLoB*bK~`Pkt(!!uo?J;ZW*< tS65d{c5*VS)l52~q9m4e?1TU-L)~}Nmwg%D20%*~JYD@<);T3K0RZs+F9QGo literal 0 HcmV?d00001 diff --git a/resource-pack/assets/openinv/textures/item/not_a_slot.png b/resource-pack/assets/openinv/textures/item/not_a_slot.png new file mode 100644 index 0000000000000000000000000000000000000000..300e19d2c712143eb9059b8224d6ad69f6bacd40 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|1y2{p5R22vHf3*a)U# Date: Sun, 7 Jul 2024 09:03:35 -0400 Subject: [PATCH 185/340] Overhaul permissions, add slot perms (#217) --- README.MD | 100 ++------------- api/pom.xml | 2 +- .../lishid/openinv/util/InventoryAccess.java | 14 ++- assembly/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- .../ContainerSlotCraftingResult.java | 2 +- .../inventory/ContainerSlotEquipment.java | 15 +++ .../inventory/ContainerSlotOffHand.java | 1 - .../ContainerSlotUninteractable.java | 6 +- .../v1_21_R1/inventory/OpenInventory.java | 2 +- .../v1_21_R1/inventory/OpenInventoryMenu.java | 50 ++++++-- plugin/pom.xml | 2 +- .../com/lishid/openinv/InventoryListener.java | 39 +++--- .../openinv/LegacyInventoryListener.java | 45 +++---- .../com/lishid/openinv/OfflineHandler.java | 5 +- .../main/java/com/lishid/openinv/OpenInv.java | 28 +---- .../com/lishid/openinv/PlayerListener.java | 4 +- .../openinv/commands/OpenInvCommand.java | 40 +++--- .../com/lishid/openinv/util/Permissions.java | 73 +++++------ plugin/src/main/resources/locale/en_us.yml | 6 +- plugin/src/main/resources/plugin.yml | 114 ++++++++++++------ pom.xml | 6 +- 24 files changed, 282 insertions(+), 280 deletions(-) diff --git a/README.MD b/README.MD index b8ffb02d..66956203 100644 --- a/README.MD +++ b/README.MD @@ -1,16 +1,17 @@ ## About -OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) which allows users to open and edit anyone's inventory or ender chest - online or not! +OpenInv is a [Bukkit plugin](https://dev.bukkit.org/projects/openinv) which allows users to open and edit anyone's inventory or ender chest - online or not! ## Features - **OpenInv**: Open anyone's inventory, even if they're offline. - - Read-only mode! No edits allowed! Don't grant the permission `OpenInv.editinv` - - Cross-world support! Don't grant `OpenInv.crossworld` - - No self-opening! Don't grant `OpenInv.openself` - - Drop items as the player! Place items in the unused slots to the right of the armor to drop them + - Read-only mode! Don't grant edit permission. + - Cross-world support! Allow access only from the same world. + - No duplicate slots! Only armor is accessible when opening self (if allowed at all)! + - Drop items as the player! Place items in the dropper slot in the bottom right. Can be disabled via permission! + - Allow any item in armor slots! Configurable via permission. - **OpenEnder**: Open anyone's ender chest, even if they're offline. - - Read-only mode! No edits allowed! Don't grant `OpenInv.editender` - - Cross-world support! Don't grant `OpenInv.crossworld` - - No opening others! Don't grant `OpenInv.openenderall` + - Allow access only to own ender chest! Don't grant permission to open others. + - Read-only mode! Don't grant edit permission. + - Cross-world support! Allow access only from the same world. - **SilentContainer**: Open containers without displaying an animation or making sound. - **AnyContainer**: Open containers, even if blocked by ocelots or blocks. @@ -18,88 +19,7 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) whi See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands). ## Permissions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NodeDescription
OpenInv.*Gives permission to use all of OpenInv.
OpenInv.openinvRequired to use /openinv.
OpenInv.openselfRequired to open own inventory.
OpenInv.editinvRequired to make changes to open inventories.
OpenInv.openonlineAllows users to open online players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.
OpenInv.openofflineAllows users to open offline players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.
OpenInv.openenderRequired to use /openender.
OpenInv.editenderRequired to make changes to open ender chests.
OpenInv.openenderallAllows users to open others' ender chests. Without it, users can only open their own.
OpenInv.exemptPrevents the player's inventory being opened by others.
OpenInv.overrideAllows bypassing of the exempt permission.
OpenInv.crossworldAllows cross-world usage of /openinv and /openender.
OpenInv.searchRequired to use /searchinv and /searchender.
OpenInv.searchenchantRequired to use /searchenchant.
OpenInv.anychestRequired to use /anychest.
OpenInv.any.defaultCause AnyContainer to be enabled by default.
OpenInv.silentRequired to use /silentcontainer.
OpenInv.silent.defaultCause SilentContainer to be enabled by default.
OpenInv.spectateAllows users in spectate gamemode to edit inventories.
+See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Permissions) ## For Developers diff --git a/api/pom.xml b/api/pom.xml index 729663db..09cf5b36 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvapi diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index 60032a40..e05bbe1e 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -20,12 +20,13 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import java.lang.reflect.Method; import org.bukkit.Bukkit; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Method; + public final class InventoryAccess implements IInventoryAccess { private static Class craftInventory = null; @@ -93,6 +94,17 @@ public static boolean isEnderChest(@NotNull Inventory inventory) { return getSpecialInventory(ISpecialEnderChest.class, inventory); } + /** + * Get a {@link ISpecialInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is not backed + * by the correct class. + * + * @param inventory the Bukkit inventory + * @return the backing implementation if available + */ + public static @Nullable ISpecialInventory getInventory(@NotNull Inventory inventory) { + return getSpecialInventory(ISpecialInventory.class, inventory); + } + private static @Nullable T getSpecialInventory(@NotNull Class expected, @NotNull Inventory inventory) { Object inv; if (isUsable() && craftInventory.isAssignableFrom(inventory.getClass())) { diff --git a/assembly/pom.xml b/assembly/pom.xml index ad00efb5..3fd0775e 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -21,7 +21,7 @@ com.lishid openinvparent - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvassembly diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 025b8efd..be4bb10b 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 6d47ee7a..666318e0 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 22582fb1..2d2b6b3e 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvadapter1_21_R1 diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java index 55a8224b..6b25d985 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java @@ -27,7 +27,7 @@ public ItemStack get() { @Override public Slot asMenuSlot(Container container, int index, int x, int y) { - return new ContainerSlotUninteractable.SlotEmpty(container, index, x, y) { + return new SlotUninteractable(container, index, x, y) { @Override ItemStack getOrDefault() { if (!ContainerSlotCrafting.isAvailable(holder)) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java index 0d6cfa19..68ed1eb2 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java @@ -40,6 +40,8 @@ public Slot asMenuSlot(Container container, int index, int x, int y) { class SlotEquipment extends MenuSlotPlaceholder { + private ServerPlayer viewer; + SlotEquipment(Container container, int index, int x, int y) { super(container, index, x, y); } @@ -57,6 +59,19 @@ EquipmentSlot getEquipmentSlot() { return equipmentSlot; } + void onlyEquipmentFor(ServerPlayer viewer) { + this.viewer = viewer; + } + + @Override + public boolean mayPlace(ItemStack var0) { + if (viewer == null) { + return true; + } + + return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot; + } + } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java index 2243fc18..556203f5 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java @@ -40,7 +40,6 @@ public void setChanged() { holder.resendItemInHands(); } } - }; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java index 57b1e53b..f27579c8 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java @@ -48,7 +48,7 @@ public void set(ItemStack itemStack) { @Override public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotEmpty(container, index, x, y); + return new SlotUninteractable(container, index, x, y); } @Override @@ -56,9 +56,9 @@ public InventoryType.SlotType getSlotType() { return InventoryType.SlotType.OUTSIDE; } - static class SlotEmpty extends MenuSlotPlaceholder { + static class SlotUninteractable extends MenuSlotPlaceholder { - SlotEmpty(Container container, int index, int x, int y) { + SlotUninteractable(Container container, int index, int x, int y) { super(container, index, x, y); } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java index 9d6e438f..ab0336d5 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java @@ -172,7 +172,7 @@ private int addCrafting(int startIndex, boolean pretty) { slots.set(startIndex + 2, new ContainerSlotUninteractable(owner) { @Override public Slot asMenuSlot(Container container, int index, int x, int y) { - return new ContainerSlotUninteractable.SlotEmpty(container, index, x, y) { + return new SlotUninteractable(container, index, x, y) { @Override ItemStack getOrDefault() { return PlaceholderManager.craftingOutput; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java index 54b194da..2cad157e 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -1,6 +1,7 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; import com.google.common.base.Suppliers; +import com.lishid.openinv.util.Permissions; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.server.level.ServerPlayer; @@ -59,16 +60,12 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) // Guard against weird inventory sizes. if (index >= inventory.getContainerSize()) { - addSlot(new ContainerSlotUninteractable.SlotEmpty(inventory, index, x, y)); + addSlot(new ContainerSlotUninteractable.SlotUninteractable(inventory, index, x, y)); continue; } - Slot slot = inventory.getMenuSlot(index, x, y); + Slot slot = getUpperSlot(index, x, y, ownInv); - // Only allow access to own gear slots and drop slot (though really, just click outside the inventory). - if (ownInv && !(slot instanceof ContainerSlotEquipment.SlotEquipment || slot instanceof ContainerSlotDrop.SlotDrop)) { - slot = new ContainerSlotUninteractable.SlotEmpty(inventory, index, x, y); - } addSlot(slot); } } @@ -92,6 +89,45 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) this.topSize = slots.size() - 36; } + private Slot getUpperSlot(int index, int x, int y, boolean ownInv) { + Slot slot = inventory.getMenuSlot(index, x, y); + + // If the slot is cannot be interacted with there's nothing to configure. + if (slot.getClass().equals(ContainerSlotUninteractable.SlotUninteractable.class)) { + return slot; + } + + // Remove drop slot if viewer is not allowed to use it. + if (slot instanceof ContainerSlotDrop.SlotDrop + && !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity())) { + return new ContainerSlotUninteractable.SlotUninteractable(inventory, index, x, y); + } + + if (slot instanceof ContainerSlotEquipment.SlotEquipment equipment) { + Permissions perm = switch (equipment.getEquipmentSlot()) { + case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY; + case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY; + case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY; + case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY; + // Off-hand can hold anything, not just equipment. + default -> null; + }; + // If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot. + if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) { + equipment.onlyEquipmentFor(inventory.getOwnerHandle()); + } + // Equipment slots are a core part of the inventory, so they will always be shown. + return slot; + } + + // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). + if (ownInv && !(slot instanceof ContainerSlotDrop.SlotDrop)) { + return new ContainerSlotUninteractable.SlotUninteractable(inventory, index, x, y); + } + + return slot; + } + static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { int size = inventory.getContainerSize(); if (inventory.getOwnerHandle().equals(viewer)) { @@ -465,7 +501,7 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { @Override public boolean canDragTo(Slot slot) { - return !(slot instanceof ContainerSlotDrop.SlotDrop || slot instanceof ContainerSlotUninteractable.SlotEmpty); + return !(slot instanceof ContainerSlotDrop.SlotDrop || slot instanceof ContainerSlotUninteractable.SlotUninteractable); } } diff --git a/plugin/pom.xml b/plugin/pom.xml index 3eae70a6..2a7b9df6 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvplugincore diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index 38426d6f..0e425fb1 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -16,6 +16,7 @@ package com.lishid.openinv; +import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; @@ -34,6 +35,8 @@ import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + /** * Listener for inventory-related events to prevent modification of inventories where not allowed. * @@ -84,7 +87,7 @@ private void handleInventoryInteract(@NotNull final InventoryInteractEvent event HumanEntity entity = event.getWhoClicked(); // Un-cancel spectator interactions. - if (Permissions.SPECTATE.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) { + if (entity.getGameMode() == GameMode.SPECTATOR && Permissions.SPECTATE_CLICK.hasPermission(entity)) { event.setCancelled(false); } @@ -93,26 +96,34 @@ private void handleInventoryInteract(@NotNull final InventoryInteractEvent event } Inventory inventory = event.getView().getTopInventory(); + ISpecialInventory backing = InventoryAccess.getInventory(inventory); - // Is the inventory a special ender chest? - if (InventoryAccess.isEnderChest(inventory)) { - // Disallow ender chest interaction for users without edit permission. - if (!Permissions.EDITENDER.hasPermission(entity)) { - event.setCancelled(true); - } + // Not a special inventory. + if (backing == null) { return; } - ISpecialPlayerInventory playerInventory = InventoryAccess.getPlayerInventory(inventory); - - // Ignore inventories other than special player inventories. - if (playerInventory == null) { + Permissions editSelf; + Permissions editOther; + if (backing instanceof ISpecialEnderChest) { + editSelf = Permissions.ENDERCHEST_EDIT_SELF; + editOther = Permissions.ENDERCHEST_EDIT_OTHER; + } else if (backing instanceof ISpecialPlayerInventory) { + editSelf = Permissions.INVENTORY_EDIT_SELF; + editOther = Permissions.INVENTORY_EDIT_OTHER; + } else { + // Unknown implementation. return; } - // Disallow player inventory interaction for users without edit permission. - if (!Permissions.EDITINV.hasPermission(entity)) { - event.setCancelled(true); + if (Objects.equals(entity, backing.getPlayer())) { + if (!editSelf.hasPermission(entity)) { + event.setCancelled(true); + } + } else { + if (!editOther.hasPermission(entity)) { + event.setCancelled(true); + } } } diff --git a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java b/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java index 7586fc9c..9e49ca54 100644 --- a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java @@ -1,5 +1,6 @@ package com.lishid.openinv; +import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; @@ -23,6 +24,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -148,7 +150,7 @@ private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent ev HumanEntity entity = event.getWhoClicked(); // Un-cancel spectator interactions. - if (Permissions.SPECTATE.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) { + if (Permissions.SPECTATE_CLICK.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) { event.setCancelled(false); } @@ -157,31 +159,32 @@ private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent ev } Inventory inventory = event.getView().getTopInventory(); - - // Is the inventory a special ender chest? - if (InventoryAccess.isEnderChest(inventory)) { - // Disallow ender chest interaction for users without edit permission. - if (!Permissions.EDITENDER.hasPermission(entity)) { - event.setCancelled(true); - } - return true; - } - - ISpecialPlayerInventory playerInventory = InventoryAccess.getPlayerInventory(inventory); - - // Ignore inventories other than special player inventories. - if (playerInventory == null) { + ISpecialInventory backing = InventoryAccess.getInventory(inventory); + Permissions editSelf; + Permissions editOther; + if (backing instanceof ISpecialEnderChest) { + editSelf = Permissions.ENDERCHEST_EDIT_SELF; + editOther = Permissions.ENDERCHEST_EDIT_OTHER; + } else if (backing instanceof ISpecialPlayerInventory) { + editSelf = Permissions.INVENTORY_EDIT_SELF; + editOther = Permissions.INVENTORY_EDIT_OTHER; + } else { + // Unknown implementation. return true; } - // Disallow player inventory interaction for users without edit permission. - if (!Permissions.EDITINV.hasPermission(entity)) { - event.setCancelled(true); + if (Objects.equals(entity, backing.getPlayer())) { + if (!editSelf.hasPermission(entity)) { + event.setCancelled(true); + return true; + } + return !(backing instanceof ISpecialPlayerInventory); + } else { + if (!editOther.hasPermission(entity)) { + event.setCancelled(true); + } return true; } - - // Only specially handle actions in the player's own inventory. - return !event.getWhoClicked().equals(playerInventory.getPlayer()); } private static int convertToPlayerSlot(InventoryView view, int rawSlot) { diff --git a/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java b/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java index ea0e8c3f..d2dee95c 100644 --- a/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java +++ b/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java @@ -18,11 +18,12 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.util.Permissions; +import org.jetbrains.annotations.NotNull; + import java.util.Map; import java.util.UUID; import java.util.function.BiFunction; import java.util.function.Consumer; -import org.jetbrains.annotations.NotNull; record OfflineHandler( @NotNull BiFunction, UUID, ISpecialInventory> fetch, @@ -35,7 +36,7 @@ record OfflineHandler( static final OfflineHandler REQUIRE_PERMISSIONS = new OfflineHandler( Map::get, - inventory -> OpenInv.ejectViewers(inventory, viewer -> !Permissions.OPENOFFLINE.hasPermission(viewer)) + inventory -> OpenInv.ejectViewers(inventory, viewer -> !Permissions.ACCESS_OFFLINE.hasPermission(viewer)) ); } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index a42f2adf..a945e8fc 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -221,16 +221,7 @@ public boolean noArgsOpensSelf() { @Override public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) { - boolean defaultState = false; - - if (offline.isOnline()) { - Player onlinePlayer = offline.getPlayer(); - if (onlinePlayer != null) { - defaultState = Permissions.ANY_DEFAULT.hasPermission(onlinePlayer); - } - } - - return this.getConfig().getBoolean("toggles.any-chest." + offline.getUniqueId(), defaultState); + return this.getConfig().getBoolean("toggles.any-chest." + offline.getUniqueId(), false); } @Override @@ -241,16 +232,7 @@ public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final bo @Override public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) { - boolean defaultState = false; - - if (offline.isOnline()) { - Player onlinePlayer = offline.getPlayer(); - if (onlinePlayer != null) { - defaultState = Permissions.SILENT_DEFAULT.hasPermission(onlinePlayer); - } - } - - return this.getConfig().getBoolean("toggles.silent-chest." + offline.getUniqueId(), defaultState); + return this.getConfig().getBoolean("toggles.silent-chest." + offline.getUniqueId(), false); } @Override @@ -450,7 +432,7 @@ private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInve ejectViewers( inventory, viewer -> - !Permissions.CROSSWORLD.hasPermission(viewer) + !Permissions.ACCESS_CROSSWORLD.hasPermission(viewer) && !Objects.equals(viewer.getWorld(), player.getWorld())); } @@ -655,8 +637,8 @@ private void setPlayerOnline( ejectViewers( inventory, viewer -> - !Permissions.OPENONLINE.hasPermission(viewer) - || !Permissions.CROSSWORLD.hasPermission(viewer) + !Permissions.ACCESS_ONLINE.hasPermission(viewer) + || !Permissions.ACCESS_CROSSWORLD.hasPermission(viewer) && !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld())); } diff --git a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java index fa0a201c..64ad0592 100644 --- a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java @@ -61,14 +61,14 @@ private void onPlayerInteract(@NotNull PlayerInteractEvent event) { } Player player = event.getPlayer(); - boolean any = Permissions.ANYCHEST.hasPermission(player) && plugin.getAnyContainerStatus(player); + boolean any = Permissions.CONTAINER_ANY.hasPermission(player) && plugin.getAnyContainerStatus(player); boolean needsAny = plugin.getAnySilentContainer().isAnyContainerNeeded(event.getClickedBlock()); if (!any && needsAny) { return; } - boolean silent = Permissions.SILENT.hasPermission(player) && plugin.getSilentContainerStatus(player); + boolean silent = Permissions.CONTAINER_SILENT.hasPermission(player) && plugin.getSilentContainerStatus(player); // If anycontainer or silentcontainer is active if (any || silent) { diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index d4f193aa..fba649e7 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -21,10 +21,6 @@ import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.Replacement; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.StringJoiner; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -34,6 +30,12 @@ import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.StringJoiner; +import java.util.logging.Level; + public class OpenInvCommand implements TabExecutor { private final OpenInv plugin; @@ -138,7 +140,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool boolean online = target.isOnline(); if (!online) { - if (!plugin.disableOfflineAccess() && Permissions.OPENOFFLINE.hasPermission(player)) { + if (!plugin.disableOfflineAccess() && Permissions.ACCESS_OFFLINE.hasPermission(player)) { // Try loading the player's data onlineTarget = this.plugin.loadPlayer(target); } else { @@ -146,7 +148,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool return; } } else { - if (Permissions.OPENONLINE.hasPermission(player)) { + if (Permissions.ACCESS_ONLINE.hasPermission(player)) { onlineTarget = target.getPlayer(); } else { plugin.sendMessage(player, "messages.error.permissionPlayerOnline"); @@ -161,30 +163,34 @@ private void openInventory(final Player player, final OfflinePlayer target, bool // Permissions checks if (onlineTarget.equals(player)) { - // Inventory: Additional permission required to open own inventory - if (openinv && !Permissions.OPENSELF.hasPermission(player)) { + // Permission for opening own inventory. + if (!(openinv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(player)) { plugin.sendMessage(player, "messages.error.permissionOpenSelf"); return; + } } else { - // Enderchest: Additional permission required to open others' ender chests - if (!openinv && !Permissions.ENDERCHEST_ALL.hasPermission(player)) { - plugin.sendMessage(player, "messages.error.permissionEnderAll"); + // Permission for opening others' inventories. + if (!(openinv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(player)) { + plugin.sendMessage(player, "messages.error.permissionOpenOther"); return; } // Protected check - if (!Permissions.OVERRIDE.hasPermission(player) - && Permissions.EXEMPT.hasPermission(onlineTarget)) { - plugin.sendMessage( + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + if (onlineTarget.hasPermission(permission) + && !player.hasPermission(permission)) { + plugin.sendMessage( player, "messages.error.permissionExempt", new Replacement("%target%", onlineTarget.getDisplayName())); - return; + return; + } } // Crossworld check - if (!Permissions.CROSSWORLD.hasPermission(player) + if (!Permissions.ACCESS_CROSSWORLD.hasPermission(player) && !onlineTarget.getWorld().equals(player.getWorld())) { plugin.sendMessage( player, @@ -205,7 +211,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool inv = openinv ? this.plugin.getSpecialInventory(onlineTarget, online) : this.plugin.getSpecialEnderChest(onlineTarget, online); } catch (Exception e) { plugin.sendMessage(player, "messages.error.commandException"); - e.printStackTrace(); + plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); return; } diff --git a/plugin/src/main/java/com/lishid/openinv/util/Permissions.java b/plugin/src/main/java/com/lishid/openinv/util/Permissions.java index c2af2e1c..dc1a4e74 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/Permissions.java +++ b/plugin/src/main/java/com/lishid/openinv/util/Permissions.java @@ -21,59 +21,40 @@ public enum Permissions { - OPENINV("openinv"), - OVERRIDE("override"), - EXEMPT("exempt"), - CROSSWORLD("crossworld"), - SILENT("silent"), - SILENT_DEFAULT("silent.default", true), - ANYCHEST("anychest"), - ANY_DEFAULT("any.default", true), - ENDERCHEST("openender"), - ENDERCHEST_ALL("openenderall"), - SEARCH("search"), - EDITINV("editinv"), - EDITENDER("editender"), - OPENSELF("openself"), - OPENONLINE("openonline"), - OPENOFFLINE("openoffline"), - SPECTATE("spectate"); + INVENTORY_OPEN_SELF("inventory.open.self"), + INVENTORY_OPEN_OTHER("inventory.open.other"), + INVENTORY_EDIT_SELF("inventory.edit.self"), + INVENTORY_EDIT_OTHER("inventory.edit.other"), + INVENTORY_SLOT_HEAD_ANY("inventory.slot.head.any"), + INVENTORY_SLOT_CHEST_ANY("inventory.slot.chest.any"), + INVENTORY_SLOT_LEGS_ANY("inventory.slot.legs.any"), + INVENTORY_SLOT_FEET_ANY("inventory.slot.feet.any"), + INVENTORY_SLOT_DROP("inventory.slot.drop"), + + ENDERCHEST_OPEN_SELF("enderchest.open.self"), + ENDERCHEST_OPEN_OTHER("enderchest.open.other"), + ENDERCHEST_EDIT_SELF("enderchest.edit.self"), + ENDERCHEST_EDIT_OTHER("enderchest.edit.other"), + + ACCESS_CROSSWORLD("access.crossworld"), + ACCESS_OFFLINE("access.offline"), + ACCESS_ONLINE("access.online"), + + SPECTATE_CLICK("spectate.click"), + + CONTAINER_ANY("container.any"), + CONTAINER_SILENT("container.silent"), + SEARCH_INVENTORY("search.inventory"), + SEARCH_CONTAINER("search.container"); private final String permission; - private final boolean uninheritable; Permissions(String permission) { - this(permission, false); - } - - Permissions(String permission, boolean uninheritable) { - this.permission = "OpenInv." + permission; - this.uninheritable = uninheritable; + this.permission = "openinv." + permission; } public boolean hasPermission(@NotNull Permissible permissible) { - - boolean hasPermission = permissible.hasPermission(permission); - if (uninheritable || hasPermission || permissible.isPermissionSet(permission)) { - return hasPermission; - } - - StringBuilder permissionDestroyer = new StringBuilder(permission); - for (int lastPeriod = permissionDestroyer.lastIndexOf("."); lastPeriod > 0; - lastPeriod = permissionDestroyer.lastIndexOf(".")) { - permissionDestroyer.delete(lastPeriod + 1, permissionDestroyer.length()).append('*'); - - hasPermission = permissible.hasPermission(permissionDestroyer.toString()); - if (hasPermission || permissible.isPermissionSet(permissionDestroyer.toString())) { - return hasPermission; - } - - permissionDestroyer.delete(lastPeriod, permissionDestroyer.length()); - - } - - return permissible.hasPermission("*"); - + return permissible.hasPermission(permission); } } diff --git a/plugin/src/main/resources/locale/en_us.yml b/plugin/src/main/resources/locale/en_us.yml index 58e66f2c..829a0e3c 100644 --- a/plugin/src/main/resources/locale/en_us.yml +++ b/plugin/src/main/resources/locale/en_us.yml @@ -6,11 +6,11 @@ messages: invalidNumber: '&cInvalid number: "%target%"' invalidPlayer: '&cPlayer not found!' permissionOpenSelf: '&cYou''re not allowed to open your own inventory.' - permissionEnderAll: '&cYou''re not allowed to access other players'' ender chests.' + permissionOpenOther: '&cYou''re not allowed to access others'' inventories.' permissionExempt: '&c%target%''s inventory is protected.' permissionCrossWorld: '&c%target% is not in your world.' - permissionPlayerOnline: '&cYou''re not allowed to open the inventory of online players.' - permissionPlayerOffline: '&cYou''re not allowed to open the inventory of offline players.' + permissionPlayerOnline: '&cYou''re not allowed to open inventories of online players.' + permissionPlayerOffline: '&cYou''re not allowed to open inventories of offline players.' commandException: '&cAn error occurred. Please check console for details.' info: containerBlocked: 'You are opening a blocked container.' diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 767b886d..dd8014a2 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -3,87 +3,123 @@ main: com.lishid.openinv.OpenInv version: ${project.version} author: lishid authors: [Jikoo, ShadowRanger] -description: > - This plugin allows you to open a player's inventory as a chest and interact with it in real time. +description: Open a player's inventory as a chest and interact with it in real time. api-version: "1.16" permissions: - OpenInv.any.default: - description: Permission for AnyContainer to default on prior to toggling. - default: false - OpenInv.silent.default: - description: Permission for SilentContainer to default on prior to toggling. - default: false - OpenInv.*: - description: Permission for all OpenInv features. - default: op - children: - OpenInv.openinv: true - OpenInv.openender: true - OpenInv.search: true - OpenInv.silent: true - OpenInv.anychest: true - OpenInv.searchenchant: true - OpenInv.searchcontainer: true - OpenInv.openonline: true - OpenInv.openoffline: true - OpenInv.spectate: true - OpenInv.openinv: - default: op - children: - OpenInv.openonline: true - OpenInv.openoffline: true - OpenInv.openender: - default: op + + openinv: children: - OpenInv.openonline: true - OpenInv.openoffline: true + # Inventory nodes (/openinv) + openinv.inventory: + children: + openinv.inventory.open: + children: + openinv.inventory.open.self: true + openinv.inventory.open.other: true + openinv.inventory.edit: + children: + openinv.inventory.open: true + openinv.inventory.edit.self: + children: + openinv.inventory.open.self: true + openinv.inventory.edit.other: + children: + openinv.inventory.open.other: true + # Specific slot behaviors inside opened player inventories + openinv.inventory.slot: + default: true + children: + openinv.inventory.slot.head.any: true + openinv.inventory.slot.chest.any: true + openinv.inventory.slot.legs.any: true + openinv.inventory.slot.feet.any: true + openinv.inventory.slot.drop: true + # Ender chest nodes (/openender) + openinv.enderchest: + children: + openinv.enderchest.open: + children: + openinv.enderchest.open.self: true + openinv.enderchest.open.other: true + openinv.enderchest.edit: + children: + openinv.enderchest.edit.self: + children: + openinv.enderchest.open.self: true + openinv.enderchest.edit.other: + children: + openinv.enderchest.open.other: true + # Player access + openinv.access: + children: + openinv.access.offline: true + openinv.access.online: true + openinv.access.crossworld: true + openinv.access.level.1: true + openinv.access.level.2: false + openinv.access.level.3: false + openinv.access.level.4: false + # Spectate features + openinv.spectate: + children: + openinv.spectate.click: true + # Container features + openinv.container: + children: + openinv.container.any: true + openinv.container.silent: true + # Search functionality + openinv.search: + children: + openinv.search.inventory: true + openinv.search.container: true commands: openinv: aliases: [oi, inv, open] description: Open a player's inventory - permission: OpenInv.openinv + permission: openinv.inventory.open.self;openinv.inventory.open.other usage: |- / [Player] - Open a player's inventory openender: aliases: [oe] description: Opens the enderchest of a player - permission: OpenInv.openender + permission: openinv.enderchest.open.self;openinv.enderchest.open.other usage: |- / [Player] - Open a player's enderchest searchinv: aliases: [si] description: Search and list players having a specific item - permission: OpenInv.search + permission: openinv.search.inventory usage: |- / [MinAmount] - MinAmount is optional, the minimum amount required searchender: aliases: [se] - permission: OpenInv.search + permission: openinv.search.inventory description: Searches and lists players having a specific item in their ender chest usage: |- / [MinAmount] - MinAmount is optional, the minimum amount required silentcontainer: aliases: [sc, silent, silentchest] description: SilentContainer stops sounds and animations when using containers. - permission: OpenInv.silent + permission: openinv.container.silent usage: |- / [check|on|off] - Check, toggle, or set SilentContainer anycontainer: aliases: [ac, anychest] description: AnyContainer allows using blocked containers. - permission: OpenInv.anychest + permission: openinv.container.any usage: |- / [check|on|off] - Check, toggle, or set AnyContainer searchenchant: aliases: [searchenchants] description: Search and list players with a specific enchantment. - permission: OpenInv.searchenchant + permission: openinv.search.inventory usage: |- / <[Enchantment] [MinLevel]> - Enchantment is the enchantment type, MinLevel is the minimum level. One is optional searchcontainer: aliases: [searchchest] description: Search and list containers with a specific material. - permission: OpenInv.searchcontainer + permission: openinv.search.container usage: / [ChunkRadius] - ChunkRadius is optional, the length that will be searched for matching items. Default 5 diff --git a/pom.xml b/pom.xml index 10662f02..9a94f68a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ openinvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT pom @@ -89,13 +89,13 @@ openinvapi com.lishid compile - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT openinvplugincore com.lishid compile - 4.4.8-SNAPSHOT + 5.0.0-SNAPSHOT com.lishid From 1cfa9bdd81d279c03b3b94f3d454d88a07e8c36d Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 7 Jul 2024 10:05:59 -0400 Subject: [PATCH 186/340] Search all slots with /searchinv (#218) --- .../commands/SearchContainerCommand.java | 2 +- .../commands/SearchEnchantCommand.java | 23 ++++++++----------- .../openinv/commands/SearchInvCommand.java | 19 +++++++++++---- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java index 161dfedf..b524b2f9 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java @@ -105,7 +105,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } // Matches found, delete trailing comma and space - if (locations.length() > 0) { + if (!locations.isEmpty()) { locations.delete(locations.length() - 2, locations.length()); } else { plugin.sendMessage( diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java index 2c52f5d9..8abaec9e 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java @@ -19,10 +19,9 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.Replacement; -import java.util.Collections; -import java.util.List; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -34,6 +33,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.List; + /** * Command adding the ability to search online players' inventories for enchantments of a specific * type at or above the level specified. @@ -64,19 +66,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } catch (NumberFormatException ignored) {} argument = argument.toLowerCase(); - int colon = argument.indexOf(':'); - NamespacedKey key; - try { - if (colon > -1 && colon < argument.length() - 1) { - key = new NamespacedKey(argument.substring(0, colon), argument.substring(colon + 1)); - } else { - key = NamespacedKey.minecraft(argument); - } - } catch (IllegalArgumentException ignored) { + NamespacedKey key = NamespacedKey.fromString(argument); + if (key == null) { continue; } - Enchantment localEnchant = Enchantment.getByKey(key); + Enchantment localEnchant = Registry.ENCHANTMENT.get(key); if (localEnchant != null) { enchant = localEnchant; } @@ -111,7 +106,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command players.append("), "); } - if (players.length() > 0) { + if (!players.isEmpty()) { // Matches found, delete trailing comma and space players.delete(players.length() - 2, players.length()); } else { @@ -164,7 +159,7 @@ public List onTabComplete(@NotNull CommandSender sender, @NotNull Comman } if (args.length == 1) { - return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Enchantment.values()); + return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Registry.ENCHANTMENT.stream().toArray(Enchantment[]::new)); } else { return TabCompleter.completeInteger(args[1]); } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java index 60235bd4..f40588c5 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java @@ -19,16 +19,18 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.Replacement; -import java.util.Collections; -import java.util.List; import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.List; + public class SearchInvCommand implements TabExecutor { private final OpenInv plugin; @@ -72,13 +74,20 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command boolean searchInv = command.getName().equals("searchinv"); for (Player player : plugin.getServer().getOnlinePlayers()) { Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest(); - if (inventory.contains(material, count)) { - players.append(player.getName()).append(", "); + int total = 0; + for (ItemStack itemStack : inventory.getContents()) { + if (itemStack != null && itemStack.getType() == material) { + total += itemStack.getAmount(); + if (total >= count) { + players.append(player.getName()).append(", "); + break; + } + } } } // Matches found, delete trailing comma and space - if (players.length() > 0) { + if (!players.isEmpty()) { players.delete(players.length() - 2, players.length()); } else { plugin.sendMessage( From b895cb400b671f034627b39ef46229064f6def0d Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 10 Jul 2024 10:40:44 -0400 Subject: [PATCH 187/340] Remove deprecated methods, restructure (#219) --- README.MD | 8 +- .../java/com/lishid/openinv/IOpenInv.java | 111 +---------- .../openinv/event/OpenPlayerSaveEvent.java | 65 ++----- .../lishid/openinv/event/PlayerSaveEvent.java | 67 +++++++ .../openinv/internal/IAnySilentContainer.java | 36 +--- .../openinv/internal/IInventoryAccess.java | 62 ------ .../lishid/openinv/util/InventoryAccess.java | 69 ++----- assembly/pom.xml | 58 ------ assembly/src/assembly/reactor-uberjar.xml | 54 ------ common/pom.xml | 30 +++ .../com/lishid/openinv/event/OpenEvents.java | 32 +++ .../com/lishid/openinv/internal/Accessor.java | 24 +++ .../openinv/internal/InventoryViewTitle.java | 10 +- .../openinv/internal/OpenInventoryView.java | 0 .../openinv/internal/PlaceholderParser.java | 0 .../openinv/internal/PlayerManager.java | 2 +- .../com/lishid/openinv/util/Permissions.java | 0 .../lishid/openinv/util/ReflectionHelper.java | 11 +- .../openinv/util/lang/LanguageManager.java | 81 +++++++- .../lishid/openinv/util/lang/Replacement.java | 0 internal/v1_20_R3/pom.xml | 10 +- .../internal/v1_20_R3/AnySilentContainer.java | 23 +-- .../internal/v1_20_R3/InternalAccessor.java | 64 ++++++ .../openinv/internal/v1_20_R3/OpenPlayer.java | 12 +- ...yerDataManager.java => PlayerManager.java} | 27 +-- .../internal/v1_20_R3/SpecialEnderChest.java | 8 +- .../v1_20_R3/SpecialPlayerInventory.java | 4 +- internal/v1_20_R4/pom.xml | 10 +- .../internal/v1_20_R4/AnySilentContainer.java | 23 +-- .../internal/v1_20_R4/InternalAccessor.java | 64 ++++++ .../openinv/internal/v1_20_R4/OpenPlayer.java | 12 +- ...yerDataManager.java => PlayerManager.java} | 27 +-- .../internal/v1_20_R4/SpecialEnderChest.java | 8 +- .../v1_20_R4/SpecialPlayerInventory.java | 4 +- internal/v1_21_R1/pom.xml | 10 +- .../internal/v1_21_R1/AnySilentContainer.java | 23 +-- .../internal/v1_21_R1/InternalAccessor.java | 78 ++++++++ .../openinv/internal/v1_21_R1/OpenPlayer.java | 12 +- ...yerDataManager.java => PlayerManager.java} | 29 +-- .../internal/v1_21_R1/SpecialEnderChest.java | 8 +- .../inventory/ContainerSlotCrafting.java | 2 +- .../ContainerSlotCraftingResult.java | 2 +- .../inventory/ContainerSlotCursor.java | 4 +- .../v1_21_R1/inventory/ContainerSlotDrop.java | 4 +- .../inventory/ContainerSlotEquipment.java | 10 +- .../ContainerSlotUninteractable.java | 19 +- .../v1_21_R1/inventory/OpenInventory.java | 10 +- .../v1_21_R1/inventory/OpenInventoryMenu.java | 14 ++ ...ceholderManager.java => Placeholders.java} | 12 +- plugin/pom.xml | 60 +++++- .../com/lishid/openinv/InventoryListener.java | 4 + .../openinv/LegacyInventoryListener.java | 8 +- .../main/java/com/lishid/openinv/OpenInv.java | 112 +++-------- .../com/lishid/openinv/PlayerListener.java | 22 ++- .../commands/ContainerSettingCommand.java | 27 +-- .../openinv/commands/OpenInvCommand.java | 29 +-- .../commands/SearchContainerCommand.java | 27 +-- .../commands/SearchEnchantCommand.java | 22 ++- .../openinv/commands/SearchInvCommand.java | 21 +- .../openinv/{ => util}/InternalAccessor.java | 182 ++++-------------- .../com/lishid/openinv/util/StringMetric.java | 0 .../com/lishid/openinv/util/TabCompleter.java | 19 +- pom.xml | 101 +++++----- 63 files changed, 970 insertions(+), 917 deletions(-) create mode 100644 api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java delete mode 100644 api/src/main/java/com/lishid/openinv/internal/IInventoryAccess.java delete mode 100644 assembly/pom.xml delete mode 100644 assembly/src/assembly/reactor-uberjar.xml create mode 100644 common/pom.xml create mode 100644 common/src/main/java/com/lishid/openinv/event/OpenEvents.java create mode 100644 common/src/main/java/com/lishid/openinv/internal/Accessor.java rename {plugin => common}/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java (82%) rename {plugin => common}/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java (100%) rename {plugin => common}/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java (100%) rename plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java => common/src/main/java/com/lishid/openinv/internal/PlayerManager.java (97%) rename {plugin => common}/src/main/java/com/lishid/openinv/util/Permissions.java (100%) rename {api => common}/src/main/java/com/lishid/openinv/util/ReflectionHelper.java (93%) rename {plugin => common}/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java (79%) rename {plugin => common}/src/main/java/com/lishid/openinv/util/lang/Replacement.java (100%) create mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java rename internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/{PlayerDataManager.java => PlayerManager.java} (93%) create mode 100644 internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java rename internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/{PlayerDataManager.java => PlayerManager.java} (93%) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{PlayerDataManager.java => PlayerManager.java} (93%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{PlaceholderManager.java => Placeholders.java} (96%) rename plugin/src/main/java/com/lishid/openinv/{ => util}/InternalAccessor.java (50%) rename {api => plugin}/src/main/java/com/lishid/openinv/util/StringMetric.java (100%) diff --git a/README.MD b/README.MD index 66956203..7354892e 100644 --- a/README.MD +++ b/README.MD @@ -49,11 +49,9 @@ As OpenInv is compiled against Mojang's mappings, you must run BuildTools with t `java -jar BuildTools.jar --remapped --rev $serverVersion` `$serverVersion` is the version of the server, i.e. `1.18.1` -To compile for a single version, specify the module you are targeting: -`mvn -pl $moduleName -am clean install` -`$moduleName` is the name of the module, i.e. `internal/v1_18_R1`. +To compile just the API, execute Maven as usual: +`mvn clean package` -To compile for a set of versions, use a profile. Select a profile using the `-P` argument: +To compile the full plugin, use the provided profile with `-P all`: `mvn clean package -am -P all` -The only provided profile is `all`. The final file is `target/OpenInv.jar` For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html). diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index d1e0155f..7c4e1bc8 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -17,21 +17,19 @@ package com.lishid.openinv; import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.internal.IInventoryAccess; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.util.InventoryAccess; -import java.util.UUID; -import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; -import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.UUID; +import java.util.logging.Logger; + /** * Interface defining behavior for the OpenInv plugin. */ @@ -80,22 +78,6 @@ public interface IOpenInv { */ @NotNull IAnySilentContainer getAnySilentContainer(); - /** - * @deprecated Use static {@link InventoryAccess} methods. - */ - @Deprecated(forRemoval = true) - default @NotNull IInventoryAccess getInventoryAccess() { - return new InventoryAccess(); - } - - /** - * @deprecated Use {@link #getAnyContainerStatus(OfflinePlayer)}. Not all containers are chests. - */ - @Deprecated(forRemoval = true, since = "4.2.0") - default boolean getPlayerAnyChestStatus(@NotNull OfflinePlayer offline) { - return getAnyContainerStatus(offline); - } - /** * Get whether a user has AnyContainer mode enabled. * @@ -104,14 +86,6 @@ default boolean getPlayerAnyChestStatus(@NotNull OfflinePlayer offline) { */ boolean getAnyContainerStatus(@NotNull OfflinePlayer offline); - /** - * @deprecated Use {@link #setAnyContainerStatus(OfflinePlayer, boolean)}. Not all containers are chests. - */ - @Deprecated(forRemoval = true, since = "4.2.0") - default void setPlayerAnyChestStatus(@NotNull OfflinePlayer offline, boolean status) { - setAnyContainerStatus(offline, status); - } - /** * Set whether a user has AnyContainer mode enabled. * @@ -120,14 +94,6 @@ default void setPlayerAnyChestStatus(@NotNull OfflinePlayer offline, boolean sta */ void setAnyContainerStatus(@NotNull OfflinePlayer offline, boolean status); - /** - * @deprecated Use {@link #getSilentContainerStatus(OfflinePlayer)}. Not all containers are chests. - */ - @Deprecated(forRemoval = true, since = "4.2.0") - default boolean getPlayerSilentChestStatus(@NotNull OfflinePlayer offline) { - return getSilentContainerStatus(offline); - } - /** * Get whether a user has SilentContainer mode enabled. * @@ -136,14 +102,6 @@ default boolean getPlayerSilentChestStatus(@NotNull OfflinePlayer offline) { */ boolean getSilentContainerStatus(@NotNull OfflinePlayer offline); - /** - * @deprecated Use {@link #setSilentContainerStatus(OfflinePlayer, boolean)}. Not all containers are chests. - */ - @Deprecated(forRemoval = true, since = "4.2.0") - default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean status) { - setSilentContainerStatus(offline, status); - } - /** * Set whether a user has SilentContainer mode enabled. * @@ -152,20 +110,6 @@ default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean */ void setSilentContainerStatus(@NotNull OfflinePlayer offline, boolean status); - /** - * Get a unique identifier by which the OfflinePlayer can be referenced. - * - * @deprecated Use {@link OfflinePlayer#getUniqueId()} and {@link UUID#toString()}. This was necessary for non-UUID - * versions of Minecraft, but support for them has been dropped for years. - * @param offline the OfflinePlayer - * @return the identifier - * @throws IllegalStateException if the server version is unsupported - */ - @Deprecated(forRemoval = true) - default @NotNull String getPlayerID(@NotNull OfflinePlayer offline) { - return offline.getUniqueId().toString(); - } - /** * Get an {@link ISpecialEnderChest} for a user. * @@ -229,55 +173,6 @@ default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean */ @Nullable OfflinePlayer matchPlayer(@NotNull String name); - /** - * @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings. - */ - @Deprecated(forRemoval = true) - default boolean notifyAnyChest() { - return true; - } - - /** - * @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings. - */ - @Deprecated(forRemoval = true) - default boolean notifySilentChest() { - return true; - } - - /** - * @deprecated see {@link #retainPlayer(Player, Plugin)} - */ - @Deprecated(forRemoval = true, since = "4.2.0") - default void releasePlayer(@NotNull Player player, @NotNull Plugin plugin) {} - - /** - * @deprecated OpenInv no longer uses an internal cache beyond maintaining copies of currently open inventories. - * If you wish to use/modify a player, ensure either {@link IOpenInv#isPlayerLoaded(UUID)} is false or the player - * instance is the same memory address as the one in use by OpenInv. - *
-     *  public @NotNull Player savePlayerData(@NotNull Player player) {
-     *     IOpenInv openInv = ...
-     *     if (!openInv.disableSaving() && openInv.isPlayerLoaded(player.getUniqueId())) {
-     *         Player openInvLoadedPlayer = openInv.loadPlayer(myInUsePlayer);
-     *         if (openInvLoadedPlayer != player) {
-     *             // The copy loaded by OpenInv is not the same as our loaded copy. Push our changes.
-     *             copyPlayerModifications(player, openInvLoadedPlayer);
-     *         }
-     *         // OpenInv will handle saving data when the player is unloaded.
-     *         // Optionally, to be sure our changes will persist, save now.
-     *         // openInvLoadedPlayer.saveData();
-     *         return openInvLoadedPlayer;
-     *     }
-     *
-     *     player.saveData();
-     *     return player;
-     * }
-     * 
- */ - @Deprecated(forRemoval = true, since = "4.2.0") - default void retainPlayer(@NotNull Player player, @NotNull Plugin plugin) {} - /** * Forcibly close inventories of and unload any cached data for a user. * diff --git a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java index a36a6c96..f20c635a 100644 --- a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java @@ -1,35 +1,34 @@ package com.lishid.openinv.event; +import com.google.errorprone.annotations.RestrictedApi; import com.lishid.openinv.internal.ISpecialInventory; import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** - * Event fired before OpenInv saves a player's data. + * Event fired before OpenInv saves a player's data when closing an {@link ISpecialInventory}. */ -public class OpenPlayerSaveEvent extends Event implements Cancellable { +public class OpenPlayerSaveEvent extends PlayerSaveEvent { - private static final HandlerList HANDLERS = new HandlerList(); - - private final Player player; private final ISpecialInventory inventory; - private boolean cancelled = false; - - public OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) { - this.player = player; - this.inventory = inventory; - } /** - * Get the {@link Player} whose data is being saved. + * Construct a new {@code OpenPlayerSaveEvent}. + * + *

The constructor is not considered part of the API, and may be subject to change.

* - * @return player the Player whose data is being saved + * @param player the player to be saved + * @param inventory the {@link ISpecialInventory} being closed */ - public @NotNull Player getPlayer() { - return player; + @RestrictedApi( + explanation = "Constructor is not considered part of the API and may be subject to change.", + link = "", + allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java") + @ApiStatus.Internal + OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) { + super(player); + this.inventory = inventory; } /** @@ -41,34 +40,4 @@ public OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory in return inventory; } - /** - * Get whether the event is cancelled. - * - * @return true if the event is cancelled - */ - @Override - public boolean isCancelled() { - return cancelled; - } - - /** - * Set whether the event is cancelled. - * - * @param cancel whether the event is cancelled - */ - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return HANDLERS; - } - - public static HandlerList getHandlerList() { - return HANDLERS; - } - } diff --git a/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java new file mode 100644 index 00000000..4ca1d715 --- /dev/null +++ b/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java @@ -0,0 +1,67 @@ +package com.lishid.openinv.event; + + +import com.google.errorprone.annotations.RestrictedApi; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Event fired before a {@link Player} loaded via OpenInv is saved. + */ +public class PlayerSaveEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private boolean cancelled = false; + + /** + * Construct a new {@code PlayerSaveEvent}. + * + *

The constructor is not considered part of the API, and may be subject to change.

+ * + * @param player the player to be saved + */ + @RestrictedApi( + explanation = "Constructor is not considered part of the API and may be subject to change.", + link = "", + allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java") + @ApiStatus.Internal + PlayerSaveEvent(@NotNull Player player) { + super(player); + } + + /** + * Get whether the event is cancelled. + * + * @return true if the event is cancelled + */ + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * Set whether the event is cancelled. + * + * @param cancel whether the event is cancelled + */ + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + +} diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 99026bc3..d4bccb91 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -51,17 +51,6 @@ public interface IAnySilentContainer { */ void deactivateContainer(@NotNull Player player); - /** - * @param player the player opening the container - * @param block the {@link Block} of the container - * @return true if the container is blocked - * @deprecated use {@link #isAnyContainerNeeded(Block)} - */ - @Deprecated(forRemoval = true, since = "4.1.9") - default boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block) { - return isAnyContainerNeeded(block); - } - /** * Check if the container at the given coordinates is blocked. * @@ -99,9 +88,14 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { return false; } - int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4; - BlockFace relativeFace = BlockFace.values()[ordinal]; - org.bukkit.block.Block relative = block.getRelative(relativeFace); + BlockFace relativeFace = switch (chest.getFacing()) { + case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST; + case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH; + case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST; + case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH; + default -> BlockFace.SELF; + }; + Block relative = block.getRelative(relativeFace); if (relative.getType() != block.getType()) { return false; @@ -120,18 +114,6 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { return isChestBlocked(relative); } - /** - * Check if a {@link ShulkerBox} cannot be opened under ordinary circumstances. - * - * @deprecated Use {@link #isShulkerBlocked(Block)}. - * @param shulkerBox the shulker box container - * @return whether the container is blocked - */ - @Deprecated(since = "4.4.4", forRemoval = true) - default boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) { - return isShulkerBlocked(shulkerBox.getBlock()); - } - /** * Check if a shulker box block cannot be opened under ordinary circumstances. * @@ -176,7 +158,7 @@ default boolean isAnySilentContainer(@NotNull Block block) { * @return true if the type is a supported container */ default boolean isAnySilentContainer(@NotNull BlockState blockState) { - return blockState instanceof InventoryHolder holder && isAnySilentContainer(holder) + return (blockState instanceof InventoryHolder holder && isAnySilentContainer(holder)) || blockState instanceof EnderChest; } diff --git a/api/src/main/java/com/lishid/openinv/internal/IInventoryAccess.java b/api/src/main/java/com/lishid/openinv/internal/IInventoryAccess.java deleted file mode 100644 index 7f29a070..00000000 --- a/api/src/main/java/com/lishid/openinv/internal/IInventoryAccess.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal; - -import com.lishid.openinv.util.InventoryAccess; -import org.bukkit.inventory.Inventory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @deprecated Use static {@link InventoryAccess} methods. - */ -@Deprecated(forRemoval = true) -public interface IInventoryAccess { - - /** - * @deprecated Use static {@link InventoryAccess} methods. - */ - @Deprecated(forRemoval = true) - default @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) { - return InventoryAccess.getEnderChest(inventory); - } - - /** - * @deprecated Use static {@link InventoryAccess} methods. - */ - @Deprecated(forRemoval = true) - default @Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory) { - return InventoryAccess.getPlayerInventory(inventory); - } - - /** - * @deprecated Use static {@link InventoryAccess} methods. - */ - @Deprecated(forRemoval = true) - default boolean isSpecialEnderChest(@NotNull Inventory inventory) { - return InventoryAccess.isEnderChest(inventory); - } - - /** - * @deprecated Use static {@link InventoryAccess} methods. - */ - @Deprecated(forRemoval = true) - default boolean isSpecialPlayerInventory(@NotNull Inventory inventory) { - return InventoryAccess.isPlayerInventory(inventory); - } - -} diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index e05bbe1e..466cae61 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -16,40 +16,23 @@ package com.lishid.openinv.util; -import com.lishid.openinv.internal.IInventoryAccess; +import com.google.errorprone.annotations.RestrictedApi; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import org.bukkit.Bukkit; import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Method; +import java.util.function.BiFunction; -public final class InventoryAccess implements IInventoryAccess { +public final class InventoryAccess { - private static Class craftInventory = null; - private static Method getInventory = null; - - static { - String packageName = Bukkit.getServer().getClass().getPackage().getName(); - try { - craftInventory = Class.forName(packageName + ".inventory.CraftInventory"); - getInventory = craftInventory.getDeclaredMethod("getInventory"); - } catch (ClassNotFoundException | NoSuchMethodException ignored) {} - } - - /** - * @deprecated use {@link #isUsable()} - */ - @Deprecated(forRemoval = true) - public static boolean isUseable() { - return isUsable(); - } + private static @Nullable BiFunction, ISpecialInventory> provider; public static boolean isUsable() { - return craftInventory != null && getInventory != null; + return provider != null; } /** @@ -70,7 +53,7 @@ public static boolean isPlayerInventory(@NotNull Inventory inventory) { * @return the backing implementation if available */ public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) { - return getSpecialInventory(ISpecialPlayerInventory.class, inventory); + return provider == null ? null : (ISpecialPlayerInventory) provider.apply(inventory, ISpecialPlayerInventory.class); } /** @@ -91,7 +74,7 @@ public static boolean isEnderChest(@NotNull Inventory inventory) { * @return the backing implementation if available */ public static @Nullable ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) { - return getSpecialInventory(ISpecialEnderChest.class, inventory); + return provider == null ? null : (ISpecialEnderChest) provider.apply(inventory, ISpecialEnderChest.class); } /** @@ -102,34 +85,20 @@ public static boolean isEnderChest(@NotNull Inventory inventory) { * @return the backing implementation if available */ public static @Nullable ISpecialInventory getInventory(@NotNull Inventory inventory) { - return getSpecialInventory(ISpecialInventory.class, inventory); + return provider == null ? null : provider.apply(inventory, ISpecialInventory.class); } - private static @Nullable T getSpecialInventory(@NotNull Class expected, @NotNull Inventory inventory) { - Object inv; - if (isUsable() && craftInventory.isAssignableFrom(inventory.getClass())) { - try { - inv = getInventory.invoke(inventory); - if (expected.isInstance(inv)) { - return expected.cast(inv); - } - } catch (ReflectiveOperationException ignored) {} - } - - // Use reflection to find the IInventory - inv = ReflectionHelper.grabObjectByType(inventory, expected); - - if (expected.isInstance(inv)) { - return expected.cast(inv); - } - - return null; + @RestrictedApi( + explanation = "Not part of the API.", + link = "", + allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java") + @ApiStatus.Internal + static void setProvider(@Nullable BiFunction, ISpecialInventory> provider) { + InventoryAccess.provider = provider; } - /** - * @deprecated Do not create a new instance to use static methods. - */ - @Deprecated(forRemoval = true, since = "4.2.0") - public InventoryAccess() {} + private InventoryAccess() { + throw new IllegalStateException("Cannot create instance of utility class."); + } } diff --git a/assembly/pom.xml b/assembly/pom.xml deleted file mode 100644 index 3fd0775e..00000000 --- a/assembly/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - 4.0.0 - - - com.lishid - openinvparent - 5.0.0-SNAPSHOT - - - openinvassembly - OpenInvAssembly - pom - - - ../target - OpenInv - - - - maven-assembly-plugin - - - reactor-uberjar - package - - single - - - false - - src/assembly/reactor-uberjar.xml - - - - - - - - - - diff --git a/assembly/src/assembly/reactor-uberjar.xml b/assembly/src/assembly/reactor-uberjar.xml deleted file mode 100644 index cb400f35..00000000 --- a/assembly/src/assembly/reactor-uberjar.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - reactor-uberjar - - - jar - - - false - - - - - true - - - / - true - - - - META-INF/** - - - - false - - - - - - diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 00000000..09389665 --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + com.lishid + openinvparent + 5.0.0-SNAPSHOT + + + openinvcommon + + + + annotations + org.jetbrains + + + org.spigotmc + spigot-api + + + openinvapi + com.lishid + + + + diff --git a/common/src/main/java/com/lishid/openinv/event/OpenEvents.java b/common/src/main/java/com/lishid/openinv/event/OpenEvents.java new file mode 100644 index 00000000..77349ccf --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/event/OpenEvents.java @@ -0,0 +1,32 @@ +package com.lishid.openinv.event; + +import com.lishid.openinv.internal.ISpecialInventory; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.jetbrains.annotations.NotNull; + +/** + * Construct and call events. + */ +public final class OpenEvents { + + public static boolean saveCancelled(@NotNull Player player) { + return call(new PlayerSaveEvent(player)); + } + + public static boolean saveCancelled(@NotNull Player player, @NotNull ISpecialInventory inventory) { + return call(new OpenPlayerSaveEvent(player, inventory)); + } + + private static boolean call(T event) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + + private OpenEvents() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + +} diff --git a/common/src/main/java/com/lishid/openinv/internal/Accessor.java b/common/src/main/java/com/lishid/openinv/internal/Accessor.java new file mode 100644 index 00000000..e57a4afb --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/internal/Accessor.java @@ -0,0 +1,24 @@ +package com.lishid.openinv.internal; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface Accessor { + + @NotNull + PlayerManager getPlayerManager(); + + @NotNull IAnySilentContainer getAnySilentContainer(); + + @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player); + + @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player); + + @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz); + + void reload(@NotNull ConfigurationSection config); + +} diff --git a/plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java b/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java similarity index 82% rename from plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java rename to common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java index 7bff29a1..ccd8fd13 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java +++ b/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal; -import com.lishid.openinv.OpenInv; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; @@ -22,11 +22,13 @@ public enum InventoryViewTitle { this.defaultSuffix = defaultSuffix; } - public @NotNull String getTitle(@NotNull Player viewer, @NotNull ISpecialInventory inventory) { + public @NotNull String getTitle( + @NotNull LanguageManager lang, + @NotNull Player viewer, + @NotNull ISpecialInventory inventory) { HumanEntity owner = inventory.getPlayer(); - String localTitle = OpenInv.getPlugin(OpenInv.class) - .getLocalizedMessage( + String localTitle = lang.getLocalizedMessage( viewer, localizationKey, new Replacement("%player%", owner.getName())); diff --git a/plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/common/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java similarity index 100% rename from plugin/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java rename to common/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java diff --git a/plugin/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java b/common/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java similarity index 100% rename from plugin/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java rename to common/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java diff --git a/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java b/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java similarity index 97% rename from plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java rename to common/src/main/java/com/lishid/openinv/internal/PlayerManager.java index 37bb1bf0..d1d3c6c0 100644 --- a/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java +++ b/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public interface IPlayerDataManager { +public interface PlayerManager { /** * Loads a Player for an OfflinePlayer. diff --git a/plugin/src/main/java/com/lishid/openinv/util/Permissions.java b/common/src/main/java/com/lishid/openinv/util/Permissions.java similarity index 100% rename from plugin/src/main/java/com/lishid/openinv/util/Permissions.java rename to common/src/main/java/com/lishid/openinv/util/Permissions.java diff --git a/api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java b/common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java similarity index 93% rename from api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java rename to common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java index 7c283749..2c60ffe0 100644 --- a/api/src/main/java/com/lishid/openinv/util/ReflectionHelper.java +++ b/common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java @@ -16,16 +16,15 @@ package com.lishid.openinv.util; -import java.lang.reflect.Field; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; + /** * A utility for making reflection easier. */ public final class ReflectionHelper { - private ReflectionHelper() {} - /** * Grab an {@link Object} stored in a {@link Field} of another {@code Object}. * @@ -59,8 +58,8 @@ private ReflectionHelper() {} */ public static @Nullable Field grabFieldByType(Class holderType, Class fieldType) { for (Field field : holderType.getDeclaredFields()) { - field.setAccessible(true); if (fieldType.isAssignableFrom(field.getType())) { + field.setAccessible(true); return field; } } @@ -72,4 +71,8 @@ private ReflectionHelper() {} return null; } + private ReflectionHelper() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java similarity index 79% rename from plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java rename to common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java index 4438977b..6e5fe207 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java +++ b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -16,7 +16,18 @@ package com.lishid.openinv.util.lang; -import com.lishid.openinv.OpenInv; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -26,15 +37,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Predicate; import java.util.logging.Level; -import org.bukkit.ChatColor; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * A simple language manager supporting both custom and bundled languages. @@ -43,11 +49,11 @@ */ public class LanguageManager { - private final OpenInv plugin; + private final Plugin plugin; private final String defaultLocale; private final Map locales; - public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) { + public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) { this.plugin = plugin; this.defaultLocale = defaultLocale; this.locales = new HashMap<>(); @@ -191,7 +197,7 @@ private void addTranslationFallthrough( } public @Nullable String getValue(@NotNull String key, @Nullable String locale) { - String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key); + String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ROOT)).getString(key); if (value == null || value.isEmpty()) { return null; } @@ -215,4 +221,59 @@ private void addTranslationFallthrough( return value; } + public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) { + return getValue(key, getLocale(sender)); + } + + public @Nullable String getLocalizedMessage( + @NotNull CommandSender sender, + @NotNull String key, + Replacement @NotNull ... replacements) { + return getValue(key, getLocale(sender), replacements); + } + + private @NotNull String getLocale(@NotNull CommandSender sender) { + if (sender instanceof Player) { + return ((Player) sender).getLocale(); + } else { + return plugin.getConfig().getString("settings.locale", "en_us"); + } + } + + public void sendMessage(@NotNull CommandSender sender, @NotNull String key) { + String message = getLocalizedMessage(sender, key); + + if (message != null && !message.isEmpty()) { + sender.sendMessage(message); + } + } + + public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) { + String message = getLocalizedMessage(sender, key, replacements); + + if (message != null && !message.isEmpty()) { + sender.sendMessage(message); + } + } + + public void sendSystemMessage(@NotNull Player player, @NotNull String key) { + String message = getLocalizedMessage(player, key); + + if (message == null) { + return; + } + + int newline = message.indexOf('\n'); + if (newline != -1) { + // No newlines in action bar chat. + message = message.substring(0, newline); + } + + if (message.isEmpty()) { + return; + } + + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message)); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/util/lang/Replacement.java b/common/src/main/java/com/lishid/openinv/util/lang/Replacement.java similarity index 100% rename from plugin/src/main/java/com/lishid/openinv/util/lang/Replacement.java rename to common/src/main/java/com/lishid/openinv/util/lang/Replacement.java diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index be4bb10b..53508df9 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -30,8 +30,8 @@ OpenInvAdapter1_20_R3 - 17 - 17 + 17 + 17 1.20.4-R0.1-SNAPSHOT @@ -51,10 +51,9 @@ openinvapi com.lishid - provided - openinvplugincore + openinvcommon com.lishid @@ -65,9 +64,6 @@ - - maven-shade-plugin - maven-compiler-plugin diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java index eb6d8fef..403b3ed8 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java @@ -16,9 +16,9 @@ package com.lishid.openinv.internal.v1_20_R3; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.util.ReflectionHelper; +import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -50,15 +50,18 @@ public class AnySilentContainer implements IAnySilentContainer { + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; private @Nullable Field serverPlayerGameModeGameType; - public AnySilentContainer() { + public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; try { try { this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); this.serverPlayerGameModeGameType.setAccessible(true); } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); @@ -66,7 +69,6 @@ public AnySilentContainer() { this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); } } catch (SecurityException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to directly write player game mode! SilentContainer will fail."); logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); } @@ -85,7 +87,7 @@ public boolean activateContainer( return true; } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); final net.minecraft.world.level.Level level = player.level(); final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); @@ -100,10 +102,10 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + MenuType containers = PlayerManager.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable(("container.enderchest")))); + }, Component.translatable("container.enderchest"))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } @@ -121,7 +123,7 @@ public boolean activateContainer( menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); if (menuProvider == null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } @@ -153,7 +155,7 @@ public boolean activateContainer( if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } } @@ -171,7 +173,7 @@ public void deactivateContainer(@NotNull final Player bukkitPlayer) { return; } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. // Regular game mode set calls GameModeChangeEvent and is cancellable. @@ -199,7 +201,6 @@ private void forceGameType(final ServerPlayer player, final GameType gameMode) { this.serverPlayerGameModeGameType.setAccessible(true); this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); } catch (IllegalArgumentException | IllegalAccessException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); } } diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java new file mode 100644 index 00000000..47e199da --- /dev/null +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java @@ -0,0 +1,64 @@ +package com.lishid.openinv.internal.v1_20_R3; + +import com.lishid.openinv.internal.Accessor; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.util.lang.LanguageManager; +import net.minecraft.world.Container; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Logger; + +public class InternalAccessor implements Accessor { + + private final @NotNull PlayerManager manager; + private final @NotNull AnySilentContainer anySilentContainer; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + manager = new PlayerManager(logger, lang); + anySilentContainer = new AnySilentContainer(logger, lang); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { + return anySilentContainer; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new SpecialPlayerInventory(player, player.isOnline()); + } + + @Override + public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { + return new SpecialEnderChest(player, player.isOnline()); + } + + @Override + public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { + if (!(bukkitInventory instanceof CraftInventory craftInventory)) { + return null; + } + Container container = craftInventory.getInventory(); + if (clazz.isInstance(container)) { + return clazz.cast(container); + } + return null; + } + + @Override + public void reload(@NotNull ConfigurationSection config) {} + +} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 2861c2bc..52d553af 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -16,6 +16,7 @@ package com.lishid.openinv.internal.v1_20_R3; +import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; @@ -70,17 +71,24 @@ public class OpenPlayer extends CraftPlayer { "Brain" ); - public OpenPlayer(CraftServer server, ServerPlayer entity) { + private final PlayerManager manager; + + OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { super(server, entity); + this.manager = manager; } @Override public void loadData() { - PlayerDataManager.loadData(getHandle()); + manager.loadData(getHandle()); } @Override public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + ServerPlayer player = this.getHandle(); // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try { diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java similarity index 93% rename from internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java index 17ed8e4e..7f02bb7d 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java @@ -16,11 +16,10 @@ package com.lishid.openinv.internal.v1_20_R3; -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; +import com.lishid.openinv.util.lang.LanguageManager; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -48,7 +47,6 @@ import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,7 +54,7 @@ import java.util.UUID; import java.util.logging.Logger; -public class PlayerDataManager implements IPlayerDataManager { +public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { private static boolean paper; @@ -69,13 +67,16 @@ public class PlayerDataManager implements IPlayerDataManager { } } + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; private @Nullable Field bukkitEntity; - public PlayerDataManager() { + public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; try { bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); } catch (NoSuchFieldException e) { - Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); bukkitEntity = null; @@ -156,7 +157,7 @@ public PlayerDataManager() { try { injectPlayer(entity); } catch (IllegalAccessException e) { - JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + logger.log( java.util.logging.Level.WARNING, e, () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); @@ -165,7 +166,7 @@ public PlayerDataManager() { return entity; } - static boolean loadData(@NotNull ServerPlayer player) { + boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player); @@ -189,7 +190,7 @@ static boolean loadData(@NotNull ServerPlayer player) { return true; } - private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { // See PlayerList#placeNewPlayer World bukkitWorld; if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { @@ -201,7 +202,7 @@ private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTa } else { // Vanilla player data. DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning) + .resultOrPartial(logger::warning) .map(player.server::getLevel) // If ServerLevel exists, set, otherwise move to spawn. .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); @@ -221,7 +222,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { bukkitEntity.setAccessible(true); - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); } @Override @@ -234,7 +235,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { - JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + logger.log( java.util.logging.Level.WARNING, e, () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); @@ -257,7 +258,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return player.openInventory(inventory.getBukkitInventory()); } - InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(player, inventory)); + InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(lang, player, inventory)); AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override public MenuType getType() { diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java index 941e7979..2b8b3f38 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java @@ -49,9 +49,9 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe private boolean playerOnline; public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); + super(PlayerManager.getHandle(player)); this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); + this.owner = PlayerManager.getHandle(player); this.playerOnline = online; this.items = this.owner.getEnderChestInventory().items; } @@ -73,7 +73,7 @@ public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { } ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + ServerPlayer onlinePlayer = PlayerManager.getHandle(player); // Set owner to new player. this.owner = onlinePlayer; @@ -198,7 +198,7 @@ public ItemStack addItem(ItemStack itemstack) { @Override public boolean canAddItem(ItemStack itemstack) { for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + if (itemstack1.isEmpty() || (ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) { return true; } } diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java index 5eac3b1b..e4c278b2 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java @@ -66,7 +66,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI private List> compartments; public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); + super(PlayerManager.getHandle(bukkitPlayer)); this.inventory = new CraftInventory(this); this.playerOnline = online; this.player = super.player; @@ -84,7 +84,7 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { } Player offlinePlayer = this.player; - Player onlinePlayer = PlayerDataManager.getHandle(player); + Player onlinePlayer = PlayerManager.getHandle(player); onlinePlayer.getInventory().transaction.addAll(this.transaction); // Set owner to new player. diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 666318e0..5e8673a7 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -30,8 +30,8 @@ OpenInvAdapter1_20_R4 - 21 - 21 + 21 + 21 1.20.6-R0.1-SNAPSHOT @@ -51,10 +51,9 @@ openinvapi com.lishid - provided - openinvplugincore + openinvcommon com.lishid @@ -65,9 +64,6 @@ - - maven-shade-plugin - maven-compiler-plugin diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java index 7731837d..f04de59b 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java @@ -16,9 +16,9 @@ package com.lishid.openinv.internal.v1_20_R4; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.util.ReflectionHelper; +import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -50,15 +50,18 @@ public class AnySilentContainer implements IAnySilentContainer { + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; private @Nullable Field serverPlayerGameModeGameType; - public AnySilentContainer() { + public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; try { try { this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); this.serverPlayerGameModeGameType.setAccessible(true); } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); @@ -66,7 +69,6 @@ public AnySilentContainer() { this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); } } catch (SecurityException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to directly write player game mode! SilentContainer will fail."); logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); } @@ -85,7 +87,7 @@ public boolean activateContainer( return true; } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); final net.minecraft.world.level.Level level = player.level(); final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); @@ -100,10 +102,10 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + MenuType containers = PlayerManager.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable(("container.enderchest")))); + }, Component.translatable("container.enderchest"))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } @@ -121,7 +123,7 @@ public boolean activateContainer( menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); if (menuProvider == null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } @@ -153,7 +155,7 @@ public boolean activateContainer( if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } } @@ -171,7 +173,7 @@ public void deactivateContainer(@NotNull final Player bukkitPlayer) { return; } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. // Regular game mode set calls GameModeChangeEvent and is cancellable. @@ -199,7 +201,6 @@ private void forceGameType(final ServerPlayer player, final GameType gameMode) { this.serverPlayerGameModeGameType.setAccessible(true); this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); } catch (IllegalArgumentException | IllegalAccessException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); } } diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java new file mode 100644 index 00000000..ff599644 --- /dev/null +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java @@ -0,0 +1,64 @@ +package com.lishid.openinv.internal.v1_20_R4; + +import com.lishid.openinv.internal.Accessor; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.util.lang.LanguageManager; +import net.minecraft.world.Container; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Logger; + +public class InternalAccessor implements Accessor { + + private final @NotNull PlayerManager manager; + private final @NotNull AnySilentContainer anySilentContainer; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + manager = new PlayerManager(logger, lang); + anySilentContainer = new AnySilentContainer(logger, lang); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { + return anySilentContainer; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new SpecialPlayerInventory(player, player.isOnline()); + } + + @Override + public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { + return new SpecialEnderChest(player, player.isOnline()); + } + + @Override + public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { + if (!(bukkitInventory instanceof CraftInventory craftInventory)) { + return null; + } + Container container = craftInventory.getInventory(); + if (clazz.isInstance(container)) { + return clazz.cast(container); + } + return null; + } + + @Override + public void reload(@NotNull ConfigurationSection config) {} + +} diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java index be7fbb47..9223fa19 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java @@ -16,6 +16,7 @@ package com.lishid.openinv.internal.v1_20_R4; +import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; @@ -80,17 +81,24 @@ public class OpenPlayer extends CraftPlayer { "Brain" ); - public OpenPlayer(CraftServer server, ServerPlayer entity) { + private final PlayerManager manager; + + OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { super(server, entity); + this.manager = manager; } @Override public void loadData() { - PlayerDataManager.loadData(getHandle()); + manager.loadData(getHandle()); } @Override public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + ServerPlayer player = this.getHandle(); // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try { diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java similarity index 93% rename from internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java rename to internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java index 430fa975..2acab9a3 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerDataManager.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java @@ -16,11 +16,10 @@ package com.lishid.openinv.internal.v1_20_R4; -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; +import com.lishid.openinv.util.lang.LanguageManager; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -48,7 +47,6 @@ import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,7 +54,7 @@ import java.util.UUID; import java.util.logging.Logger; -public class PlayerDataManager implements IPlayerDataManager { +public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { private static boolean paper; @@ -69,13 +67,16 @@ public class PlayerDataManager implements IPlayerDataManager { } } + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; private @Nullable Field bukkitEntity; - public PlayerDataManager() { + public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; try { bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); } catch (NoSuchFieldException e) { - Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); bukkitEntity = null; @@ -156,7 +157,7 @@ public PlayerDataManager() { try { injectPlayer(entity); } catch (IllegalAccessException e) { - JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + logger.log( java.util.logging.Level.WARNING, e, () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); @@ -165,7 +166,7 @@ public PlayerDataManager() { return entity; } - static boolean loadData(@NotNull ServerPlayer player) { + boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); @@ -189,7 +190,7 @@ static boolean loadData(@NotNull ServerPlayer player) { return true; } - private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { // See PlayerList#placeNewPlayer World bukkitWorld; if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { @@ -201,7 +202,7 @@ private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTa } else { // Vanilla player data. DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning) + .resultOrPartial(logger::warning) .map(player.server::getLevel) // If ServerLevel exists, set, otherwise move to spawn. .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); @@ -221,7 +222,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { bukkitEntity.setAccessible(true); - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); } @Override @@ -234,7 +235,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { - JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + logger.log( java.util.logging.Level.WARNING, e, () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); @@ -257,7 +258,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return player.openInventory(inventory.getBukkitInventory()); } - InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(player, inventory)); + InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(lang, player, inventory)); AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override public MenuType getType() { diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java index 712a68af..d38ac428 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java @@ -50,9 +50,9 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe private boolean playerOnline; public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); + super(PlayerManager.getHandle(player)); this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); + this.owner = PlayerManager.getHandle(player); this.playerOnline = online; this.items = this.owner.getEnderChestInventory().items; } @@ -74,7 +74,7 @@ public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { } ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + ServerPlayer onlinePlayer = PlayerManager.getHandle(player); // Set owner to new player. this.owner = onlinePlayer; @@ -199,7 +199,7 @@ public ItemStack addItem(ItemStack itemstack) { @Override public boolean canAddItem(ItemStack itemstack) { for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + if (itemstack1.isEmpty() || (ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) { return true; } } diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java index a447f63d..155f4224 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java @@ -63,7 +63,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI private List> compartments; public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerDataManager.getHandle(bukkitPlayer)); + super(PlayerManager.getHandle(bukkitPlayer)); this.inventory = new CraftInventory(this); this.playerOnline = online; this.player = super.player; @@ -81,7 +81,7 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { } Player offlinePlayer = this.player; - Player onlinePlayer = PlayerDataManager.getHandle(player); + Player onlinePlayer = PlayerManager.getHandle(player); onlinePlayer.getInventory().transaction.addAll(this.transaction); // Set owner to new player. diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 2d2b6b3e..b3aa9c15 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -30,8 +30,8 @@ OpenInvAdapter1_21_R1 - 21 - 21 + 21 + 21 1.21-R0.1-SNAPSHOT @@ -51,10 +51,9 @@ openinvapi com.lishid - provided - openinvplugincore + openinvcommon com.lishid @@ -65,9 +64,6 @@ - - maven-shade-plugin - maven-compiler-plugin diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java index d03f25aa..15faeff8 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java @@ -16,9 +16,9 @@ package com.lishid.openinv.internal.v1_21_R1; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.util.ReflectionHelper; +import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -50,15 +50,18 @@ public class AnySilentContainer implements IAnySilentContainer { + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; private @Nullable Field serverPlayerGameModeGameType; - public AnySilentContainer() { + public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; try { try { this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); this.serverPlayerGameModeGameType.setAccessible(true); } catch (NoSuchFieldException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); @@ -66,7 +69,6 @@ public AnySilentContainer() { this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); } } catch (SecurityException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to directly write player game mode! SilentContainer will fail."); logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); } @@ -85,7 +87,7 @@ public boolean activateContainer( return true; } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); final net.minecraft.world.level.Level level = player.level(); final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); @@ -100,10 +102,10 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerDataManager.getContainers(enderChest.getContainerSize()); + MenuType containers = PlayerManager.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable(("container.enderchest")))); + }, Component.translatable("container.enderchest"))); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } @@ -121,7 +123,7 @@ public boolean activateContainer( menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); if (menuProvider == null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } @@ -153,7 +155,7 @@ public boolean activateContainer( if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { if (lootable.lootTable != null) { - OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } } @@ -171,7 +173,7 @@ public void deactivateContainer(@NotNull final Player bukkitPlayer) { return; } - ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer); + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); // Force game mode change without informing plugins or players. // Regular game mode set calls GameModeChangeEvent and is cancellable. @@ -199,7 +201,6 @@ private void forceGameType(final ServerPlayer player, final GameType gameMode) { this.serverPlayerGameModeGameType.setAccessible(true); this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); } catch (IllegalArgumentException | IllegalAccessException e) { - Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger(); logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java new file mode 100644 index 00000000..59f3481a --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java @@ -0,0 +1,78 @@ +package com.lishid.openinv.internal.v1_21_R1; + +import com.lishid.openinv.internal.Accessor; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.util.lang.LanguageManager; +import net.minecraft.world.Container; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InternalAccessor implements Accessor { + + private final @NotNull Logger logger; + private final @NotNull PlayerManager manager; + private final @NotNull AnySilentContainer anySilentContainer; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + manager = new PlayerManager(logger, lang); + anySilentContainer = new AnySilentContainer(logger, lang); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { + return anySilentContainer; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new OpenInventory(player); + } + + @Override + public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { + return new SpecialEnderChest(player, player.isOnline()); + } + + @Override + public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { + if (!(bukkitInventory instanceof CraftInventory craftInventory)) { + return null; + } + Container container = craftInventory.getInventory(); + if (clazz.isInstance(container)) { + return clazz.cast(container); + } + return null; + } + + @Override + public void reload(@NotNull ConfigurationSection config) { + ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); + if (placeholders != null) { + try { + Placeholders.load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java index 6a10ada3..ac07285d 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java @@ -16,6 +16,7 @@ package com.lishid.openinv.internal.v1_21_R1; +import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; @@ -79,17 +80,24 @@ public class OpenPlayer extends CraftPlayer { "Brain" ); - public OpenPlayer(CraftServer server, ServerPlayer entity) { + private final PlayerManager manager; + + OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { super(server, entity); + this.manager = manager; } @Override public void loadData() { - PlayerDataManager.loadData(getHandle()); + manager.loadData(getHandle()); } @Override public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + ServerPlayer player = this.getHandle(); // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java index 4660d80a..2c81a192 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerDataManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java @@ -16,13 +16,11 @@ package com.lishid.openinv.internal.v1_21_R1; -import com.github.jikoo.planarwrappers.function.TriFunction; -import com.lishid.openinv.OpenInv; -import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.InventoryViewTitle; import com.lishid.openinv.internal.OpenInventoryView; import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; +import com.lishid.openinv.util.lang.LanguageManager; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -39,6 +37,7 @@ import net.minecraft.world.inventory.MenuType; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; +import org.apache.commons.lang3.function.TriFunction; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; @@ -50,7 +49,6 @@ import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -58,7 +56,7 @@ import java.util.UUID; import java.util.logging.Logger; -public class PlayerDataManager implements IPlayerDataManager { +public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { private static boolean paper; private static TriFunction viewProvider; @@ -78,13 +76,16 @@ public class PlayerDataManager implements IPlayerDataManager { } } + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; private @Nullable Field bukkitEntity; - public PlayerDataManager() { + public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; try { bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); } catch (NoSuchFieldException e) { - Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger(); logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); bukkitEntity = null; @@ -165,7 +166,7 @@ public PlayerDataManager() { try { injectPlayer(entity); } catch (IllegalAccessException e) { - JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + logger.log( java.util.logging.Level.WARNING, e, () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); @@ -174,7 +175,7 @@ public PlayerDataManager() { return entity; } - static boolean loadData(@NotNull ServerPlayer player) { + boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); @@ -198,7 +199,7 @@ static boolean loadData(@NotNull ServerPlayer player) { return true; } - private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { // See PlayerList#placeNewPlayer World bukkitWorld; if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { @@ -210,7 +211,7 @@ private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTa } else { // Vanilla player data. DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning) + .resultOrPartial(logger::warning) .map(player.server::getLevel) // If ServerLevel exists, set, otherwise move to spawn. .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); @@ -230,7 +231,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { bukkitEntity.setAccessible(true); - bukkitEntity.set(player, new OpenPlayer(player.server.server, player)); + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); } @Override @@ -243,7 +244,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { injectPlayer(nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { - JavaPlugin.getPlugin(OpenInv.class).getLogger().log( + logger.log( java.util.logging.Level.WARNING, e, () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); @@ -292,7 +293,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return bukkitPlayer.openInventory(inventory.getBukkitInventory()); } - String originalTitle = viewTitle.getTitle(bukkitPlayer, inventory); + String originalTitle = viewTitle.getTitle(lang, bukkitPlayer, inventory); InventoryView view = viewProvider.apply(bukkitPlayer, inventory, originalTitle); Component title = Component.literal(originalTitle); AbstractContainerMenu container = new CraftContainer(view, player, player.nextContainerCounter()) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java index 512f2d95..a7d74c49 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java @@ -50,9 +50,9 @@ public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpe private boolean playerOnline; public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerDataManager.getHandle(player)); + super(PlayerManager.getHandle(player)); this.inventory = new CraftInventory(this); - this.owner = PlayerDataManager.getHandle(player); + this.owner = PlayerManager.getHandle(player); this.playerOnline = online; this.items = this.owner.getEnderChestInventory().items; } @@ -74,7 +74,7 @@ public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { } ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player); + ServerPlayer onlinePlayer = PlayerManager.getHandle(player); // Set owner to new player. this.owner = onlinePlayer; @@ -199,7 +199,7 @@ public ItemStack addItem(ItemStack itemstack) { @Override public boolean canAddItem(ItemStack itemstack) { for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) { + if (itemstack1.isEmpty() || (ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) { return true; } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java index f3f241cf..a071e123 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java @@ -102,7 +102,7 @@ private SlotCrafting(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { - return isAvailable() ? items.get(ContainerSlotCrafting.this.index) : PlaceholderManager.survivalOnly(holder); + return isAvailable() ? items.get(ContainerSlotCrafting.this.index) : Placeholders.survivalOnly(holder); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java index 6b25d985..3bf9fb43 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java @@ -31,7 +31,7 @@ public Slot asMenuSlot(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { if (!ContainerSlotCrafting.isAvailable(holder)) { - return PlaceholderManager.survivalOnly(holder); + return Placeholders.survivalOnly(holder); } InventoryMenu inventoryMenu = holder.inventoryMenu; return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java index 65a9e33c..33b83df6 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java @@ -85,10 +85,10 @@ private SlotCursor(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { if (!isAvailable()) { - return PlaceholderManager.survivalOnly(holder); + return Placeholders.survivalOnly(holder); } ItemStack carried = holder.containerMenu.getCarried(); - return carried.isEmpty() ? PlaceholderManager.cursor : carried; + return carried.isEmpty() ? Placeholders.cursor : carried; } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java index 355ccd1f..e7fb80c5 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java @@ -63,8 +63,8 @@ private SlotDrop(Container container, int index, int x, int y) { @Override ItemStack getOrDefault() { return holder.connection != null && !holder.connection.isDisconnected() - ? PlaceholderManager.drop - : PlaceholderManager.blockedOffline; + ? Placeholders.drop + : Placeholders.blockedOffline; } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java index 68ed1eb2..99f8b39d 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java @@ -19,11 +19,11 @@ class ContainerSlotEquipment extends ContainerSlotList { ContainerSlotEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { super(holder, index, InventoryType.SlotType.ARMOR); placeholder = switch (equipmentSlot) { - case HEAD -> PlaceholderManager.emptyHelmet; - case CHEST -> PlaceholderManager.emptyChestplate; - case LEGS -> PlaceholderManager.emptyLeggings; - case FEET -> PlaceholderManager.emptyBoots; - default -> PlaceholderManager.emptyOffHand; + case HEAD -> Placeholders.emptyHelmet; + case CHEST -> Placeholders.emptyChestplate; + case LEGS -> Placeholders.emptyLeggings; + case FEET -> Placeholders.emptyBoots; + default -> Placeholders.emptyOffHand; }; this.equipmentSlot = equipmentSlot; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java index f27579c8..48b57709 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java @@ -64,11 +64,13 @@ static class SlotUninteractable extends MenuSlotPlaceholder { @Override ItemStack getOrDefault() { - return PlaceholderManager.notSlot; + return Placeholders.notSlot; } + @Override public void onQuickCraft(ItemStack var0, ItemStack var1) {} + @Override public void onTake(Player var0, ItemStack var1) {} @Override @@ -76,6 +78,7 @@ public boolean mayPlace(ItemStack var0) { return false; } + @Override public ItemStack getItem() { return ItemStack.EMPTY; } @@ -85,22 +88,29 @@ public boolean hasItem() { return false; } + @Override public void setByPlayer(ItemStack newStack) {} + @Override public void setByPlayer(ItemStack newStack, ItemStack oldStack) {} + @Override public void set(ItemStack var0) {} + @Override public void setChanged() {} + @Override public int getMaxStackSize() { return 0; } + @Override public int getMaxStackSize(ItemStack itemStack) { return 0; } + @Override public ItemStack remove(int amount) { return ItemStack.EMPTY; } @@ -110,22 +120,27 @@ public boolean mayPickup(Player var0) { return false; } + @Override public boolean isActive() { return false; } + @Override public Optional tryRemove(int var0, int var1, Player var2) { return Optional.empty(); } + @Override public ItemStack safeTake(int var0, int var1, Player var2) { return ItemStack.EMPTY; } + @Override public ItemStack safeInsert(ItemStack itemStack) { return itemStack; } + @Override public ItemStack safeInsert(ItemStack itemStack, int amount) { return itemStack; } @@ -135,10 +150,12 @@ public boolean allowModification(Player var0) { return false; } + @Override public int getContainerSlot() { return this.slot; } + @Override public boolean isHighlightable() { return false; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java index ab0336d5..5bdea862 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.v1_21_R1.PlayerDataManager; +import com.lishid.openinv.internal.v1_21_R1.PlayerManager; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -38,7 +38,7 @@ public class OpenInventory implements Container, Nameable, MenuProvider, ISpecia public List transaction = new ArrayList<>(); public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { - owner = PlayerDataManager.getHandle(bukkitPlayer); + owner = PlayerManager.getHandle(bukkitPlayer); // Get total size, rounding up to nearest 9 for client compatibility. int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; @@ -175,7 +175,7 @@ public Slot asMenuSlot(Container container, int index, int x, int y) { return new SlotUninteractable(container, index, x, y) { @Override ItemStack getOrDefault() { - return PlaceholderManager.craftingOutput; + return Placeholders.craftingOutput; } }; } @@ -201,7 +201,7 @@ public ServerPlayer getOwnerHandle() { public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { MutableComponent component = Component.empty(); // Prefix for use with custom bitmap image fonts. - if (viewer == owner) { + if (owner.equals(viewer)) { component.append( Component.translatableWithFallback("openinv.container.inventory.self", "") .withStyle(style -> style @@ -230,7 +230,7 @@ public ServerPlayer getOwnerHandle() { @Override public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - ServerPlayer newOwner = PlayerDataManager.getHandle(player); + ServerPlayer newOwner = PlayerManager.getHandle(player); // Only transfer regular inventory - crafting and cursor slots are transient. newOwner.getInventory().replaceWith(owner.getInventory()); owner = newOwner; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java index 2cad157e..a0eef7ff 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -190,6 +190,7 @@ public InventoryType.SlotType getSlotType(int slot) { private ItemStack remoteCarried = ItemStack.EMPTY; private boolean suppressRemoteUpdates; + @Override protected Slot addSlot(Slot slot) { slot.index = this.slots.size(); this.slots.add(slot); @@ -198,18 +199,21 @@ protected Slot addSlot(Slot slot) { return slot; } + @Override protected DataSlot addDataSlot(DataSlot dataSlot) { this.dataSlots.add(dataSlot); this.remoteDataSlots.add(0); return dataSlot; } + @Override protected void addDataSlots(ContainerData containerData) { for (int i = 0; i < containerData.getCount(); i++) { this.addDataSlot(DataSlot.forContainer(containerData, i)); } } + @Override public void addSlotListener(ContainerListener containerListener) { if (!this.containerListeners.contains(containerListener)) { this.containerListeners.add(containerListener); @@ -217,11 +221,13 @@ public void addSlotListener(ContainerListener containerListener) { } } + @Override public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { this.synchronizer = containerSynchronizer; this.sendAllDataToRemote(); } + @Override public void sendAllDataToRemote() { for (int index = 0; index < slots.size(); ++index) { Slot slot = slots.get(index); @@ -239,6 +245,7 @@ public void sendAllDataToRemote() { } } + @Override public void broadcastCarriedItem() { this.remoteCarried = this.getCarried().copy(); if (this.synchronizer != null) { @@ -246,10 +253,12 @@ public void broadcastCarriedItem() { } } + @Override public void removeSlotListener(ContainerListener containerListener) { this.containerListeners.remove(containerListener); } + @Override public void broadcastChanges() { for (int index = 0; index < this.slots.size(); ++index) { Slot slot = this.slots.get(index); @@ -272,6 +281,7 @@ public void broadcastChanges() { } } + @Override public void broadcastFullState() { for (int index = 0; index < this.slots.size(); ++index) { ItemStack itemstack = this.slots.get(index).getItem(); @@ -340,14 +350,17 @@ private void synchronizeCarriedToRemote() { } } + @Override public void setRemoteCarried(ItemStack itemstack) { this.remoteCarried = itemstack.copy(); } + @Override public void suppressRemoteUpdates() { this.suppressRemoteUpdates = true; } + @Override public void resumeRemoteUpdates() { this.suppressRemoteUpdates = false; } @@ -429,6 +442,7 @@ public boolean stillValid(Player player) { * @param topDown whether to start at the top of the range or bottom * @return whether the stack was modified as a result of being quick-moved */ + @Override protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { boolean modified = false; boolean stackable = itemStack.isStackable(); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java index 9a594460..48b91ba7 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/PlaceholderManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java @@ -1,6 +1,5 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import com.lishid.openinv.internal.PlaceholderParser; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; @@ -28,7 +27,7 @@ import java.util.List; import java.util.Optional; -public class PlaceholderManager implements PlaceholderParser { +public final class Placeholders { private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); @@ -54,8 +53,7 @@ public class PlaceholderManager implements PlaceholderParser { } } - @Override - public void load(@NotNull ConfigurationSection section) throws Exception { + public static void load(@NotNull ConfigurationSection section) throws Exception { craftingOutput = parse(section, "crafting-output", craftingOutput); cursor = parse(section, "cursor", cursor); drop = parse(section, "drop", drop); @@ -70,7 +68,7 @@ public void load(@NotNull ConfigurationSection section) throws Exception { BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); } - private @NotNull ItemStack parse( + private static @NotNull ItemStack parse( @NotNull ConfigurationSection section, @NotNull String path, @NotNull ItemStack defaultStack) throws Exception { @@ -178,4 +176,8 @@ private static ItemStack defaultBlockedOffline() { return itemStack; } + private Placeholders() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + } diff --git a/plugin/pom.xml b/plugin/pom.xml index 2a7b9df6..381a5018 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -24,29 +24,52 @@ 5.0.0-SNAPSHOT - openinvplugincore + openinvplugin OpenInvPlugin + + spigot-api + org.spigotmc + + + planarwrappers + com.github.jikoo + openinvapi com.lishid - annotations - org.jetbrains + openinvcommon + com.lishid - spigot-api - org.spigotmc + com.lishid + openinvadapter1_21_R1 + 5.0.0-SNAPSHOT + compile - planarwrappers - com.github.jikoo + com.lishid + openinvadapter1_20_R4 + 5.0.0-SNAPSHOT + compile + + + com.lishid + openinvadapter1_20_R3 + 5.0.0-SNAPSHOT + compile + + + annotations + org.jetbrains + OpenInv src/main/resources @@ -86,6 +109,29 @@ maven-compiler-plugin + + maven-resources-plugin + + + copy-final-jar + package + + copy-resources + + + ${project.parent.build.directory} + + + ${project.build.directory} + + ${project.build.finalName}.jar + + + + + + + diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index 0e425fb1..390277b2 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -16,6 +16,7 @@ package com.lishid.openinv; +import com.google.errorprone.annotations.Keep; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; @@ -44,6 +45,7 @@ */ record InventoryListener(OpenInv plugin) implements Listener { + @Keep @EventHandler private void onInventoryClose(@NotNull final InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player player)) { @@ -68,11 +70,13 @@ private void onInventoryClose(@NotNull final InventoryCloseEvent event) { } } + @Keep @EventHandler(priority = EventPriority.LOWEST) private void onInventoryClick(@NotNull final InventoryClickEvent event) { handleInventoryInteract(event); } + @Keep @EventHandler(priority = EventPriority.LOWEST) private void onInventoryDrag(@NotNull final InventoryDragEvent event) { handleInventoryInteract(event); diff --git a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java b/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java index 9e49ca54..a92c92e6 100644 --- a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java @@ -1,5 +1,6 @@ package com.lishid.openinv; +import com.google.errorprone.annotations.Keep; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; @@ -30,6 +31,7 @@ record LegacyInventoryListener(OpenInv plugin) implements Listener { + @Keep @EventHandler private void onInventoryClose(@NotNull final InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player player)) { @@ -54,6 +56,7 @@ private void onInventoryClose(@NotNull final InventoryCloseEvent event) { } } + @Keep @EventHandler(priority = EventPriority.LOWEST) private void onInventoryClick(@NotNull final InventoryClickEvent event) { if (handleInventoryInteract(event)) { @@ -79,11 +82,10 @@ private void onInventoryClick(@NotNull final InventoryClickEvent event) { event.setCurrentItem(null); // Complete add action in same tick after event completion. - this.plugin.getServer().getScheduler().runTask(this.plugin, () -> { - player.getInventory().addItem(clone); - }); + this.plugin.getServer().getScheduler().runTask(this.plugin, () -> player.getInventory().addItem(clone)); } + @Keep @EventHandler(priority = EventPriority.LOWEST) private void onInventoryDrag(@NotNull final InventoryDragEvent event) { if (handleInventoryInteract(event)) { diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index a945e8fc..f6c9f160 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -25,18 +25,16 @@ import com.lishid.openinv.commands.SearchContainerCommand; import com.lishid.openinv.commands.SearchEnchantCommand; import com.lishid.openinv.commands.SearchInvCommand; -import com.lishid.openinv.event.OpenPlayerSaveEvent; +import com.lishid.openinv.event.OpenEvents; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.ConfigUpdater; +import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.StringMetric; import com.lishid.openinv.util.lang.LanguageManager; -import com.lishid.openinv.util.lang.Replacement; -import net.md_5.bungee.api.ChatMessageType; -import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; @@ -87,7 +85,7 @@ public void reloadConfig() { super.reloadConfig(); this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; if (this.accessor != null && this.accessor.isSupported()) { - this.accessor.reload(); + this.accessor.reload(this.getConfig()); } } @@ -130,12 +128,8 @@ public void onEnable() { // Save default configuration if not present. this.saveDefaultConfig(); - // Get plugin manager - PluginManager pm = this.getServer().getPluginManager(); - - this.accessor = new InternalAccessor(this); - this.languageManager = new LanguageManager(this, "en_us"); + this.accessor = new InternalAccessor(getLogger(), languageManager); try { Class.forName("org.bukkit.entity.Player$Spigot"); @@ -151,20 +145,22 @@ public void onEnable() { // Update existing configuration. May require internal access. new ConfigUpdater(this).checkForUpdates(); + // Get plugin manager + PluginManager pm = this.getServer().getPluginManager(); // Register listeners if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { pm.registerEvents(new LegacyInventoryListener(this), this); } else { - pm.registerEvents(new PlayerListener(this), this); + pm.registerEvents(new PlayerListener(this, languageManager), this); } pm.registerEvents(new InventoryListener(this), this); // Register commands to their executors - this.setCommandExecutor(new OpenInvCommand(this), "openinv", "openender"); - this.setCommandExecutor(new SearchContainerCommand(this), "searchcontainer"); - this.setCommandExecutor(new SearchInvCommand(this), "searchinv", "searchender"); - this.setCommandExecutor(new SearchEnchantCommand(this), "searchenchant"); - this.setCommandExecutor(new ContainerSettingCommand(this), "silentcontainer", "anycontainer"); + this.setCommandExecutor(new OpenInvCommand(this, languageManager), "openinv", "openender"); + this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); + this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); + this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); + this.setCommandExecutor(new ContainerSettingCommand(this, languageManager), "silentcontainer", "anycontainer"); } else { this.sendVersionError(this.getLogger()::warning); @@ -182,9 +178,15 @@ private void setCommandExecutor(@NotNull CommandExecutor executor, String @NotNu } private void sendVersionError(@NotNull Consumer messageMethod) { - if (!this.accessor.isSupported()) { - messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported."); - messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink()); + if (!accessor.isSupported()) { + messageMethod.accept("Your server version (" + accessor.getVersion() + ") is not supported."); + messageMethod.accept("Please download the correct version of OpenInv here: " + accessor.getReleasesLink()); + + // We check this property late so users can use jars that were remapped by Paper already. + if (Boolean.getBoolean("paper.disable-plugin-rewriting")) { + messageMethod.accept("OpenInv uses Spigot-mapped internals, but you have disabled plugin rewriting in Paper!"); + messageMethod.accept("Please set system property 'paper.disable-plugin-rewriting' to false."); + } } if (!isSpigot) { messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread"); @@ -242,22 +244,20 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final } @Override - public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) - throws InstantiationException { + public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) { UUID key = player.getUniqueId(); if (this.enderChests.containsKey(key)) { return this.enderChests.get(key); } - ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online); + ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player); this.enderChests.put(key, inv); return inv; } @Override - public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) - throws InstantiationException { + public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) { UUID key = player.getUniqueId(); if (this.inventories.containsKey(key)) { @@ -436,61 +436,6 @@ private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInve && !Objects.equals(viewer.getWorld(), player.getWorld())); } - public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) { - return this.languageManager.getValue(key, getLocale(sender)); - } - - public @Nullable String getLocalizedMessage( - @NotNull CommandSender sender, - @NotNull String key, - Replacement @NotNull ... replacements) { - return this.languageManager.getValue(key, getLocale(sender), replacements); - } - - private @NotNull String getLocale(@NotNull CommandSender sender) { - if (sender instanceof Player) { - return ((Player) sender).getLocale(); - } else { - return this.getConfig().getString("settings.locale", "en_us"); - } - } - - public void sendMessage(@NotNull CommandSender sender, @NotNull String key) { - String message = getLocalizedMessage(sender, key); - - if (message != null && !message.isEmpty()) { - sender.sendMessage(message); - } - } - - public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) { - String message = getLocalizedMessage(sender, key, replacements); - - if (message != null && !message.isEmpty()) { - sender.sendMessage(message); - } - } - - public void sendSystemMessage(@NotNull Player player, @NotNull String key) { - String message = getLocalizedMessage(player, key); - - if (message == null) { - return; - } - - int newline = message.indexOf('\n'); - if (newline != -1) { - // No newlines in action bar chat. - message = message.substring(0, newline); - } - - if (message.isEmpty()) { - return; - } - - player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message)); - } - /** * Method for handling a Player going offline. * @@ -562,10 +507,7 @@ void handleCloseInventory(@NotNull ISpecialInventory inventory) { return; } - OpenPlayerSaveEvent event = new OpenPlayerSaveEvent(player, current); - getServer().getPluginManager().callEvent(event); - - if (!event.isCancelled()) { + if (!OpenEvents.saveCancelled(player, current)) { this.accessor.getPlayerDataManager().inject(player).saveData(); } }); @@ -638,8 +580,8 @@ private void setPlayerOnline( inventory, viewer -> !Permissions.ACCESS_ONLINE.hasPermission(viewer) - || !Permissions.ACCESS_CROSSWORLD.hasPermission(viewer) - && !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld())); + || (!Permissions.ACCESS_CROSSWORLD.hasPermission(viewer) + && !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld()))); } static void ejectViewers(@NotNull ISpecialInventory inventory, @NotNull Predicate<@NotNull HumanEntity> predicate) { diff --git a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java index 64ad0592..ddf868ad 100644 --- a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java @@ -16,7 +16,9 @@ package com.lishid.openinv; +import com.google.errorprone.annotations.Keep; import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.lang.LanguageManager; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; @@ -29,23 +31,35 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.jetbrains.annotations.NotNull; -record PlayerListener(OpenInv plugin) implements Listener { +class PlayerListener implements Listener { + private final @NotNull OpenInv plugin; + private final @NotNull LanguageManager lang; + + PlayerListener(@NotNull OpenInv plugin, @NotNull LanguageManager lang) { + this.plugin = plugin; + this.lang = lang; + } + + @Keep @EventHandler(priority = EventPriority.LOWEST) private void onPlayerJoin(@NotNull PlayerJoinEvent event) { plugin.setPlayerOnline(event.getPlayer()); } + @Keep @EventHandler(priority = EventPriority.MONITOR) private void onPlayerQuit(@NotNull PlayerQuitEvent event) { plugin.setPlayerOffline(event.getPlayer()); } + @Keep @EventHandler private void onWorldChange(@NotNull PlayerChangedWorldEvent event) { plugin.changeWorld(event.getPlayer()); } + @Keep @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) private void onPlayerInteract(@NotNull PlayerInteractEvent event) { @@ -74,11 +88,11 @@ private void onPlayerInteract(@NotNull PlayerInteractEvent event) { if (any || silent) { if (plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) { if (silent && needsAny) { - plugin.sendSystemMessage(player, "messages.info.containerBlockedSilent"); + lang.sendSystemMessage(player, "messages.info.containerBlockedSilent"); } else if (needsAny) { - plugin.sendSystemMessage(player, "messages.info.containerBlocked"); + lang.sendSystemMessage(player, "messages.info.containerBlocked"); } else if (silent) { - plugin.sendSystemMessage(player, "messages.info.containerSilent"); + lang.sendSystemMessage(player, "messages.info.containerSilent"); } } event.setCancelled(true); diff --git a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java index c8dd3c7d..02e5b759 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java @@ -16,13 +16,10 @@ package com.lishid.openinv.commands; -import com.lishid.openinv.OpenInv; +import com.lishid.openinv.IOpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Predicate; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -30,18 +27,26 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.function.BiConsumer; +import java.util.function.Predicate; + public class ContainerSettingCommand implements TabExecutor { - private final OpenInv plugin; + private final @NotNull IOpenInv plugin; + private final @NotNull LanguageManager lang; - public ContainerSettingCommand(final OpenInv plugin) { + public ContainerSettingCommand(@NotNull IOpenInv plugin, @NotNull LanguageManager lang) { this.plugin = plugin; + this.lang = lang; } @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!(sender instanceof Player player)) { - plugin.sendMessage(sender, "messages.error.consoleUnsupported"); + lang.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } @@ -50,7 +55,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command BiConsumer setSetting = any ? plugin::setAnyContainerStatus : plugin::setSilentContainerStatus; if (args.length > 0) { - args[0] = args[0].toLowerCase(); + args[0] = args[0].toLowerCase(Locale.ENGLISH); if (args[0].equals("on")) { setSetting.accept(player, true); @@ -65,12 +70,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command setSetting.accept(player, !getSetting.test(player)); } - String onOff = plugin.getLocalizedMessage(player, getSetting.test(player) ? "messages.info.on" : "messages.info.off"); + String onOff = lang.getLocalizedMessage(player, getSetting.test(player) ? "messages.info.on" : "messages.info.off"); if (onOff == null) { onOff = String.valueOf(getSetting.test(player)); } - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.settingState", new Replacement("%setting%", any ? "AnyContainer" : "SilentContainer"), diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index fba649e7..f082c959 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -20,6 +20,7 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; @@ -38,12 +39,14 @@ public class OpenInvCommand implements TabExecutor { - private final OpenInv plugin; + private final @NotNull OpenInv plugin; + private final @NotNull LanguageManager lang; private final HashMap openInvHistory = new HashMap<>(); private final HashMap openEnderHistory = new HashMap<>(); - public OpenInvCommand(final OpenInv plugin) { + public OpenInvCommand(@NotNull OpenInv plugin, @NotNull LanguageManager lang) { this.plugin = plugin; + this.lang = lang; } @Override @@ -56,7 +59,7 @@ public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Com } if (!(sender instanceof Player player)) { - plugin.sendMessage(sender, "messages.error.consoleUnsupported"); + lang.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } @@ -86,8 +89,8 @@ public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Com public void run() { final OfflinePlayer offlinePlayer = OpenInvCommand.this.plugin.matchPlayer(name); - if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline()) { - plugin.sendMessage(player, "messages.error.invalidPlayer"); + if (offlinePlayer == null || (!offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline())) { + lang.sendMessage(player, "messages.error.invalidPlayer"); return; } @@ -144,20 +147,20 @@ private void openInventory(final Player player, final OfflinePlayer target, bool // Try loading the player's data onlineTarget = this.plugin.loadPlayer(target); } else { - plugin.sendMessage(player, "messages.error.permissionPlayerOffline"); + lang.sendMessage(player, "messages.error.permissionPlayerOffline"); return; } } else { if (Permissions.ACCESS_ONLINE.hasPermission(player)) { onlineTarget = target.getPlayer(); } else { - plugin.sendMessage(player, "messages.error.permissionPlayerOnline"); + lang.sendMessage(player, "messages.error.permissionPlayerOnline"); return; } } if (onlineTarget == null) { - plugin.sendMessage(player, "messages.error.invalidPlayer"); + lang.sendMessage(player, "messages.error.invalidPlayer"); return; } @@ -165,14 +168,14 @@ private void openInventory(final Player player, final OfflinePlayer target, bool if (onlineTarget.equals(player)) { // Permission for opening own inventory. if (!(openinv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(player)) { - plugin.sendMessage(player, "messages.error.permissionOpenSelf"); + lang.sendMessage(player, "messages.error.permissionOpenSelf"); return; } } else { // Permission for opening others' inventories. if (!(openinv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(player)) { - plugin.sendMessage(player, "messages.error.permissionOpenOther"); + lang.sendMessage(player, "messages.error.permissionOpenOther"); return; } @@ -181,7 +184,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool String permission = "openinv.access.level." + level; if (onlineTarget.hasPermission(permission) && !player.hasPermission(permission)) { - plugin.sendMessage( + lang.sendMessage( player, "messages.error.permissionExempt", new Replacement("%target%", onlineTarget.getDisplayName())); @@ -192,7 +195,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool // Crossworld check if (!Permissions.ACCESS_CROSSWORLD.hasPermission(player) && !onlineTarget.getWorld().equals(player.getWorld())) { - plugin.sendMessage( + lang.sendMessage( player, "messages.error.permissionCrossWorld", new Replacement("%target%", onlineTarget.getDisplayName())); @@ -210,7 +213,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool try { inv = openinv ? this.plugin.getSpecialInventory(onlineTarget, online) : this.plugin.getSpecialEnderChest(onlineTarget, online); } catch (Exception e) { - plugin.sendMessage(player, "messages.error.commandException"); + lang.sendMessage(player, "messages.error.commandException"); plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); return; } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java index b524b2f9..fd61fc22 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java @@ -16,11 +16,9 @@ package com.lishid.openinv.commands; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; -import java.util.Collections; -import java.util.List; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; @@ -30,23 +28,30 @@ import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + /** * Command for searching containers in a radius of chunks. */ public class SearchContainerCommand implements TabExecutor { - private final OpenInv plugin; + private final @NotNull Plugin plugin; + private final @NotNull LanguageManager lang; - public SearchContainerCommand(OpenInv plugin) { + public SearchContainerCommand(@NotNull Plugin plugin, @NotNull LanguageManager lang) { this.plugin = plugin; + this.lang = lang; } @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!(sender instanceof Player senderPlayer)) { - plugin.sendMessage(sender, "messages.error.consoleUnsupported"); + lang.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } @@ -55,10 +60,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return false; } - Material material = Material.getMaterial(args[0].toUpperCase()); + Material material = Material.matchMaterial(args[0]); if (material == null) { - plugin.sendMessage( + lang.sendMessage( sender, "messages.error.invalidMaterial", new Replacement("%target%", args[0])); @@ -97,7 +102,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (!holder.getInventory().contains(material)) { continue; } - locations.append(holder.getInventory().getType().name().toLowerCase()).append(" (") + locations.append(holder.getInventory().getType().name().toLowerCase(Locale.ENGLISH)).append(" (") .append(tileEntity.getX()).append(',').append(tileEntity.getY()).append(',') .append(tileEntity.getZ()).append("), "); } @@ -108,14 +113,14 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (!locations.isEmpty()) { locations.delete(locations.length() - 2, locations.length()); } else { - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.container.noMatches", new Replacement("%target%", material.name())); return true; } - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.container.matches", new Replacement("%target%", material.name()), diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java index 8abaec9e..3e66acca 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java @@ -16,9 +16,10 @@ package com.lishid.openinv.commands; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Registry; @@ -35,6 +36,7 @@ import java.util.Collections; import java.util.List; +import java.util.Locale; /** * Command adding the ability to search online players' inventories for enchantments of a specific @@ -44,10 +46,10 @@ */ public class SearchEnchantCommand implements TabExecutor { - private final OpenInv plugin; + private final LanguageManager lang; - public SearchEnchantCommand(OpenInv plugin) { - this.plugin = plugin; + public SearchEnchantCommand(LanguageManager lang) { + this.lang = lang; } @Override @@ -63,9 +65,11 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command try { level = Integer.parseInt(argument); continue; - } catch (NumberFormatException ignored) {} + } catch (NumberFormatException ignored) { + // Not a level being specified. + } - argument = argument.toLowerCase(); + argument = argument.toLowerCase(Locale.ENGLISH); NamespacedKey key = NamespacedKey.fromString(argument); if (key == null) { continue; @@ -83,7 +87,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } StringBuilder players = new StringBuilder(); - for (Player player : plugin.getServer().getOnlinePlayers()) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { boolean flagInventory = containsEnchantment(player.getInventory(), enchant, level); boolean flagEnder = containsEnchantment(player.getEnderChest(), enchant, level); @@ -110,14 +114,14 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command // Matches found, delete trailing comma and space players.delete(players.length() - 2, players.length()); } else { - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.player.noMatches", new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level)); return true; } - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.player.matches", new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level), diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java index f40588c5..234d4ead 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java @@ -16,9 +16,10 @@ package com.lishid.openinv.commands; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -33,10 +34,10 @@ public class SearchInvCommand implements TabExecutor { - private final OpenInv plugin; + private final LanguageManager lang; - public SearchInvCommand(OpenInv plugin) { - this.plugin = plugin; + public SearchInvCommand(LanguageManager lang) { + this.lang = lang; } @Override @@ -45,11 +46,11 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Material material = null; if (args.length >= 1) { - material = Material.getMaterial(args[0].toUpperCase()); + material = Material.matchMaterial(args[0]); } if (material == null) { - plugin.sendMessage( + lang.sendMessage( sender, "messages.error.invalidMaterial", new Replacement("%target%", args.length > 0 ? args[0] : "null")); @@ -62,7 +63,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command try { count = Integer.parseInt(args[1]); } catch (NumberFormatException ex) { - plugin.sendMessage( + lang.sendMessage( sender, "messages.error.invalidNumber", new Replacement("%target%", args[1])); @@ -72,7 +73,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command StringBuilder players = new StringBuilder(); boolean searchInv = command.getName().equals("searchinv"); - for (Player player : plugin.getServer().getOnlinePlayers()) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest(); int total = 0; for (ItemStack itemStack : inventory.getContents()) { @@ -90,14 +91,14 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (!players.isEmpty()) { players.delete(players.length() - 2, players.length()); } else { - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.player.noMatches", new Replacement("%target%", material.name())); return true; } - plugin.sendMessage( + lang.sendMessage( sender, "messages.info.player.matches", new Replacement("%target%", material.name()), diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java similarity index 50% rename from plugin/src/main/java/com/lishid/openinv/InternalAccessor.java rename to plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 63c2a067..55cea7b0 100644 --- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -14,76 +14,44 @@ * along with this program. If not, see . */ -package com.lishid.openinv; +package com.lishid.openinv.util; import com.github.jikoo.planarwrappers.util.version.BukkitVersions; import com.github.jikoo.planarwrappers.util.version.Version; +import com.lishid.openinv.internal.Accessor; import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialEnderChest; -import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.PlaceholderParser; -import com.lishid.openinv.util.InventoryAccess; +import com.lishid.openinv.internal.PlayerManager; +import com.lishid.openinv.util.lang.LanguageManager; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Constructor; -import java.util.logging.Level; +import java.util.logging.Logger; -class InternalAccessor { +public class InternalAccessor { - private final @NotNull Plugin plugin; - private final @Nullable String versionPackage; - private boolean supported = false; - private IPlayerDataManager playerDataManager; - private IAnySilentContainer anySilentContainer; - private @Nullable PlaceholderParser placeholderParser; + private @Nullable Accessor internal; - InternalAccessor(@NotNull Plugin plugin) { - this.plugin = plugin; - this.versionPackage = getVersionPackage(); - checkSupported(); - } + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - private void checkSupported() { - if (versionPackage == null) { - return; - } try { - try { - Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".inventory.OpenInventory"); - this.placeholderParser = createObject(PlaceholderParser.class, "inventory.PlaceholderManager"); - } catch (ClassNotFoundException e) { - Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialPlayerInventory"); + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21))) { + internal = new com.lishid.openinv.internal.v1_21_R1.InternalAccessor(logger, lang); + } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) { + internal = new com.lishid.openinv.internal.v1_20_R3.InternalAccessor(logger, lang); + } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 6))) { + internal = new com.lishid.openinv.internal.v1_20_R4.InternalAccessor(logger, lang); } - Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".inventory.OpenInventory"); - Class.forName("com.lishid.openinv.internal." + this.versionPackage + ".SpecialEnderChest"); - this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager"); - this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer"); - this.supported = InventoryAccess.isUsable(); - } catch (Exception ignored) {} - } - - private @Nullable String getVersionPackage() { - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20, 4)) // Min supported version: 1.20.4 - || BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 not supported at all. - return null; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) { // 1.20.4 - return "v1_20_R3"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 6))) { // 1.20.6 - return "v1_20_R4"; - } - if (BukkitVersions.MINECRAFT.greaterThan(Version.of(1, 21))) { - return null; + if (internal != null) { + InventoryAccess.setProvider(internal::get); + } + } catch (NoClassDefFoundError | Exception e) { + internal = null; + InventoryAccess.setProvider(null); } - // 1.21 - return "v1_21_R1"; } public String getReleasesLink() { @@ -150,68 +118,9 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3 return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 shouldn't be used. - return "https://github.com/Jikoo/OpenInv/releases"; - } return "https://github.com/Jikoo/OpenInv/releases"; } - private @NotNull T createObject( - @NotNull Class assignableClass, - @NotNull String className, - @NotNull Object @NotNull ... params) - throws ClassCastException, ReflectiveOperationException { - // Fetch internal class if it exists. - Class internalClass = Class.forName("com.lishid.openinv.internal." + this.versionPackage + "." + className); - - // Quick return: no parameters, no need to fiddle about finding the correct constructor. - if (params.length == 0) { - return assignableClass.cast(internalClass.getConstructor().newInstance()); - } - - // Search constructors for one matching the given parameters - nextConstructor: for (Constructor constructor : internalClass.getConstructors()) { - Class[] requiredClasses = constructor.getParameterTypes(); - if (requiredClasses.length != params.length) { - continue; - } - for (int i = 0; i < params.length; ++i) { - if (!requiredClasses[i].isAssignableFrom(params[i].getClass())) { - continue nextConstructor; - } - } - return assignableClass.cast(constructor.newInstance(params)); - } - - StringBuilder builder = new StringBuilder("Found class ").append(internalClass.getName()) - .append(" but cannot find any matching constructors for ["); - for (Object object : params) { - builder.append(object.getClass().getName()).append(", "); - } - builder.delete(builder.length() - 2, builder.length()); - - String message = builder.append(']').toString(); - this.plugin.getLogger().warning(message); - - throw new NoSuchMethodException(message); - } - - private @NotNull T createSpecialInventory( - @NotNull Class assignableClass, - @NotNull String className, - @NotNull Object @NotNull ... params) throws InstantiationException { - if (!this.supported) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - try { - return this.createObject(assignableClass, className, params); - } catch (Exception original) { - InstantiationException exception = new InstantiationException(String.format("Unable to create a new %s", className)); - exception.initCause(original.fillInStackTrace()); - throw exception; - } - } - /** * Creates an instance of the IAnySilentContainer implementation for the current server version. * @@ -219,10 +128,10 @@ public String getReleasesLink() { * @throws IllegalStateException if server version is unsupported */ public @NotNull IAnySilentContainer getAnySilentContainer() { - if (!this.supported) { + if (internal == null) { throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } - return this.anySilentContainer; + return internal.getAnySilentContainer(); } /** @@ -231,28 +140,19 @@ public String getReleasesLink() { * @return the IPlayerDataManager * @throws IllegalStateException if server version is unsupported */ - public @NotNull IPlayerDataManager getPlayerDataManager() { - if (!this.supported) { + public @NotNull PlayerManager getPlayerDataManager() { + if (internal == null) { throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } - return this.playerDataManager; + return internal.getPlayerManager(); } /** * Reload internal features. */ - void reload() { - if (placeholderParser == null) { - return; - } - - ConfigurationSection placeholders = plugin.getConfig().getConfigurationSection("placeholders"); - if (placeholders != null) { - try { - placeholderParser.load(placeholders); - } catch (Exception e) { - plugin.getLogger().log(Level.WARNING, "Caught exception loading placeholder overrides!", e); - } + public void reload(ConfigurationSection config) { + if (internal != null) { + internal.reload(config); } } @@ -271,35 +171,33 @@ void reload() { * @return true if initialized for a supported server version */ public boolean isSupported() { - return this.supported; + return internal != null; } /** - * Creates an instance of the ISpecialEnderChest implementation for the given Player, or - * null if the current version is unsupported. + * Creates an instance of the ISpecialEnderChest implementation for the given Player. * * @param player the Player - * @param online true if the Player is online * @return the ISpecialEnderChest created - * @throws InstantiationException if the ISpecialEnderChest could not be instantiated */ - public ISpecialEnderChest newSpecialEnderChest(final Player player, final boolean online) throws InstantiationException { - return this.createSpecialInventory(ISpecialEnderChest.class, "SpecialEnderChest", player, online); + public ISpecialEnderChest newSpecialEnderChest(final Player player) { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); + } + return internal.createEnderChest(player); } /** - * Creates an instance of the ISpecialPlayerInventory implementation for the given Player.. + * Creates an instance of the ISpecialPlayerInventory implementation for the given Player. * * @param player the Player * @return the ISpecialPlayerInventory created - * @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated */ - public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player) throws InstantiationException { - try { - return this.createSpecialInventory(ISpecialPlayerInventory.class, "inventory.OpenInventory", player); - } catch (InstantiationException ignored) { - return this.createSpecialInventory(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, player.isOnline()); + public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player) { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } + return internal.createPlayerInventory(player); } } diff --git a/api/src/main/java/com/lishid/openinv/util/StringMetric.java b/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java similarity index 100% rename from api/src/main/java/com/lishid/openinv/util/StringMetric.java rename to plugin/src/main/java/com/lishid/openinv/util/StringMetric.java diff --git a/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java b/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java index 882274fe..f61be773 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java +++ b/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java @@ -16,20 +16,21 @@ package com.lishid.openinv.util; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.function.Function; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.util.StringUtil; /** * Utility class for common tab completions. */ -public class TabCompleter { +public final class TabCompleter { /** * Offer tab completions for whole numbers. @@ -67,7 +68,7 @@ public static List completeEnum(String argument, Class List completions = new ArrayList<>(); for (Enum enumConstant : enumClazz.getEnumConstants()) { - String name = enumConstant.name().toLowerCase(); + String name = enumConstant.name().toLowerCase(Locale.ENGLISH); if (name.startsWith(argument)) { completions.add(name); } @@ -133,7 +134,7 @@ public static List completeObject(String argument, Function completions = new ArrayList<>(); for (T option : options) { - String optionString = converter.apply(option).toLowerCase(); + String optionString = converter.apply(option).toLowerCase(Locale.ENGLISH); if (optionString.startsWith(argument)) { completions.add(optionString); } @@ -142,6 +143,8 @@ public static List completeObject(String argument, Functionopeninvparent OpenInv http://dev.bukkit.org/bukkit-plugins/openinv/ + + common + 5.0.0-SNAPSHOT pom UTF-8 - 17 - 17 + 17 17 unknown @@ -39,23 +41,21 @@ all api - plugin + common internal/v1_21_R1 internal/v1_20_R4 internal/v1_20_R3 - assembly + plugin - default + api-only true api - plugin - assembly @@ -89,23 +89,18 @@ openinvapi com.lishid compile - 5.0.0-SNAPSHOT + ${project.version} - openinvplugincore + openinvcommon com.lishid compile - 5.0.0-SNAPSHOT - - - com.lishid - openinvapi - - + ${project.version} planarwrappers com.github.jikoo + compile 3.2.3 @@ -126,39 +121,55 @@ maven-shade-plugin - - - - - com.lishid:openinv* - - ** - - - - *:* - - - META-INF/MANIFEST.MF - - - - true - - - - package - - shade - - - org.apache.maven.plugins 3.6.0 + + maven-resources-plugin + org.apache.maven.plugins + 3.3.1 + + + + maven-jar-plugin + org.apache.maven.plugins + 3.4.1 + + + + spigot + + + + + maven-compiler-plugin + + + + com.google.errorprone + error_prone_core + 2.28.0 + + + + -XDcompilePolicy=simple + -Xplugin:ErrorProne + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + + true + org.apache.maven.plugins 3.13.0 @@ -170,9 +181,7 @@
- net.md-5 specialsource-maven-plugin - 2.0.3 package @@ -201,6 +210,8 @@ + net.md-5 + 2.0.3
From ebc063d34dc26623a9139a0b5392a8676c2f03c5 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 11 Jul 2024 00:33:47 -0400 Subject: [PATCH 188/340] Reimplement ISpecialEnderChest (#220) --- .../internal/v1_21_R1/AnySilentContainer.java | 14 +- .../internal/v1_21_R1/InternalAccessor.java | 5 +- .../internal/v1_21_R1/OpenCraftView.java | 59 --- .../internal/v1_21_R1/PlayerManager.java | 107 ++---- .../internal/v1_21_R1/SpecialEnderChest.java | 352 ------------------ .../v1_21_R1/inventory/ContainerSlot.java | 6 +- .../v1_21_R1/inventory/OpenEnderChest.java | 219 +++++++++++ .../v1_21_R1/inventory/OpenInventory.java | 18 +- .../v1_21_R1/inventory/OpenInventoryMenu.java | 13 +- 9 files changed, 280 insertions(+), 513 deletions(-) delete mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java delete mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java index 15faeff8..637c6bc4 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java @@ -74,6 +74,18 @@ public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) } } + public static @NotNull MenuType getContainers(int inventorySize) { + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; + case 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + // Default to 27-slot inventory + default -> MenuType.GENERIC_9x3; + }; + } + @Override public boolean activateContainer( @NotNull final Player bukkitPlayer, @@ -102,7 +114,7 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerManager.getContainers(enderChest.getContainerSize()); + MenuType containers = getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); }, Component.translatable("container.enderchest"))); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java index 59f3481a..a70af0af 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java @@ -5,6 +5,7 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.v1_21_R1.inventory.OpenEnderChest; import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; import com.lishid.openinv.util.lang.LanguageManager; @@ -27,7 +28,7 @@ public class InternalAccessor implements Accessor { public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { this.logger = logger; - manager = new PlayerManager(logger, lang); + manager = new PlayerManager(logger); anySilentContainer = new AnySilentContainer(logger, lang); } @@ -48,7 +49,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { @Override public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { - return new SpecialEnderChest(player, player.isOnline()); + return new OpenEnderChest(player); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java deleted file mode 100644 index d2a003ed..00000000 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenCraftView.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R1; - -import com.lishid.openinv.internal.ISpecialInventory; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftAbstractInventoryView; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.jetbrains.annotations.NotNull; - -class OpenCraftView extends CraftAbstractInventoryView { - - private final @NotNull Player player; - private final @NotNull ISpecialInventory inventory; - private final @NotNull String originalTitle; - private String title = null; - - OpenCraftView(@NotNull Player player, @NotNull ISpecialInventory inventory, @NotNull String originalTitle) { - this.player = player; - this.inventory = inventory; - this.originalTitle = originalTitle; - } - - @Override - public @NotNull Inventory getTopInventory() { - return inventory.getBukkitInventory(); - } - - @Override - public @NotNull Inventory getBottomInventory() { - return player.getInventory(); - } - - @Override - public @NotNull HumanEntity getPlayer() { - return player; - } - - @Override - public @NotNull InventoryType getType() { - return inventory.getBukkitInventory().getType(); - } - - @Override - public @NotNull String getTitle() { - return title == null ? originalTitle : title; - } - - @Override - public @NotNull String getOriginalTitle() { - return originalTitle; - } - - @Override - public void setTitle(@NotNull String title) { - this.title = title; - } - -} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java index 2c81a192..e40eade4 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java @@ -17,10 +17,8 @@ package com.lishid.openinv.internal.v1_21_R1; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.InventoryViewTitle; -import com.lishid.openinv.internal.OpenInventoryView; +import com.lishid.openinv.internal.v1_21_R1.inventory.OpenEnderChest; import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; -import com.lishid.openinv.util.lang.LanguageManager; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -31,13 +29,12 @@ import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; -import org.apache.commons.lang3.function.TriFunction; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; @@ -46,7 +43,6 @@ import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; 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.CraftContainer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -59,31 +55,22 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { private static boolean paper; - private static TriFunction viewProvider; - static { + static { try { Class.forName("io.papermc.paper.configuration.Configuration"); paper = true; } catch (ClassNotFoundException ignored) { paper = false; } - try { - Class.forName(Bukkit.getServer().getClass().getPackageName() + ".inventory.CraftAbstractInventoryView"); - viewProvider = OpenCraftView::new; - } catch (ClassNotFoundException ignored) { - viewProvider = OpenInventoryView::new; - } } private final @NotNull Logger logger; - private final @NotNull LanguageManager lang; private @Nullable Field bukkitEntity; - public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { + public PlayerManager(@NotNull Logger logger) { this.logger = logger; - this.lang = lang; - try { + try { bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); } catch (NoSuchFieldException e) { logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); @@ -260,76 +247,42 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { return null; } - if (inventory instanceof OpenInventory container) { - // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) - AbstractContainerMenu menu = container.createMenu(player.nextContainerCounter(), player.getInventory(), player); - - // Should never happen, player is a ServerPlayer with an active connection. - if (menu == null) { - return null; - } - - // Set up title (the whole reason we're not just using Player#openInventory(Inventory)). - // Title can only be set once for a menu, and is set during the open process. - menu.setTitle(container.getTitle(player)); - - menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); - - // Menu is null if event is cancelled. - if (menu == null) { - return null; - } - - player.containerMenu = menu; - player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); - player.initMenu(menu); - - return menu.getBukkitView(); + MenuProvider provider; + Component title; + if (inventory instanceof OpenInventory playerInv) { + provider = playerInv; + title = playerInv.getTitle(player); + } else if (inventory instanceof OpenEnderChest enderChest) { + provider = enderChest; + title = enderChest.getDisplayName(); + } else { + return null; } - InventoryViewTitle viewTitle = InventoryViewTitle.of(inventory); + // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) + AbstractContainerMenu menu = provider.createMenu(player.nextContainerCounter(), player.getInventory(), player); - if (viewTitle == null) { - return bukkitPlayer.openInventory(inventory.getBukkitInventory()); + // Should never happen, player is a ServerPlayer with an active connection. + if (menu == null) { + return null; } - String originalTitle = viewTitle.getTitle(lang, bukkitPlayer, inventory); - InventoryView view = viewProvider.apply(bukkitPlayer, inventory, originalTitle); - Component title = Component.literal(originalTitle); - AbstractContainerMenu container = new CraftContainer(view, player, player.nextContainerCounter()) { - @Override - public MenuType getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - container.setTitle(title); + // Set up title (the whole reason we're not just using Player#openInventory(Inventory)). + // Title can only be set once for a menu, and is set during the open process. + menu.setTitle(title); - container = CraftEventFactory.callInventoryOpenEvent(player, container); + menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); - if (container == null) { + // Menu is null if event is cancelled. + if (menu == null) { return null; } - // Note: Reusing component prevents plugins from changing title during InventoryOpenEvent, but there's not much - // we can do about that - we can't call InventoryView#getTitle on older versions without causing an - // IncompatibleClassChangeError due to the fact that InventoryView is now an interface. - player.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), title)); - player.containerMenu = container; - player.initMenu(container); - - return container.getBukkitView(); - - } + player.containerMenu = menu; + player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); + player.initMenu(menu); - static @NotNull MenuType getContainers(int inventorySize) { - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; - case 41, 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> MenuType.GENERIC_9x3; // Default 27-slot inventory - }; + return menu.getBukkitView(); } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java deleted file mode 100644 index a7d74c49..00000000 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/SpecialEnderChest.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_21_R1; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.ContainerListener; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { - - private final CraftInventory inventory; - private ServerPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChestInventory().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerManager.getHandle(player); - - // Set owner to new player. - this.owner = onlinePlayer; - - // Set player's ender chest contents to our modified contents. - PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); - for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { - onlineEnderChest.setItem(i, this.items.get(i)); - } - - // Set our item array to the new inventory's array. - this.items = onlineEnderChest.items; - - // Add viewers to new inventory. - onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChestInventory().getViewers(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public void setActiveChest(EnderChestBlockEntity enderChest) { - this.owner.getEnderChestInventory().setActiveChest(enderChest); - } - - @Override - public boolean isActiveChest(EnderChestBlockEntity enderChest) { - return this.owner.getEnderChestInventory().isActiveChest(enderChest); - } - - @Override - public int getMaxStackSize() { - return this.owner.getEnderChestInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChestInventory().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChestInventory().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void addListener(ContainerListener listener) { - this.owner.getEnderChestInventory().addListener(listener); - } - - @Override - public void removeListener(ContainerListener listener) { - this.owner.getEnderChestInventory().removeListener(listener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int i, int j) { - ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack addItem(ItemStack itemstack) { - ItemStack localItem = itemstack.copy(); - this.moveItemToOccupiedSlotsWithSameType(localItem); - if (localItem.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.moveItemToEmptySlots(localItem); - return localItem.isEmpty() ? ItemStack.EMPTY : localItem; - } - } - - @Override - public boolean canAddItem(ItemStack itemstack) { - for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || (ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) { - return true; - } - } - - return false; - } - - private void moveItemToEmptySlots(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (localItem.isEmpty()) { - this.setItem(i, itemstack.copy()); - itemstack.setCount(0); - return; - } - } - } - - private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameComponents(localItem, itemstack)) { - this.moveItemsBetweenStacks(itemstack, localItem); - if (itemstack.isEmpty()) { - return; - } - } - } - } - - private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { - int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); - int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); - if (j > 0) { - itemstack1.grow(j); - itemstack.shrink(j); - this.setChanged(); - } - } - - @Override - public ItemStack removeItemNoUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.items.set(i, ItemStack.EMPTY); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.setChanged(); - } - - @Override - public int getContainerSize() { - return this.owner.getEnderChestInventory().getContainerSize(); - } - - @Override - public boolean isEmpty() { - return this.items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void startOpen(Player player) { - } - - @Override - public void stopOpen(Player player) { - } - - @Override - public boolean canPlaceItem(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clearContent() { - this.items.clear(); - this.setChanged(); - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountStack(itemstack); - } - - } - - @Override - public List removeAllItems() { - List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clearContent(); - return list; - } - - @Override - public ItemStack removeItemType(Item item, int i) { - ItemStack itemstack = new ItemStack(item, 0); - - for(int j = this.getContainerSize() - 1; j >= 0; --j) { - ItemStack localItem = this.getItem(j); - if (localItem.getItem().equals(item)) { - int k = i - itemstack.getCount(); - ItemStack splitItem = localItem.split(k); - itemstack.grow(splitItem.getCount()); - if (itemstack.getCount() == i) { - break; - } - } - } - - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); - } - - @Override - public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) { - for (int i = 0; i < this.getContainerSize(); ++i) { - this.setItem(i, ItemStack.EMPTY); - } - - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag)); - } - } - - } - -} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java index 8c003095..a61b1549 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java @@ -19,21 +19,21 @@ interface ContainerSlot { void setHolder(@NotNull ServerPlayer holder); /** - * Get the current item. May be fake for display purposes! + * Get the current item. * * @return the current item */ ItemStack get(); /** - * Remove the current item. Item removed is real. + * Remove the current item. * * @return the current item */ ItemStack remove(); /** - * Remove some of the current item. Item removed is real. + * Remove some of the current item. * * @return the current item */ diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java new file mode 100644 index 00000000..b9234690 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011-2023 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.v1_21_R1.AnySilentContainer; +import com.lishid.openinv.internal.v1_21_R1.PlayerManager; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.StackedContentsCompatible; +import net.minecraft.world.item.ItemStack; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class OpenEnderChest implements Container, StackedContentsCompatible, MenuProvider, ISpecialEnderChest { + + private CraftInventory inventory; + private @NotNull ServerPlayer owner; + private NonNullList items; + private int maxStack = 64; + private final List transaction = new ArrayList<>(); + + public OpenEnderChest(@NotNull org.bukkit.entity.Player player) { + this.owner = PlayerManager.getHandle(player); + this.items = owner.getEnderChestInventory().items; + } + + @Override + public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { + if (inventory == null) { + inventory = new CraftInventory(this) { + @Override + public @NotNull InventoryType getType() { + return InventoryType.ENDER_CHEST; + } + }; + } + return inventory; + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + owner = PlayerManager.getHandle(player); + NonNullList activeItems = owner.getEnderChestInventory().items; + + // Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally. + int max = Math.min(items.size(), activeItems.size()); + for (int index = 0; index < max; ++index) { + activeItems.set(index, items.get(index)); + } + + items = activeItems; + } + + @Override + public void setPlayerOffline() {} + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public int getContainerSize() { + return items.size(); + } + + @Override + public boolean isEmpty() { + return items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public ItemStack getItem(int index) { + return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int index, int amount) { + ItemStack itemstack = ContainerHelper.removeItem(items, index, amount); + + if (!itemstack.isEmpty()) { + setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack removeItemNoUpdate(int index) { + return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY; + } + + @Override + public void setItem(int index, ItemStack itemStack) { + if (index >= 0 && index < items.size()) { + items.set(index, itemStack); + } + } + + @Override + public int getMaxStackSize() { + return maxStack; + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public List getContents() { + return items; + } + + @Override + public void onOpen(CraftHumanEntity craftHumanEntity) { + transaction.add(craftHumanEntity); + } + + @Override + public void onClose(CraftHumanEntity craftHumanEntity) { + transaction.remove(craftHumanEntity); + } + + @Override + public List getViewers() { + return transaction; + } + + @Override + public org.bukkit.entity.Player getOwner() { + return getPlayer(); + } + + @Override + public void setMaxStackSize(int size) { + maxStack = size; + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void clearContent() { + items.clear(); + setChanged(); + } + + @Override + public void fillStackedContents(StackedContents stackedContents) { + for (ItemStack itemstack : items) { + stackedContents.accountStack(itemstack); + } + } + + @Override + public Component getDisplayName() { + return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName()) + .append(Component.translatable("container.enderchest")) + .append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName())); + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { + int rows = (getContainerSize() % 9) + 1; + return new ChestMenu(AnySilentContainer.getContainers(getContainerSize()), i, inventory, this, rows) { + private CraftInventoryView view; + @Override + public CraftInventoryView getBukkitView() { + if (view == null) { + view = new CraftInventoryView(player.getBukkitEntity(), getBukkitInventory(), this); + } + return view; + } + }; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java index 5bdea862..9d584eb5 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java @@ -10,7 +10,6 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.MenuProvider; -import net.minecraft.world.Nameable; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -28,7 +27,7 @@ import java.util.ArrayList; import java.util.List; -public class OpenInventory implements Container, Nameable, MenuProvider, ISpecialPlayerInventory { +public class OpenInventory implements Container, MenuProvider, ISpecialPlayerInventory { private final List slots; private final int size; @@ -215,7 +214,8 @@ public ServerPlayer getOwnerHandle() { .withColor(ChatFormatting.WHITE))); } // Normal title: "Inventory - OwnerName" - component.append(Component.translatable("container.inventory")) + component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) + .append(Component.translatable("container.inventory")) .append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName())); return component; } @@ -241,6 +241,11 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { @Override public void setPlayerOffline() {} + @Override + public boolean isInUse() { + return !transaction.isEmpty(); + } + @Override public @NotNull org.bukkit.entity.Player getPlayer() { return getOwner(); @@ -336,14 +341,9 @@ public void clearContent() { owner.containerMenu.setCarried(ItemStack.EMPTY); } - @Override - public Component getName() { - return getTitle(null); - } - @Override public Component getDisplayName() { - return getName(); + return getTitle(null); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java index a0eef7ff..7e2e4a59 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -1,6 +1,7 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.v1_21_R1.AnySilentContainer; import com.lishid.openinv.util.Permissions; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -135,21 +136,13 @@ static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { size = ((int) Math.ceil(size / 9.0)) * 9; } - return switch (size) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; - case 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - // Default to 27-slot inventory. - default -> MenuType.GENERIC_9x3; - }; + return AnySilentContainer.getContainers(size); } @Override public CraftInventoryView getBukkitView() { if (bukkitEntity == null) { - bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), new OpenPlayerInventory(inventory), this) { + bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), inventory.getBukkitInventory(), this) { @Override public org.bukkit.inventory.ItemStack getItem(int index) { if (index < 0) { From 8bf63e7f12f5e2ff61cacbecabbe85682e27ada2 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 11 Jul 2024 12:34:58 -0400 Subject: [PATCH 189/340] Internalize view-only status (#221) --- .../internal/v1_21_R1/InternalAccessor.java | 2 + .../{ => inventory}/AnySilentContainer.java | 17 +- .../ContainerSlotUninteractable.java | 169 --------- .../v1_21_R1/inventory/OpenContainerMenu.java | 306 ++++++++++++++++ .../v1_21_R1/inventory/OpenEnderChest.java | 39 +-- .../inventory/OpenEnderChestMenu.java | 127 +++++++ .../v1_21_R1/inventory/OpenInventory.java | 46 ++- .../v1_21_R1/inventory/OpenInventoryMenu.java | 326 ++---------------- .../v1_21_R1/inventory/Placeholders.java | 24 +- .../{ContainerSlot.java => slot/Content.java} | 10 +- .../ContentCrafting.java} | 17 +- .../ContentCraftingResult.java} | 15 +- .../ContentCursor.java} | 15 +- .../ContentDrop.java} | 15 +- .../ContentEquipment.java} | 19 +- .../ContentList.java} | 12 +- .../ContentOffHand.java} | 10 +- .../inventory/slot/ContentViewOnly.java | 56 +++ .../SlotPlaceholder.java} | 8 +- .../v1_21_R1/inventory/slot/SlotViewOnly.java | 151 ++++++++ .../v1_21_R1/{ => player}/OpenPlayer.java | 18 +- .../v1_21_R1/{ => player}/PlayerManager.java | 18 +- plugin/pom.xml | 8 + 23 files changed, 795 insertions(+), 633 deletions(-) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{ => inventory}/AnySilentContainer.java (93%) delete mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlot.java => slot/Content.java} (86%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotCrafting.java => slot/ContentCrafting.java} (84%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotCraftingResult.java => slot/ContentCraftingResult.java} (68%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotCursor.java => slot/ContentCursor.java} (86%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotDrop.java => slot/ContentDrop.java} (78%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotEquipment.java => slot/ContentEquipment.java} (73%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotList.java => slot/ContentList.java} (75%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{ContainerSlotOffHand.java => slot/ContentOffHand.java} (75%) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/{MenuSlotPlaceholder.java => slot/SlotPlaceholder.java} (61%) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{ => player}/OpenPlayer.java (89%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{ => player}/PlayerManager.java (93%) diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java index a70af0af..a612f41e 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java @@ -5,9 +5,11 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.v1_21_R1.inventory.AnySilentContainer; import com.lishid.openinv.internal.v1_21_R1.inventory.OpenEnderChest; import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; import org.bukkit.configuration.ConfigurationSection; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/AnySilentContainer.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/AnySilentContainer.java index 637c6bc4..a7c0211c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/AnySilentContainer.java @@ -14,9 +14,10 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_21_R1; +package com.lishid.openinv.internal.v1_21_R1.inventory; import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -74,18 +75,6 @@ public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) } } - public static @NotNull MenuType getContainers(int inventorySize) { - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; - case 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - // Default to 27-slot inventory - default -> MenuType.GENERIC_9x3; - }; - } - @Override public boolean activateContainer( @NotNull final Player bukkitPlayer, @@ -114,7 +103,7 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = getContainers(enderChest.getContainerSize()); + MenuType containers = OpenContainerMenu.getContainers(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); }, Component.translatable("container.enderchest"))); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java deleted file mode 100644 index 48b57709..00000000 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotUninteractable.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; - -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; - -/** - * A fake slot used to fill spaces in the inventory that can't be interacted with. - */ -class ContainerSlotUninteractable implements ContainerSlot { - - @NotNull ServerPlayer holder; - - ContainerSlotUninteractable(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public ItemStack get() { - return ItemStack.EMPTY; - } - - @Override - public ItemStack remove() { - return ItemStack.EMPTY; - } - - @Override - public ItemStack removePartial(int amount) { - return ItemStack.EMPTY; - } - - @Override - public void set(ItemStack itemStack) { - this.holder.drop(itemStack, false); - } - - @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotUninteractable(container, index, x, y); - } - - @Override - public InventoryType.SlotType getSlotType() { - return InventoryType.SlotType.OUTSIDE; - } - - static class SlotUninteractable extends MenuSlotPlaceholder { - - SlotUninteractable(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - @Override - ItemStack getOrDefault() { - return Placeholders.notSlot; - } - - @Override - public void onQuickCraft(ItemStack var0, ItemStack var1) {} - - @Override - public void onTake(Player var0, ItemStack var1) {} - - @Override - public boolean mayPlace(ItemStack var0) { - return false; - } - - @Override - public ItemStack getItem() { - return ItemStack.EMPTY; - } - - @Override - public boolean hasItem() { - return false; - } - - @Override - public void setByPlayer(ItemStack newStack) {} - - @Override - public void setByPlayer(ItemStack newStack, ItemStack oldStack) {} - - @Override - public void set(ItemStack var0) {} - - @Override - public void setChanged() {} - - @Override - public int getMaxStackSize() { - return 0; - } - - @Override - public int getMaxStackSize(ItemStack itemStack) { - return 0; - } - - @Override - public ItemStack remove(int amount) { - return ItemStack.EMPTY; - } - - @Override - public boolean mayPickup(Player var0) { - return false; - } - - @Override - public boolean isActive() { - return false; - } - - @Override - public Optional tryRemove(int var0, int var1, Player var2) { - return Optional.empty(); - } - - @Override - public ItemStack safeTake(int var0, int var1, Player var2) { - return ItemStack.EMPTY; - } - - @Override - public ItemStack safeInsert(ItemStack itemStack) { - return itemStack; - } - - @Override - public ItemStack safeInsert(ItemStack itemStack, int amount) { - return itemStack; - } - - @Override - public boolean allowModification(Player var0) { - return false; - } - - @Override - public int getContainerSlot() { - return this.slot; - } - - @Override - public boolean isHighlightable() { - return false; - } - - @Override - public boolean isFake() { - return true; - } - - } -} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java new file mode 100644 index 00000000..005f2ad2 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java @@ -0,0 +1,306 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotPlaceholder; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.ContainerListener; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. + */ +public abstract class OpenContainerMenu extends AbstractContainerMenu { + + private @Nullable ContainerSynchronizer synchronizer; + private final List dataSlots = new ArrayList<>(); + private final IntList remoteDataSlots = new IntArrayList(); + private final List containerListeners = new ArrayList<>(); + private ItemStack remoteCarried = ItemStack.EMPTY; + private boolean suppressRemoteUpdates; + + protected OpenContainerMenu(@Nullable MenuType containers, int containerCounter) { + super(containers, containerCounter); + } + + static @NotNull MenuType getContainers(int inventorySize) { + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 36 -> MenuType.GENERIC_9x4; + case 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + // Default to 27-slot inventory + default -> MenuType.GENERIC_9x3; + }; + } + + /** + * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake + * slots and respects {@link Slot#hasItem()}. + * + * @param itemStack the stack to quick-move + * @param rangeLow the start of the range of slots that can be moved to, inclusive + * @param rangeHigh the end of the range of slots that can be moved to, exclusive + * @param topDown whether to start at the top of the range or bottom + * @return whether the stack was modified as a result of being quick-moved + */ + @Override + protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { + boolean modified = false; + boolean stackable = itemStack.isStackable(); + Slot firstEmpty = null; + + for (int index = topDown ? rangeHigh - 1 : rangeLow; + !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); + index += topDown ? -1 : 1 + ) { + Slot slot = slots.get(index); + // If the slot cannot be added to, check the next slot. + if (slot.isFake() || !slot.mayPlace(itemStack)) { + continue; + } + + if (slot.hasItem()) { + // If the item isn't stackable, check the next slot. + if (!stackable) { + continue; + } + // Otherwise, add as many as we can from our stack to the slot. + modified = addToExistingStack(itemStack, slot); + } else { + // If this is the first empty slot, keep track of it for later use. + if (firstEmpty == null) { + firstEmpty = slot; + } + // If the item isn't stackable, we've located the slot we're adding it to, so we're done. + if (!stackable) { + break; + } + } + } + + // If the item hasn't been fully added yet, add as many as we can to the first open slot. + if (!itemStack.isEmpty() && firstEmpty != null) { + firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); + firstEmpty.setChanged(); + modified = true; + } + + return modified; + } + + private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { + ItemStack existing = slot.getItem(); + + // If the items aren't the same, we can't add our item. + if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { + return false; + } + + int total = existing.getCount() + itemStack.getCount(); + int max = slot.getMaxStackSize(existing); + + // If the existing item can accept the entirety of our item, we're done! + if (total <= max) { + itemStack.setCount(0); + existing.setCount(total); + slot.setChanged(); + return true; + } + + // Otherwise, add as many as we can. + itemStack.shrink(max - existing.getCount()); + existing.setCount(max); + slot.setChanged(); + return true; + } + + @Override + protected Slot addSlot(Slot slot) { + slot.index = this.slots.size(); + this.slots.add(slot); + this.lastSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(ItemStack.EMPTY); + return slot; + } + + @Override + protected DataSlot addDataSlot(DataSlot dataSlot) { + this.dataSlots.add(dataSlot); + this.remoteDataSlots.add(0); + return dataSlot; + } + + @Override + protected void addDataSlots(ContainerData containerData) { + for (int i = 0; i < containerData.getCount(); i++) { + this.addDataSlot(DataSlot.forContainer(containerData, i)); + } + } + + @Override + public void addSlotListener(ContainerListener containerListener) { + if (!this.containerListeners.contains(containerListener)) { + this.containerListeners.add(containerListener); + this.broadcastChanges(); + } + } + + @Override + public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { + this.synchronizer = containerSynchronizer; + this.sendAllDataToRemote(); + } + + @Override + public void sendAllDataToRemote() { + for (int index = 0; index < slots.size(); ++index) { + Slot slot = slots.get(index); + this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); + } + + remoteCarried = getCarried().copy(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); + } + + if (this.synchronizer != null) { + this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); + } + } + + @Override + public void broadcastCarriedItem() { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + + @Override + public void removeSlotListener(ContainerListener containerListener) { + this.containerListeners.remove(containerListener); + } + + @Override + public void broadcastChanges() { + for (int index = 0; index < this.slots.size(); ++index) { + Slot slot = this.slots.get(index); + ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + Supplier supplier = Suppliers.memoize(itemstack::copy); + this.triggerSlotListeners(index, itemstack, supplier); + this.synchronizeSlotToRemote(index, itemstack, supplier); + } + + this.synchronizeCarriedToRemote(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot dataSlot = this.dataSlots.get(index); + int j = dataSlot.get(); + if (dataSlot.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, j); + } + + this.synchronizeDataSlotToRemote(index, j); + } + } + + @Override + public void broadcastFullState() { + for (int index = 0; index < this.slots.size(); ++index) { + ItemStack itemstack = this.slots.get(index).getItem(); + this.triggerSlotListeners(index, itemstack, itemstack::copy); + } + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot containerproperty = this.dataSlots.get(index); + if (containerproperty.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, containerproperty.get()); + } + } + + this.sendAllDataToRemote(); + } + + private void updateDataSlotListeners(int i, int j) { + for (ContainerListener containerListener : this.containerListeners) { + containerListener.dataChanged(this, i, j); + } + } + + private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + ItemStack itemStack1 = this.lastSlots.get(index); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemStack2 = supplier.get(); + this.lastSlots.set(index, itemStack2); + + for (ContainerListener containerListener : this.containerListeners) { + containerListener.slotChanged(this, index, itemStack2); + } + } + } + + private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { + if (!this.suppressRemoteUpdates) { + ItemStack itemStack1 = this.remoteSlots.get(i); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemstack2 = supplier.get(); + this.remoteSlots.set(i, itemstack2); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, i, itemstack2); + } + } + } + } + + private void synchronizeDataSlotToRemote(int index, int value) { + if (!this.suppressRemoteUpdates) { + int existing = this.remoteDataSlots.getInt(index); + if (existing != value) { + this.remoteDataSlots.set(index, value); + if (this.synchronizer != null) { + this.synchronizer.sendDataChange(this, index, value); + } + } + } + } + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + } + + @Override + public void setRemoteCarried(ItemStack itemstack) { + this.remoteCarried = itemstack.copy(); + } + + @Override + public void suppressRemoteUpdates() { + this.suppressRemoteUpdates = true; + } + + @Override + public void resumeRemoteUpdates() { + this.suppressRemoteUpdates = false; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java index b9234690..55ca749c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java @@ -1,24 +1,7 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package com.lishid.openinv.internal.v1_21_R1.inventory; import com.lishid.openinv.internal.ISpecialEnderChest; -import com.lishid.openinv.internal.v1_21_R1.AnySilentContainer; -import com.lishid.openinv.internal.v1_21_R1.PlayerManager; +import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -29,13 +12,11 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.StackedContentsCompatible; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -200,20 +181,12 @@ public Component getDisplayName() { .append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName())); } - @Nullable @Override - public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { - int rows = (getContainerSize() % 9) + 1; - return new ChestMenu(AnySilentContainer.getContainers(getContainerSize()), i, inventory, this, rows) { - private CraftInventoryView view; - @Override - public CraftInventoryView getBukkitView() { - if (view == null) { - view = new CraftInventoryView(player.getBukkitEntity(), getBukkitInventory(), this); - } - return view; - } - }; + public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenEnderChestMenu(this, serverPlayer, i); + } + return null; } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java new file mode 100644 index 00000000..dd333d5d --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java @@ -0,0 +1,127 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory; + +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotViewOnly; +import com.lishid.openinv.util.Permissions; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +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.inventory.CraftInventoryView; + +class OpenEnderChestMenu extends OpenContainerMenu { + + private final OpenEnderChest enderChest; + private final boolean viewOnly; + private final ServerPlayer viewer; + private final int topSize; + private CraftInventoryView view; + + OpenEnderChestMenu(OpenEnderChest enderChest, ServerPlayer viewer, int containerId) { + super(getMenuType(enderChest), containerId); + this.enderChest = enderChest; + this.viewer = viewer; + var bukkitViewer = viewer.getBukkitEntity(); + viewOnly = !(bukkitViewer.equals(enderChest.getPlayer()) + ? Permissions.ENDERCHEST_EDIT_SELF + : Permissions.ENDERCHEST_OPEN_OTHER) + .hasPermission(bukkitViewer); + int upperRows = (int) Math.ceil(enderChest.getContainerSize() / 9.0); + topSize = upperRows * 9; + + // View's upper inventory - our container + for (int row = 0; row < upperRows; ++row) { + for (int col = 0; col < 9; ++col) { + // x and y for client purposes, but hey, we're thorough here. + // Adapted from net.minecraft.world.inventory.ChestMenu + int x = 8 + col * 18; + int y = 18 + row * 18; + int index = row * 9 + col; + + // Guard against weird inventory sizes. + if (index >= enderChest.getContainerSize()) { + addSlot(new SlotViewOnly(enderChest, index, x, y)); + continue; + } + + addSlot(new Slot(enderChest, index, x, y)); + } + } + + // View's lower inventory - viewer inventory + int playerInvPad = (upperRows - 4) * 18; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + row * 18 + 103; + addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); + } + } + // Hotbar + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + 161; + addSlot(new Slot(viewer.getInventory(), col, x, y)); + } + } + + private static MenuType getMenuType(OpenEnderChest enderChest) { + return OpenContainerMenu.getContainers(((int) Math.ceil(enderChest.getContainerSize() / 9.0)) * 9); + } + + @Override + public CraftInventoryView getBukkitView() { + if (view == null) { + view = new CraftInventoryView(viewer.getBukkitEntity(), enderChest.getBukkitInventory(), this); + } + return view; + } + + @Override + protected Slot addSlot(Slot slot) { + slot = super.addSlot(slot); + + // If view-only and slot is in upper inventory, wrap it. + if (viewOnly && slot.index < enderChest.getContainerSize()) { + slot = SlotViewOnly.wrap(slot); + slots.set(slot.index, slot); + } + + return slot; + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + // See ChestMenu + Slot slot = this.slots.get(index); + + if (slot.isFake() || !slot.hasItem()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack original = itemStack.copy(); + + if (index < topSize) { + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) { + return ItemStack.EMPTY; + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return original; + } + + @Override + public boolean stillValid(Player entityhuman) { + return true; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java index 9d584eb5..3f6c907f 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java @@ -1,7 +1,17 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.v1_21_R1.PlayerManager; +import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.Content; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentCrafting; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentCraftingResult; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentCursor; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentList; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentOffHand; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentViewOnly; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotViewOnly; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -29,7 +39,7 @@ public class OpenInventory implements Container, MenuProvider, ISpecialPlayerInventory { - private final List slots; + private final List slots; private final int size; private ServerPlayer owner; private int maxStackSize = 99; @@ -43,7 +53,7 @@ public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; size = ((int) Math.ceil(rawSize / 9.0)) * 9; - slots = NonNullList.withSize(size, new ContainerSlotUninteractable(owner)); + slots = NonNullList.withSize(size, new ContentViewOnly(owner)); setupSlots(); } @@ -62,9 +72,9 @@ private void setupSlots() { // Off-hand: Below chestplate. addOffHand(46); // Drop slot: Bottom right. - slots.set(53, new ContainerSlotDrop(owner)); + slots.set(53, new ContentDrop(owner)); // Cursor slot: Above drop. - slots.set(44, new ContainerSlotCursor(owner)); + slots.set(44, new ContentCursor(owner)); // Crafting is displayed in the bottom right corner. // As we're using the pretty view, this is a 3x2. @@ -76,9 +86,9 @@ private void setupSlots() { nextIndex = addArmor(nextIndex); nextIndex = addOffHand(nextIndex); nextIndex = addCrafting(nextIndex, false); - slots.set(nextIndex, new ContainerSlotCursor(owner)); + slots.set(nextIndex, new ContentCursor(owner)); // Drop slot last. - slots.set(slots.size() - 1, new ContainerSlotDrop(owner)); + slots.set(slots.size() - 1, new ContentDrop(owner)); } private int addMainInventory() { @@ -97,7 +107,7 @@ private int addMainInventory() { invIndex = localIndex - hotbarDiff; } - slots.set(localIndex, new ContainerSlotList(owner, invIndex, type) { + slots.set(localIndex, new ContentList(owner, invIndex, type) { @Override public void setHolder(@NotNull ServerPlayer holder) { items = holder.getInventory().items; @@ -140,7 +150,7 @@ private int addArmor(int startIndex) { } } - slots.set(startIndex + i, new ContainerSlotEquipment(owner, armorIndex, slot)); + slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot)); } return startIndex + listSize; @@ -149,7 +159,7 @@ private int addArmor(int startIndex) { private int addOffHand(int startIndex) { int listSize = owner.getInventory().offhand.size(); for (int localIndex = 0; localIndex < listSize; ++localIndex) { - slots.set(startIndex + localIndex, new ContainerSlotOffHand(owner, localIndex)); + slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex)); } return startIndex + listSize; } @@ -164,29 +174,29 @@ private int addCrafting(int startIndex, boolean pretty) { // Otherwise, subtract 2 and add 9 to start in the same position on the next row. int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); - slots.set(modIndex, new ContainerSlotCrafting(owner, localIndex)); + slots.set(modIndex, new ContentCrafting(owner, localIndex)); } if (pretty) { - slots.set(startIndex + 2, new ContainerSlotUninteractable(owner) { + slots.set(startIndex + 2, new ContentViewOnly(owner) { @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotUninteractable(container, index, x, y) { + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { @Override - ItemStack getOrDefault() { + public ItemStack getOrDefault() { return Placeholders.craftingOutput; } }; } }); - slots.set(startIndex + 11, new ContainerSlotCraftingResult(owner)); + slots.set(startIndex + 11, new ContentCraftingResult(owner)); } return startIndex + listSize; } public Slot getMenuSlot(int index, int x, int y) { - return slots.get(index).asMenuSlot(this, index, x, y); + return slots.get(index).asSlot(this, index, x, y); } public InventoryType.SlotType getSlotType(int index) { @@ -258,7 +268,7 @@ public int getContainerSize() { @Override public boolean isEmpty() { - return slots.stream().map(ContainerSlot::get).allMatch(ItemStack::isEmpty); + return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java index 7e2e4a59..3e3940cd 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -1,18 +1,12 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; -import com.google.common.base.Suppliers; -import com.lishid.openinv.internal.v1_21_R1.AnySilentContainer; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotViewOnly; import com.lishid.openinv.util.Permissions; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ContainerData; -import net.minecraft.world.inventory.ContainerListener; -import net.minecraft.world.inventory.ContainerSynchronizer; -import net.minecraft.world.inventory.DataSlot; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -20,13 +14,8 @@ import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -public class OpenInventoryMenu extends AbstractContainerMenu { +public class OpenInventoryMenu extends OpenContainerMenu { private final OpenInventory inventory; private final ServerPlayer viewer; @@ -50,6 +39,9 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) upperRows = inventory.getContainerSize() / 9; } + boolean viewOnly = !(ownInv ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER) + .hasPermission(viewer.getBukkitEntity()); + // View's upper inventory - our container for (int row = 0; row < upperRows; ++row) { for (int col = 0; col < 9; ++col) { @@ -61,11 +53,11 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) // Guard against weird inventory sizes. if (index >= inventory.getContainerSize()) { - addSlot(new ContainerSlotUninteractable.SlotUninteractable(inventory, index, x, y)); + addSlot(new SlotViewOnly(inventory, index, x, y)); continue; } - Slot slot = getUpperSlot(index, x, y, ownInv); + Slot slot = getUpperSlot(index, x, y, ownInv, viewOnly); addSlot(slot); } @@ -90,21 +82,25 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) this.topSize = slots.size() - 36; } - private Slot getUpperSlot(int index, int x, int y, boolean ownInv) { + private Slot getUpperSlot(int index, int x, int y, boolean ownInv, boolean viewOnly) { Slot slot = inventory.getMenuSlot(index, x, y); // If the slot is cannot be interacted with there's nothing to configure. - if (slot.getClass().equals(ContainerSlotUninteractable.SlotUninteractable.class)) { + if (slot.getClass().equals(SlotViewOnly.class)) { return slot; } // Remove drop slot if viewer is not allowed to use it. - if (slot instanceof ContainerSlotDrop.SlotDrop - && !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity())) { - return new ContainerSlotUninteractable.SlotUninteractable(inventory, index, x, y); + if (slot instanceof ContentDrop.SlotDrop + && (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) { + return new SlotViewOnly(inventory, index, x, y); } - if (slot instanceof ContainerSlotEquipment.SlotEquipment equipment) { + if (slot instanceof ContentEquipment.SlotEquipment equipment) { + if (viewOnly) { + return SlotViewOnly.wrap(slot); + } + Permissions perm = switch (equipment.getEquipmentSlot()) { case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY; case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY; @@ -113,30 +109,36 @@ private Slot getUpperSlot(int index, int x, int y, boolean ownInv) { // Off-hand can hold anything, not just equipment. default -> null; }; + // If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot. if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) { equipment.onlyEquipmentFor(inventory.getOwnerHandle()); } + // Equipment slots are a core part of the inventory, so they will always be shown. return slot; } // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). - if (ownInv && !(slot instanceof ContainerSlotDrop.SlotDrop)) { - return new ContainerSlotUninteractable.SlotUninteractable(inventory, index, x, y); + if (ownInv && !(slot instanceof ContentDrop.SlotDrop)) { + return new SlotViewOnly(inventory, index, x, y); + } + + if (viewOnly) { + return SlotViewOnly.wrap(slot); } return slot; } - static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { int size = inventory.getContainerSize(); if (inventory.getOwnerHandle().equals(viewer)) { size -= viewer.getInventory().items.size(); size = ((int) Math.ceil(size / 9.0)) * 9; } - return AnySilentContainer.getContainers(size); + return OpenContainerMenu.getContainers(size); } @Override @@ -174,191 +176,6 @@ public InventoryType.SlotType getSlotType(int slot) { return bukkitEntity; } - // - // Back at it again, overriding a bunch of methods because we need access to 2 fields. - private @Nullable ContainerSynchronizer synchronizer; - private final List dataSlots = new ArrayList<>(); - private final IntList remoteDataSlots = new IntArrayList(); - private final List containerListeners = new ArrayList<>(); - private ItemStack remoteCarried = ItemStack.EMPTY; - private boolean suppressRemoteUpdates; - - @Override - protected Slot addSlot(Slot slot) { - slot.index = this.slots.size(); - this.slots.add(slot); - this.lastSlots.add(ItemStack.EMPTY); - this.remoteSlots.add(ItemStack.EMPTY); - return slot; - } - - @Override - protected DataSlot addDataSlot(DataSlot dataSlot) { - this.dataSlots.add(dataSlot); - this.remoteDataSlots.add(0); - return dataSlot; - } - - @Override - protected void addDataSlots(ContainerData containerData) { - for (int i = 0; i < containerData.getCount(); i++) { - this.addDataSlot(DataSlot.forContainer(containerData, i)); - } - } - - @Override - public void addSlotListener(ContainerListener containerListener) { - if (!this.containerListeners.contains(containerListener)) { - this.containerListeners.add(containerListener); - this.broadcastChanges(); - } - } - - @Override - public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { - this.synchronizer = containerSynchronizer; - this.sendAllDataToRemote(); - } - - @Override - public void sendAllDataToRemote() { - for (int index = 0; index < slots.size(); ++index) { - Slot slot = slots.get(index); - this.remoteSlots.set(index, (slot instanceof MenuSlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); - } - - remoteCarried = getCarried().copy(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); - } - - if (this.synchronizer != null) { - this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); - } - } - - @Override - public void broadcastCarriedItem() { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); - } - } - - @Override - public void removeSlotListener(ContainerListener containerListener) { - this.containerListeners.remove(containerListener); - } - - @Override - public void broadcastChanges() { - for (int index = 0; index < this.slots.size(); ++index) { - Slot slot = this.slots.get(index); - ItemStack itemstack = slot instanceof MenuSlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); - Supplier supplier = Suppliers.memoize(itemstack::copy); - this.triggerSlotListeners(index, itemstack, supplier); - this.synchronizeSlotToRemote(index, itemstack, supplier); - } - - this.synchronizeCarriedToRemote(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot dataSlot = this.dataSlots.get(index); - int j = dataSlot.get(); - if (dataSlot.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, j); - } - - this.synchronizeDataSlotToRemote(index, j); - } - } - - @Override - public void broadcastFullState() { - for (int index = 0; index < this.slots.size(); ++index) { - ItemStack itemstack = this.slots.get(index).getItem(); - this.triggerSlotListeners(index, itemstack, itemstack::copy); - } - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot containerproperty = this.dataSlots.get(index); - if (containerproperty.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, containerproperty.get()); - } - } - - this.sendAllDataToRemote(); - } - - private void updateDataSlotListeners(int i, int j) { - for (ContainerListener containerListener : this.containerListeners) { - containerListener.dataChanged(this, i, j); - } - } - - private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { - ItemStack itemStack1 = this.lastSlots.get(index); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemStack2 = supplier.get(); - this.lastSlots.set(index, itemStack2); - - for (ContainerListener containerListener : this.containerListeners) { - containerListener.slotChanged(this, index, itemStack2); - } - } - } - - private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { - if (!this.suppressRemoteUpdates) { - ItemStack itemStack1 = this.remoteSlots.get(i); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemstack2 = supplier.get(); - this.remoteSlots.set(i, itemstack2); - if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(this, i, itemstack2); - } - } - } - } - - private void synchronizeDataSlotToRemote(int index, int value) { - if (!this.suppressRemoteUpdates) { - int existing = this.remoteDataSlots.getInt(index); - if (existing != value) { - this.remoteDataSlots.set(index, value); - if (this.synchronizer != null) { - this.synchronizer.sendDataChange(this, index, value); - } - } - } - } - - private void synchronizeCarriedToRemote() { - if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); - } - } - } - - @Override - public void setRemoteCarried(ItemStack itemstack) { - this.remoteCarried = itemstack.copy(); - } - - @Override - public void suppressRemoteUpdates() { - this.suppressRemoteUpdates = true; - } - - @Override - public void resumeRemoteUpdates() { - this.suppressRemoteUpdates = false; - } - // - @Override public ItemStack quickMoveStack(Player player, int index) { // See ChestMenu and InventoryMenu @@ -384,7 +201,7 @@ public ItemStack quickMoveStack(Player player, int index) { // Locate the correct slot in the contents following the main inventory. for (int extra = inventory.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { Slot extraSlot = getSlot(extra); - if (extraSlot instanceof ContainerSlotEquipment.SlotEquipment equipSlot + if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot && equipSlot.getEquipmentSlot() == equipmentSlot) { // If we've found a matching slot, try to move to it. // If this succeeds, even partially, we will not attempt to move to other slots. @@ -425,90 +242,9 @@ public boolean stillValid(Player player) { return true; } - /** - * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake - * slots and respects {@link Slot#hasItem()}. - * - * @param itemStack the stack to quick-move - * @param rangeLow the start of the range of slots that can be moved to, inclusive - * @param rangeHigh the end of the range of slots that can be moved to, exclusive - * @param topDown whether to start at the top of the range or bottom - * @return whether the stack was modified as a result of being quick-moved - */ - @Override - protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { - boolean modified = false; - boolean stackable = itemStack.isStackable(); - Slot firstEmpty = null; - - for (int index = topDown ? rangeHigh - 1 : rangeLow; - !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); - index += topDown ? -1 : 1 - ) { - Slot slot = slots.get(index); - // If the slot cannot be added to, check the next slot. - if (slot.isFake() || !slot.mayPlace(itemStack)) { - continue; - } - - if (slot.hasItem()) { - // If the item isn't stackable, check the next slot. - if (!stackable) { - continue; - } - // Otherwise, add as many as we can from our stack to the slot. - modified = addToExistingStack(itemStack, slot); - } else { - // If this is the first empty slot, keep track of it for later use. - if (firstEmpty == null) { - firstEmpty = slot; - } - // If the item isn't stackable, we've located the slot we're adding it to, so we're done. - if (!stackable) { - break; - } - } - } - - // If the item hasn't been fully added yet, add as many as we can to the first open slot. - if (!itemStack.isEmpty() && firstEmpty != null) { - firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); - firstEmpty.setChanged(); - modified = true; - } - - return modified; - } - - private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { - ItemStack existing = slot.getItem(); - - // If the items aren't the same, we can't add our item. - if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { - return false; - } - - int total = existing.getCount() + itemStack.getCount(); - int max = slot.getMaxStackSize(existing); - - // If the existing item can accept the entirety of our item, we're done! - if (total <= max) { - itemStack.setCount(0); - existing.setCount(total); - slot.setChanged(); - return true; - } - - // Otherwise, add as many as we can. - itemStack.shrink(max - existing.getCount()); - existing.setCount(max); - slot.setChanged(); - return true; - } - @Override public boolean canDragTo(Slot slot) { - return !(slot instanceof ContainerSlotDrop.SlotDrop || slot instanceof ContainerSlotUninteractable.SlotUninteractable); + return !(slot instanceof ContentDrop.SlotDrop || slot instanceof SlotViewOnly); } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java index 48b91ba7..16fc3269 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java @@ -30,17 +30,17 @@ public final class Placeholders { private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); - static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); - static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); - static @NotNull ItemStack cursor = defaultCursor(); - static @NotNull ItemStack drop = defaultDrop(); - static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); - static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); - static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); - static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); - static @NotNull ItemStack emptyOffHand = getEmptyShield(); - static @NotNull ItemStack notSlot = defaultNotSlot(); - static @NotNull ItemStack blockedOffline = defaultBlockedOffline(); + public static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); + public static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); + public static @NotNull ItemStack cursor = defaultCursor(); + public static @NotNull ItemStack drop = defaultDrop(); + public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); + public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); + public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); + public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); + public static @NotNull ItemStack emptyOffHand = getEmptyShield(); + public static @NotNull ItemStack notSlot = defaultNotSlot(); + public static @NotNull ItemStack blockedOffline = defaultBlockedOffline(); static { for (GameType type : GameType.values()) { @@ -83,7 +83,7 @@ public static void load(@NotNull ConfigurationSection section) throws Exception return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); } - static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { + public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { return blockedOffline; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/Content.java similarity index 86% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/Content.java index a61b1549..eed38f34 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlot.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/Content.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -7,9 +7,9 @@ import org.jetbrains.annotations.NotNull; /** - * An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate slot reordering. + * An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering. */ -interface ContainerSlot { +public interface Content { /** * Update internal holder. @@ -51,12 +51,12 @@ interface ContainerSlot { * impose any specific restrictions to insertion or removal. * * @param container the backing container - * @param index the slot of the backing container represented + * @param slot the slot of the backing container represented * @param x clientside x dimension from top left of inventory, not used * @param y clientside y dimension from top left of inventory, not used * @return a menu slot */ - Slot asMenuSlot(Container container, int index, int x, int y); + Slot asSlot(Container container, int slot, int x, int y); /** * Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCrafting.java similarity index 84% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCrafting.java index a071e123..5edd1ef0 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCrafting.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCrafting.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; @@ -14,13 +15,13 @@ /** * A slot in a survival crafting inventory. Unavailable when not online in a survival mode. */ -class ContainerSlotCrafting implements ContainerSlot { +public class ContentCrafting implements Content { private final int index; private ServerPlayer holder; private List items; - ContainerSlotCrafting(@NotNull ServerPlayer holder, int index) { + public ContentCrafting(@NotNull ServerPlayer holder, int index) { setHolder(holder); this.index = index; } @@ -85,8 +86,8 @@ public void set(ItemStack itemStack) { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotCrafting(container, index, x, y); + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotCrafting(container, slot, x, y); } @Override @@ -94,15 +95,15 @@ public InventoryType.SlotType getSlotType() { return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE; } - class SlotCrafting extends MenuSlotPlaceholder { + public class SlotCrafting extends SlotPlaceholder { private SlotCrafting(Container container, int index, int x, int y) { super(container, index, x, y); } @Override - ItemStack getOrDefault() { - return isAvailable() ? items.get(ContainerSlotCrafting.this.index) : Placeholders.survivalOnly(holder); + public ItemStack getOrDefault() { + return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCraftingResult.java similarity index 68% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCraftingResult.java index 3bf9fb43..4a26a543 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCraftingResult.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCraftingResult.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.InventoryMenu; @@ -13,9 +14,9 @@ * *

Unmodifiable because I said so. Use your own crafting grid.

*/ -class ContainerSlotCraftingResult extends ContainerSlotUninteractable { +public class ContentCraftingResult extends ContentViewOnly { - ContainerSlotCraftingResult(@NotNull ServerPlayer holder) { + public ContentCraftingResult(@NotNull ServerPlayer holder) { super(holder); } @@ -26,11 +27,11 @@ public ItemStack get() { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotUninteractable(container, index, x, y) { + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { @Override - ItemStack getOrDefault() { - if (!ContainerSlotCrafting.isAvailable(holder)) { + public ItemStack getOrDefault() { + if (!ContentCrafting.isAvailable(holder)) { return Placeholders.survivalOnly(holder); } InventoryMenu inventoryMenu = holder.inventoryMenu; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCursor.java similarity index 86% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCursor.java index 33b83df6..19694fda 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotCursor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCursor.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; @@ -11,11 +12,11 @@ /** * A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode. */ -class ContainerSlotCursor implements ContainerSlot { +public class ContentCursor implements Content { private @NotNull ServerPlayer holder; - ContainerSlotCursor(@NotNull ServerPlayer holder) { + public ContentCursor(@NotNull ServerPlayer holder) { this.holder = holder; } @@ -66,8 +67,8 @@ private boolean isAvailable() { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotCursor(container, index, x, y); + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotCursor(container, slot, x, y); } @Override @@ -76,14 +77,14 @@ public InventoryType.SlotType getSlotType() { return InventoryType.SlotType.OUTSIDE; } - class SlotCursor extends MenuSlotPlaceholder { + public class SlotCursor extends SlotPlaceholder { private SlotCursor(Container container, int index, int x, int y) { super(container, index, x, y); } @Override - ItemStack getOrDefault() { + public ItemStack getOrDefault() { if (!isAvailable()) { return Placeholders.survivalOnly(holder); } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentDrop.java similarity index 78% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentDrop.java index e7fb80c5..45594a06 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotDrop.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentDrop.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; @@ -10,11 +11,11 @@ /** * A fake slot used to drop items. Unavailable offline. */ -class ContainerSlotDrop implements ContainerSlot { +public class ContentDrop implements Content { private ServerPlayer holder; - ContainerSlotDrop(@NotNull ServerPlayer holder) { + public ContentDrop(@NotNull ServerPlayer holder) { this.holder = holder; } @@ -44,8 +45,8 @@ public void set(ItemStack itemStack) { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotDrop(container, index, x, y); + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotDrop(container, slot, x, y); } @Override @@ -54,14 +55,14 @@ public InventoryType.SlotType getSlotType() { return InventoryType.SlotType.OUTSIDE; } - class SlotDrop extends MenuSlotPlaceholder { + public class SlotDrop extends SlotPlaceholder { private SlotDrop(Container container, int index, int x, int y) { super(container, index, x, y); } @Override - ItemStack getOrDefault() { + public ItemStack getOrDefault() { return holder.connection != null && !holder.connection.isDisconnected() ? Placeholders.drop : Placeholders.blockedOffline; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentEquipment.java similarity index 73% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentEquipment.java index 99f8b39d..2ba44873 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotEquipment.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentEquipment.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; @@ -11,12 +12,12 @@ /** * A slot for equipment that displays placeholders if empty. */ -class ContainerSlotEquipment extends ContainerSlotList { +public class ContentEquipment extends ContentList { private final ItemStack placeholder; private final EquipmentSlot equipmentSlot; - ContainerSlotEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { + public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { super(holder, index, InventoryType.SlotType.ARMOR); placeholder = switch (equipmentSlot) { case HEAD -> Placeholders.emptyHelmet; @@ -34,11 +35,11 @@ public void setHolder(@NotNull ServerPlayer holder) { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotEquipment(container, index, x, y); + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y); } - class SlotEquipment extends MenuSlotPlaceholder { + public class SlotEquipment extends SlotPlaceholder { private ServerPlayer viewer; @@ -47,7 +48,7 @@ class SlotEquipment extends MenuSlotPlaceholder { } @Override - ItemStack getOrDefault() { + public ItemStack getOrDefault() { ItemStack itemStack = getItem(); if (!itemStack.isEmpty()) { return itemStack; @@ -55,11 +56,11 @@ ItemStack getOrDefault() { return placeholder; } - EquipmentSlot getEquipmentSlot() { + public EquipmentSlot getEquipmentSlot() { return equipmentSlot; } - void onlyEquipmentFor(ServerPlayer viewer) { + public void onlyEquipmentFor(ServerPlayer viewer) { this.viewer = viewer; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentList.java similarity index 75% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentList.java index 15c0e069..980c0b12 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotList.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentList.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -12,13 +12,13 @@ /** * A normal slot backed by an item list. */ -abstract class ContainerSlotList implements ContainerSlot { +public abstract class ContentList implements Content { private final int index; private final InventoryType.SlotType slotType; - List items; + protected List items; - ContainerSlotList(ServerPlayer holder, int index, InventoryType.SlotType slotType) { + public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) { this.index = index; this.slotType = slotType; setHolder(holder); @@ -46,8 +46,8 @@ public void set(ItemStack itemStack) { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new Slot(container, index, x, y); + public Slot asSlot(Container container, int slot, int x, int y) { + return new Slot(container, slot, x, y); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentOffHand.java similarity index 75% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentOffHand.java index 556203f5..f8872900 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/ContainerSlotOffHand.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentOffHand.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -10,11 +10,11 @@ /** * A slot for equipment that updates held items if necessary. */ -class ContainerSlotOffHand extends ContainerSlotEquipment { +public class ContentOffHand extends ContentEquipment { private ServerPlayer holder; - public ContainerSlotOffHand(ServerPlayer holder, int localIndex) { + public ContentOffHand(ServerPlayer holder, int localIndex) { super(holder, localIndex, EquipmentSlot.OFFHAND); } @@ -30,8 +30,8 @@ public InventoryType.SlotType getSlotType() { } @Override - public Slot asMenuSlot(Container container, int index, int x, int y) { - return new SlotEquipment(container, index, x, y) { + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y) { @Override public void setChanged() { if (holder.connection != null diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java new file mode 100644 index 00000000..b7dc9a1a --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java @@ -0,0 +1,56 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A view-only slot that can't be interacted with. + */ +public class ContentViewOnly implements Content { + + @NotNull ServerPlayer holder; + + public ContentViewOnly(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack removePartial(int amount) { + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + this.holder.drop(itemStack, false); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.OUTSIDE; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotPlaceholder.java similarity index 61% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotPlaceholder.java index b29afdc5..315b2b5c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/MenuSlotPlaceholder.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotPlaceholder.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; @@ -9,12 +9,12 @@ * *

Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.

*/ -abstract class MenuSlotPlaceholder extends Slot { +public abstract class SlotPlaceholder extends Slot { - MenuSlotPlaceholder(Container container, int index, int x, int y) { + public SlotPlaceholder(Container container, int index, int x, int y) { super(container, index, x, y); } - abstract ItemStack getOrDefault(); + public abstract ItemStack getOrDefault(); } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java new file mode 100644 index 00000000..5fa9edd5 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java @@ -0,0 +1,151 @@ +package com.lishid.openinv.internal.v1_21_R1.inventory.slot; + +import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content. + */ +public class SlotViewOnly extends SlotPlaceholder { + + public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) { + SlotViewOnly wrapper; + if (wrapped instanceof SlotPlaceholder placeholder) { + wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) { + @Override + public ItemStack getOrDefault() { + return placeholder.getOrDefault(); + } + }; + } else { + wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) { + @Override + public ItemStack getOrDefault() { + return wrapped.getItem(); + } + }; + } + wrapper.index = wrapped.index; + return wrapper; + } + + public SlotViewOnly(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + return Placeholders.notSlot; + } + + @Override + public void onQuickCraft(ItemStack var0, ItemStack var1) { + } + + @Override + public void onTake(Player var0, ItemStack var1) { + } + + @Override + public boolean mayPlace(ItemStack var0) { + return false; + } + + @Override + public ItemStack getItem() { + return ItemStack.EMPTY; + } + + @Override + public boolean hasItem() { + return false; + } + + @Override + public void setByPlayer(ItemStack newStack) { + } + + @Override + public void setByPlayer(ItemStack newStack, ItemStack oldStack) { + } + + @Override + public void set(ItemStack var0) { + } + + @Override + public void setChanged() { + } + + @Override + public int getMaxStackSize() { + return 0; + } + + @Override + public int getMaxStackSize(ItemStack itemStack) { + return 0; + } + + @Override + public ItemStack remove(int amount) { + return ItemStack.EMPTY; + } + + @Override + public boolean mayPickup(Player var0) { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public Optional tryRemove(int var0, int var1, Player var2) { + return Optional.empty(); + } + + @Override + public ItemStack safeTake(int var0, int var1, Player var2) { + return ItemStack.EMPTY; + } + + @Override + public ItemStack safeInsert(ItemStack itemStack) { + return itemStack; + } + + @Override + public ItemStack safeInsert(ItemStack itemStack, int amount) { + return itemStack; + } + + @Override + public boolean allowModification(Player var0) { + return false; + } + + @Override + public int getContainerSlot() { + return this.slot; + } + + @Override + public boolean isHighlightable() { + return false; + } + + @Override + public boolean isFake() { + return true; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java similarity index 89% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java index ac07285d..cae26fef 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/OpenPlayer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java @@ -1,20 +1,4 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_21_R1; +package com.lishid.openinv.internal.v1_21_R1.player; import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java index e40eade4..6fd0d9ec 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/PlayerManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java @@ -1,20 +1,4 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_21_R1; +package com.lishid.openinv.internal.v1_21_R1.player; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.v1_21_R1.inventory.OpenEnderChest; diff --git a/plugin/pom.xml b/plugin/pom.xml index 381a5018..ff10c880 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -80,6 +80,14 @@ maven-shade-plugin + + + package + + shade + + + From bb80d9351029d6f51c218c566e5cf629255b007d Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 11 Jul 2024 20:25:45 -0400 Subject: [PATCH 190/340] Improve PlayerInventory implementation (#222) * Fix storage contents including other items * Fix regular contents including fake slots * Fix type not being PLAYER * Prefer slot indices to magic constants --- .../v1_21_R1/inventory/OpenContainerMenu.java | 1 + .../v1_21_R1/inventory/OpenInventoryMenu.java | 3 +- .../inventory/OpenPlayerInventory.java | 92 +++++++++++++++---- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java index 005f2ad2..3b4b0e0d 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java @@ -128,6 +128,7 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { return true; } + // Overrides from here on are purely to modify the sync process to send placeholder items. @Override protected Slot addSlot(Slot slot) { slot.index = this.slots.size(); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java index 3e3940cd..7b0a8e13 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java @@ -160,9 +160,8 @@ public boolean isInTop(int rawSlot) { return rawSlot < topSize; } - @NotNull @Override - public InventoryType.SlotType getSlotType(int slot) { + public @NotNull InventoryType.SlotType getSlotType(int slot) { if (slot < 0) { return InventoryType.SlotType.OUTSIDE; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java index e7b54edb..3a11ab19 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java @@ -1,10 +1,13 @@ package com.lishid.openinv.internal.v1_21_R1.inventory; +import com.google.common.base.Preconditions; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; -import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.jetbrains.annotations.NotNull; @@ -21,6 +24,46 @@ public OpenInventory getInventory() { return (OpenInventory) super.getInventory(); } + @Override + public ItemStack[] getContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents()); + } + + @Override + public void setContents(ItemStack[] items) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + int size = internal.getContainerSize(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + + for (int index = 0; index < size; ++index) { + if (index < items.length) { + internal.setItem(index, CraftItemStack.asNMSCopy(items[index])); + } else { + internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY); + } + } + } + + @Override + public ItemStack[] getStorageContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().items); + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + NonNullList list = getInventory().getOwnerHandle().getInventory().items; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull InventoryType getType() { + return InventoryType.PLAYER; + } + @Override public @NotNull Player getHolder() { return getInventory().getOwner(); @@ -33,8 +76,11 @@ public OpenInventory getInventory() { @Override public void setArmorContents(@Nullable ItemStack[] items) { + NonNullList list = getInventory().getOwnerHandle().getInventory().armor; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); for (int index = 0; index < items.length; ++index) { - getInventory().getOwnerHandle().getInventory().armor.set(index, CraftItemStack.asNMSCopy(items[index])); + list.set(index, CraftItemStack.asNMSCopy(items[index])); } } @@ -45,49 +91,60 @@ public void setArmorContents(@Nullable ItemStack[] items) { @Override public void setExtraContents(@Nullable ItemStack[] items) { + NonNullList list = getInventory().getOwnerHandle().getInventory().offhand; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); for (int index = 0; index < items.length; ++index) { - getInventory().getOwnerHandle().getInventory().offhand.set(index, CraftItemStack.asNMSCopy(items[index])); + list.set(index, CraftItemStack.asNMSCopy(items[index])); } } @Override public @Nullable ItemStack getHelmet() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(3)); + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.HEAD.getIndex())); } @Override public void setHelmet(@Nullable ItemStack helmet) { - getInventory().getOwnerHandle().getInventory().armor.set(3, CraftItemStack.asNMSCopy(helmet)); + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet)); } @Override public @Nullable ItemStack getChestplate() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(2)); + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.HEAD.getIndex())); } @Override public void setChestplate(@Nullable ItemStack chestplate) { - getInventory().getOwnerHandle().getInventory().armor.set(2, CraftItemStack.asNMSCopy(chestplate)); + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate)); } @Override public @Nullable ItemStack getLeggings() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(1)); + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.LEGS.getIndex())); } @Override public void setLeggings(@Nullable ItemStack leggings) { - getInventory().getOwnerHandle().getInventory().armor.set(1, CraftItemStack.asNMSCopy(leggings)); + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings)); } @Override public @Nullable ItemStack getBoots() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmor(0)); + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.FEET.getIndex())); } @Override public void setBoots(@Nullable ItemStack boots) { - getInventory().getOwnerHandle().getInventory().armor.set(0, CraftItemStack.asNMSCopy(boots)); + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots)); } @Override @@ -104,7 +161,7 @@ public void setItemInMainHand(@Nullable ItemStack item) { @Override public @NotNull ItemStack getItemInOffHand() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.get(0)); + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst()); } @Override @@ -130,15 +187,12 @@ public int getHeldItemSlot() { @Override public void setHeldItemSlot(int slot) { - Inventory internal = getInventory().getOwnerHandle().getInventory(); - if (slot < internal.items.size()) { - slot += internal.items.size() - 9; - } - internal.selected = slot; + slot %= 9; + getInventory().getOwnerHandle().getInventory().selected = slot; } @Override - public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) { + public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { return switch (slot) { case HAND -> getItemInMainHand(); case OFF_HAND -> getItemInOffHand(); @@ -151,7 +205,7 @@ public void setHeldItemSlot(int slot) { } @Override - public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) { + public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) { switch (slot) { case HAND -> setItemInMainHand(item); case OFF_HAND -> setItemInOffHand(item); From 59fff35fcc1ed71ff9137f79da54cdaba3e83559 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 12 Jul 2024 10:32:41 -0400 Subject: [PATCH 191/340] Improve language manager (#223) * Add fallthrough to "parent" language * I am aware that the "correct" parent for Portuguese is likely `pt_pt`, but as no one has submitted it, welcome to Brazil. * Use English for lowercasing locale name * Fixes potential issues locating translations for certain languages on systems in other languages * Migrate locales to "locale" subdirectory to reduce clutter * Fix container settings getting clobbered/not suggested correctly/not editable * This was due to a quirk of the YAML parser - OpenInv was looking for `'on'` and `'off'` but translation files provided `'true'` and `'false'` due to the paths being interpreted as truthy. The built-in translations have had their paths quoted to fix this. You will need to manually relocate these if you have custom translations. --- .../openinv/util/lang/LanguageManager.java | 94 +++++++++++++------ .../main/java/com/lishid/openinv/OpenInv.java | 9 +- .../com/lishid/openinv/util/LangMigrator.java | 87 +++++++++++++++++ .../resources/locale/{de_de.yml => de.yml} | 4 +- .../resources/locale/{en_us.yml => en.yml} | 4 +- .../resources/locale/{es_es.yml => es.yml} | 4 +- .../resources/locale/{pt_br.yml => pt.yml} | 4 +- plugin/src/main/resources/locale/zh_cn.yml | 4 +- plugin/src/main/resources/locale/zh_tw.yml | 4 +- 9 files changed, 174 insertions(+), 40 deletions(-) create mode 100644 plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java rename plugin/src/main/resources/locale/{de_de.yml => de.yml} (97%) rename plugin/src/main/resources/locale/{en_us.yml => en.yml} (97%) rename plugin/src/main/resources/locale/{es_es.yml => es.yml} (97%) rename plugin/src/main/resources/locale/{pt_br.yml => pt.yml} (97%) diff --git a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java index 6e5fe207..49dc55ac 100644 --- a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java +++ b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -50,6 +50,7 @@ public class LanguageManager { private final Plugin plugin; + private final File folder; private final String defaultLocale; private final Map locales; @@ -57,6 +58,17 @@ public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) { this.plugin = plugin; this.defaultLocale = defaultLocale; this.locales = new HashMap<>(); + this.folder = new File(plugin.getDataFolder(), "locale"); + + if (!folder.exists() && !folder.mkdirs()) { + plugin.getLogger().warning(() -> "Unable to create " + folder.getPath() + "! Languages may not be editable."); + } + + reload(); + } + + public void reload() { + this.locales.clear(); getOrLoadLocale(defaultLocale); } @@ -66,57 +78,86 @@ public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) { return loaded; } - File file = new File(plugin.getDataFolder(), locale + ".yml"); + LangLocation lang = bestMatch(locale, null); + + // If a parent was a better match, check if it is already loaded. + if (!locale.equals(lang.locale)) { + loaded = locales.get(lang.locale); + if (loaded != null) { + locales.put(locale, loaded); + return loaded; + } + } // Load locale config from disk and bundled locale defaults. - YamlConfiguration localeConfig = loadLocale(locale, file); + YamlConfiguration localeConfig = loadLocale(lang); // If the locale is not the default locale, also handle any missing translations from the default locale. if (!locale.equals(defaultLocale)) { - addTranslationFallthrough(locale, localeConfig, file); + addTranslationFallthrough(lang, localeConfig); if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true) && localeConfig.isConfigurationSection("guess")) { // Warn that guess section exists. This should run once per language per server restart // when accessed by a user to hint to server owners that they can make UX improvements. - plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + locale + ".yml! Check the guess section!"); + plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + lang.locale + ".yml! Check the guess section!"); } } locales.put(locale, localeConfig); + locales.put(lang.locale, localeConfig); + return localeConfig; } - private @NotNull YamlConfiguration loadLocale( - @NotNull String locale, - @NotNull File file) { - // Load defaults from the plugin's bundled resources. - InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml"); + private @NotNull LangLocation bestMatch(@NotNull String locale, @Nullable LangLocation initial) { + File file = new File(folder, locale + ".yml"); + InputStream bundled = plugin.getResource("locale/" + locale + ".yml"); + + if (file.exists() || bundled != null) { + return new LangLocation(locale, file, bundled); + } + + if (initial == null) { + initial = new LangLocation(locale, file, null); + } + + int lastSeparator = locale.lastIndexOf('_'); + + // Must be at least some content before separator. + if (lastSeparator < 1) { + return initial; + } + + return bestMatch(locale.substring(0, lastSeparator), initial); + } + + private @NotNull YamlConfiguration loadLocale(@NotNull LangLocation lang) { YamlConfiguration localeConfigDefaults; - if (resourceStream == null) { + if (lang.bundled == null) { localeConfigDefaults = new YamlConfiguration(); } else { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream, StandardCharsets.UTF_8))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(lang.bundled, StandardCharsets.UTF_8))) { localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + locale + ".yml"); + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + lang.locale + ".yml"); localeConfigDefaults = new YamlConfiguration(); } } - if (!file.exists()) { + if (!lang.file.exists()) { // If the file does not exist on disk, save bundled defaults. try { - localeConfigDefaults.save(file); + localeConfigDefaults.save(lang.file); } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml"); + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml"); } // Return loaded bundled locale. return localeConfigDefaults; } // If the file does exist on disk, load it. - YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(file); + YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(lang.file); // Check for missing translations from the bundled file. List newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet); @@ -142,22 +183,19 @@ public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) { localeConfig.set("guess", null); } - plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + locale + ".yml: " + String.join(", ", newKeys)); + plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + lang.locale + ".yml: " + String.join(", ", newKeys)); // Write new keys to disk. try { - localeConfig.save(file); + localeConfig.save(lang.file); } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml"); + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml"); } return localeConfig; } - private void addTranslationFallthrough( - @NotNull String locale, - @NotNull YamlConfiguration localeConfig, - @NotNull File file) { + private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull YamlConfiguration localeConfig) { YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale); // Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write. @@ -173,9 +211,9 @@ private void addTranslationFallthrough( // Write modified guess section to disk. try { - localeConfig.save(file); + localeConfig.save(location.file); } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml"); + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + location.locale + ".yml"); } } @@ -197,8 +235,8 @@ private void addTranslationFallthrough( } public @Nullable String getValue(@NotNull String key, @Nullable String locale) { - String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ROOT)).getString(key); - if (value == null || value.isEmpty()) { + String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ENGLISH)).getString(key); + if (value == null || value.isBlank()) { return null; } @@ -276,4 +314,6 @@ public void sendSystemMessage(@NotNull Player player, @NotNull String key) { player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message)); } + private record LangLocation(@NotNull String locale, @NotNull File file, @Nullable InputStream bundled) {} + } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index f6c9f160..b8ad1ad9 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -32,6 +32,7 @@ import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.ConfigUpdater; import com.lishid.openinv.util.InternalAccessor; +import com.lishid.openinv.util.LangMigrator; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.StringMetric; import com.lishid.openinv.util.lang.LanguageManager; @@ -51,6 +52,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; @@ -83,6 +85,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { @Override public void reloadConfig() { super.reloadConfig(); + languageManager.reload(); this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; if (this.accessor != null && this.accessor.isSupported()) { this.accessor.reload(this.getConfig()); @@ -128,7 +131,11 @@ public void onEnable() { // Save default configuration if not present. this.saveDefaultConfig(); - this.languageManager = new LanguageManager(this, "en_us"); + // Migrate locale files to a subfolder. + Path dataFolder = getDataFolder().toPath(); + new LangMigrator(dataFolder, dataFolder.resolve("locale"), getLogger()).migrate(); + + this.languageManager = new LanguageManager(this, "en"); this.accessor = new InternalAccessor(getLogger(), languageManager); try { diff --git a/plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java b/plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java new file mode 100644 index 00000000..348e0d5a --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java @@ -0,0 +1,87 @@ +package com.lishid.openinv.util; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class LangMigrator { + + private final @NotNull Path oldFolder; + private final @NotNull Path newFolder; + private final @NotNull Logger logger; + + public LangMigrator(@NotNull Path oldFolder, @NotNull Path newFolder, @NotNull Logger logger) { + this.oldFolder = oldFolder; + this.newFolder = newFolder; + this.logger = logger; + } + + public void migrate() { + if (!Files.exists(oldFolder.resolve("en_us.yml"))) { + // Probably already migrated. + return; + } + + logger.info(() -> String.format("[LanguageManager] Migrating language files to %s", newFolder)); + + if (!Files.exists(newFolder)) { + try { + Files.createDirectories(newFolder); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to create language subdirectory!", e); + } + } + + try (DirectoryStream files = Files.newDirectoryStream(oldFolder)) { + files.forEach(path -> { + if (path == null) { + return; + } + + String fileName = path.getFileName().toString(); + + if (fileName.startsWith("config") || !fileName.endsWith(".yml")) { + return; + } + + // Migrate certain files to be parent languages. + fileName = switch (fileName) { + case "en_us.yml" -> "en.yml"; + case "de_de.yml" -> "de.yml"; + case "es_es.yml" -> "es.yml"; + case "pt_br.yml" -> "pt.yml"; + default -> fileName; + }; + + try { + Files.copy(path, newFolder.resolve(fileName)); + Files.delete(path); + } catch (FileAlreadyExistsException e1) { + // File already migrated? + try { + Files.copy(path, newFolder.resolve("old_" + fileName)); + Files.delete(path); + } catch (IOException e2) { + // If it fails again, just re-throw. + throw new UncheckedIOException(e2); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } catch (UncheckedIOException e) { + logger.log(Level.WARNING, "Unable to migrate languages to subdirectory!", e.getCause()); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to migrate languages to subdirectory!", e); + } + + } + +} diff --git a/plugin/src/main/resources/locale/de_de.yml b/plugin/src/main/resources/locale/de.yml similarity index 97% rename from plugin/src/main/resources/locale/de_de.yml rename to plugin/src/main/resources/locale/de.yml index ea1d0dce..9a00b1db 100644 --- a/plugin/src/main/resources/locale/de_de.yml +++ b/plugin/src/main/resources/locale/de.yml @@ -23,8 +23,8 @@ messages: container: noMatches: 'Keine Container mit %target% gefunden.' matches: 'Container hat %target%: %detail%' - on: 'an' - off: 'aus' + 'on': 'an' + 'off': 'aus' container: player: '%player%''s Inventar' enderchest: '%player%''s Endertruhe' diff --git a/plugin/src/main/resources/locale/en_us.yml b/plugin/src/main/resources/locale/en.yml similarity index 97% rename from plugin/src/main/resources/locale/en_us.yml rename to plugin/src/main/resources/locale/en.yml index 829a0e3c..37ec8449 100644 --- a/plugin/src/main/resources/locale/en_us.yml +++ b/plugin/src/main/resources/locale/en.yml @@ -23,8 +23,8 @@ messages: container: noMatches: 'No containers found with %target%.' matches: 'Containers holding %target%: %detail%' - on: 'on' - off: 'off' + 'on': 'on' + 'off': 'off' container: player: '%player%''s Inventory' enderchest: '%player%''s Ender Chest' diff --git a/plugin/src/main/resources/locale/es_es.yml b/plugin/src/main/resources/locale/es.yml similarity index 97% rename from plugin/src/main/resources/locale/es_es.yml rename to plugin/src/main/resources/locale/es.yml index 41541fae..05d45dcf 100644 --- a/plugin/src/main/resources/locale/es_es.yml +++ b/plugin/src/main/resources/locale/es.yml @@ -23,8 +23,8 @@ messages: container: noMatches: 'No se encontraron contenedores con %target%.' matches: 'Contenedores con %target%: %detail%' - on: 'activado' - off: 'desactivado' + 'on': 'activado' + 'off': 'desactivado' container: player: 'Inventario de %player%' enderchest: 'Cofre de Ender de %player%' diff --git a/plugin/src/main/resources/locale/pt_br.yml b/plugin/src/main/resources/locale/pt.yml similarity index 97% rename from plugin/src/main/resources/locale/pt_br.yml rename to plugin/src/main/resources/locale/pt.yml index cd26a2d7..4d64c43e 100644 --- a/plugin/src/main/resources/locale/pt_br.yml +++ b/plugin/src/main/resources/locale/pt.yml @@ -23,8 +23,8 @@ messages: container: noMatches: 'Nenhum recipiente encontrado com %target%.' matches: 'Recipientes contendo %target%: %detail%' - on: 'ligado' - off: 'desligado' + 'on': 'ligado' + 'off': 'desligado' container: player: 'Inventario de %player%' enderchest: 'Bau de Ender de %player%' diff --git a/plugin/src/main/resources/locale/zh_cn.yml b/plugin/src/main/resources/locale/zh_cn.yml index bec0185c..aa696f19 100644 --- a/plugin/src/main/resources/locale/zh_cn.yml +++ b/plugin/src/main/resources/locale/zh_cn.yml @@ -24,8 +24,8 @@ messages: container: noMatches: 找不到放有 %target% 的储物箱。 matches: '找到放有 %target% 的储物箱 : %detail%' - 'true': '开启' - 'false': '关闭' + 'on': '开启' + 'off': '关闭' container: player: '%player% 的物品栏' enderchest: '%player% 的末影箱' diff --git a/plugin/src/main/resources/locale/zh_tw.yml b/plugin/src/main/resources/locale/zh_tw.yml index c5e7399d..2a798241 100644 --- a/plugin/src/main/resources/locale/zh_tw.yml +++ b/plugin/src/main/resources/locale/zh_tw.yml @@ -24,8 +24,8 @@ messages: container: noMatches: 找不到放有 %target% 的儲物箱。 matches: '找到放有 %target% 的儲物箱 : %detail%' - 'true': '開啟' - 'false': '關閉' + 'on': '開啟' + 'off': '關閉' container: player: '%player% 的物品欄' enderchest: '%player% 的終界箱' From 1f2c2b6a499632a927eadfefddb9c49d1f5a5d33 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 12 Jul 2024 10:40:35 -0400 Subject: [PATCH 192/340] Refactor new internals slightly Repackage inventory -> container (because internally Inventory is a player container) Old classes -> 2 spaces Will eventually update project style and add a .editorconfig, but not right now. --- .../internal/v1_21_R1/InternalAccessor.java | 8 +- .../AnySilentContainer.java | 2 +- .../OpenContainerMenu.java | 4 +- .../OpenEnderChest.java | 2 +- .../OpenEnderChestMenu.java | 4 +- .../OpenInventory.java | 22 +- .../OpenInventoryMenu.java | 8 +- .../OpenPlayerInventory.java | 2 +- .../Placeholders.java | 2 +- .../slot/Content.java | 2 +- .../slot/ContentCrafting.java | 4 +- .../slot/ContentCraftingResult.java | 4 +- .../slot/ContentCursor.java | 4 +- .../slot/ContentDrop.java | 4 +- .../slot/ContentEquipment.java | 4 +- .../slot/ContentList.java | 2 +- .../slot/ContentOffHand.java | 2 +- .../slot/ContentViewOnly.java | 2 +- .../slot/SlotPlaceholder.java | 2 +- .../slot/SlotViewOnly.java | 4 +- .../internal/v1_21_R1/player/OpenPlayer.java | 280 ++++++------- .../v1_21_R1/player/PlayerManager.java | 394 +++++++++--------- 22 files changed, 381 insertions(+), 381 deletions(-) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/AnySilentContainer.java (99%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/OpenContainerMenu.java (98%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/OpenEnderChest.java (98%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/OpenEnderChestMenu.java (96%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/OpenInventory.java (94%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/OpenInventoryMenu.java (97%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/OpenPlayerInventory.java (99%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/Placeholders.java (99%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/Content.java (96%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentCrafting.java (96%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentCraftingResult.java (91%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentCursor.java (96%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentDrop.java (93%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentEquipment.java (94%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentList.java (95%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentOffHand.java (95%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/ContentViewOnly.java (95%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/SlotPlaceholder.java (89%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/{inventory => container}/slot/SlotViewOnly.java (95%) diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java index a612f41e..94588f9d 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java @@ -5,10 +5,10 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.v1_21_R1.inventory.AnySilentContainer; -import com.lishid.openinv.internal.v1_21_R1.inventory.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.AnySilentContainer; +import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java similarity index 99% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/AnySilentContainer.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java index a7c0211c..05fb013a 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java similarity index 98% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java index 3b4b0e0d..347cce48 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenContainerMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java @@ -1,7 +1,7 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; import com.google.common.base.Suppliers; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotPlaceholder; +import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotPlaceholder; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.world.inventory.AbstractContainerMenu; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java similarity index 98% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java index 55ca749c..6959e8f4 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChest.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java index dd333d5d..7bfb4b3a 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenEnderChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; import com.lishid.openinv.util.Permissions; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java similarity index 94% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java index 3f6c907f..bec7a8a5 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java @@ -1,17 +1,17 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.Content; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentCrafting; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentCraftingResult; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentCursor; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentList; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentOffHand; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentViewOnly; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R1.container.slot.Content; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCrafting; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCraftingResult; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCursor; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentList; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentOffHand; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java similarity index 97% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java index 7b0a8e13..e9a1ff11 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java @@ -1,8 +1,8 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R1.inventory.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; import com.lishid.openinv.util.Permissions; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EquipmentSlot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java similarity index 99% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java index 3a11ab19..5295a625 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/OpenPlayerInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; import com.google.common.base.Preconditions; import net.minecraft.core.NonNullList; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/Placeholders.java similarity index 99% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/Placeholders.java index 16fc3269..c5f35d2d 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/Placeholders.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/Placeholders.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory; +package com.lishid.openinv.internal.v1_21_R1.container; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/Content.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/Content.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/Content.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/Content.java index eed38f34..d63536f0 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/Content.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/Content.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCrafting.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCrafting.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCrafting.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCrafting.java index 5edd1ef0..3f2bdb98 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCrafting.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCrafting.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCraftingResult.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCraftingResult.java similarity index 91% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCraftingResult.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCraftingResult.java index 4a26a543..54fb0cdc 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCraftingResult.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCraftingResult.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.InventoryMenu; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCursor.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCursor.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCursor.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCursor.java index 19694fda..5401daca 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentCursor.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCursor.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentDrop.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentDrop.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentDrop.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentDrop.java index 45594a06..9a083b33 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentDrop.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentDrop.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentEquipment.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentEquipment.java similarity index 94% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentEquipment.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentEquipment.java index 2ba44873..662d4d67 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentEquipment.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentEquipment.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentList.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentList.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentList.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentList.java index 980c0b12..48e14ced 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentList.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentList.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentOffHand.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentOffHand.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentOffHand.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentOffHand.java index f8872900..7b319041 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentOffHand.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentOffHand.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentViewOnly.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentViewOnly.java index b7dc9a1a..5dec5b32 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/ContentViewOnly.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentViewOnly.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotPlaceholder.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotPlaceholder.java similarity index 89% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotPlaceholder.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotPlaceholder.java index 315b2b5c..c7a53a3a 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotPlaceholder.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotPlaceholder.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotViewOnly.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotViewOnly.java index 5fa9edd5..93975d51 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/inventory/slot/SlotViewOnly.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotViewOnly.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.inventory.slot; +package com.lishid.openinv.internal.v1_21_R1.container.slot; -import com.lishid.openinv.internal.v1_21_R1.inventory.Placeholders; +import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java index cae26fef..c90d4e1e 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java @@ -21,157 +21,157 @@ public class OpenPlayer extends CraftPlayer { - /** - * List of tags to always reset when saving. - * - * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) - * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - */ - private static final Set RESET_TAGS = Set.of( - // Entity#saveWithoutId(CompoundTag) - "CustomName", - "CustomNameVisible", - "Silent", - "NoGravity", - "Glowing", - "TicksFrozen", - "HasVisualFire", - "Tags", - "Passengers", - // ServerPlayer#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - "warden_spawn_tracker", - "enteredNetherPosition", - "SpawnX", - "SpawnY", - "SpawnZ", - "SpawnForced", - "SpawnAngle", - "SpawnDimension", - "raid_omen_position", - // Player#addAdditionalSaveData(CompoundTag) - "ShoulderEntityLeft", - "ShoulderEntityRight", - "LastDeathLocation", - "current_explosion_impact_pos", - // LivingEntity#addAdditionalSaveData(CompoundTag) - "active_effects", - "SleepingX", - "SleepingY", - "SleepingZ", - "Brain" - ); - - private final PlayerManager manager; - - OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { - super(server, entity); - this.manager = manager; + /** + * List of tags to always reset when saving. + * + * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) + * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + */ + private static final Set RESET_TAGS = Set.of( + // Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + "raid_omen_position", + // Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + "current_explosion_impact_pos", + // LivingEntity#addAdditionalSaveData(CompoundTag) + "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + + private final PlayerManager manager; + + OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { + super(server, entity); + this.manager = manager; + } + + @Override + public void loadData() { + manager.loadData(getHandle()); + } + + @Override + public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; } - @Override - public void loadData() { - manager.loadData(getHandle()); + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); + setExtraData(playerData); + + if (oldData != null) { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + } + + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, tempFile, backupFile); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } + } - @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); - CompoundTag playerData = getWritableTag(oldData); - playerData = player.saveWithoutId(playerData); - setExtraData(playerData); - - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, tempFile); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, tempFile, backupFile); - } catch (Exception e) { - LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); - } + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + if (oldData == null) { + return new CompoundTag(); } - @Contract("null -> new") - private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { - if (oldData == null) { - return new CompoundTag(); - } - - // Copy old data. This is a deep clone, so operating on it should be safe. - oldData = oldData.copy(); - - // Remove vanilla/server data that is not written every time. - oldData.getAllKeys() - .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); - - return oldData; + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @SuppressWarnings("SameParameterValue") @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { + return; } - private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { - // Revert automatic updates to play timestamps. - copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); - } + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } - private void copyValue( - @NotNull CompoundTag source, - @NotNull CompoundTag target, - @NotNull String container, - @NotNull String key, - @NotNull Class tagType) { - CompoundTag oldContainer = getTag(source, container, CompoundTag.class); - CompoundTag newContainer = getTag(target, container, CompoundTag.class); - - // New container being null means the server implementation doesn't store this data. - if (newContainer == null) { - return; - } - - // If old tag exists, copy it to new location, removing otherwise. - setTag(newContainer, key, getTag(oldContainer, key, tagType)); + private @Nullable T getTag( + @Nullable CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + if (container == null) { + return null; } - - private @Nullable T getTag( - @Nullable CompoundTag container, - @NotNull String key, - @NotNull Class dataType) { - if (container == null) { - return null; - } - Tag value = container.get(key); - if (value == null || !dataType.isAssignableFrom(value.getClass())) { - return null; - } - return dataType.cast(value); + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; } - - private void setTag( - @NotNull CompoundTag container, - @NotNull String key, - @Nullable T data) { - if (data == null) { - container.remove(key); - } else { - container.put(key, data); - } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); } + } } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java index 6fd0d9ec..350270bc 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java @@ -1,8 +1,8 @@ package com.lishid.openinv.internal.v1_21_R1.player; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.v1_21_R1.inventory.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R1.inventory.OpenInventory; +import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -38,235 +38,235 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { - private static boolean paper; + private static boolean paper; static { - try { - Class.forName("io.papermc.paper.configuration.Configuration"); - paper = true; - } catch (ClassNotFoundException ignored) { - paper = false; - } + try { + Class.forName("io.papermc.paper.configuration.Configuration"); + paper = true; + } catch (ClassNotFoundException ignored) { + paper = false; } - - private final @NotNull Logger logger; - private @Nullable Field bukkitEntity; - - public PlayerManager(@NotNull Logger logger) { - this.logger = logger; - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); - logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); - bukkitEntity = null; - } + } + + private final @NotNull Logger logger; + private @Nullable Field bukkitEntity; + + public PlayerManager(@NotNull Logger logger) { + this.logger = logger; + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; } + } - public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - ServerPlayer nmsPlayer = null; + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); - } + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); - } + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } - return nmsPlayer; + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); } - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } + return nmsPlayer; + } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + // Ensure player has data + if (!offline.hasPlayedBefore()) { + return null; + } - if (worldServer == null) { - return null; - } + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); - // Create a new ServerPlayer. - ServerPlayer entity = createNewPlayer(server, worldServer, offline); + if (worldServer == null) { + return null; + } - // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. - entity.getAdvancements().stopListening(); + // Create a new ServerPlayer. + ServerPlayer entity = createNewPlayer(server, worldServer, offline); - // Try to load the player's data. - if (loadData(entity)) { - // If data is loaded successfully, return the Bukkit entity. - return entity.getBukkitEntity(); - } + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); - return null; + // Try to load the player's data. + if (loadData(entity)) { + // If data is loaded successfully, return the Bukkit entity. + return entity.getBukkitEntity(); } - private @NotNull ServerPlayer createNewPlayer( - @NotNull MinecraftServer server, - @NotNull ServerLevel worldServer, - @NotNull final OfflinePlayer offline) { - // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) - // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - - ClientInformation dummyInfo = new ClientInformation( - "en_us", - 1, // Reduce distance just in case. - ChatVisiblity.HIDDEN, // Don't accept chat. - false, - ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, - ServerPlayer.DEFAULT_MAIN_HAND, - true, - false // Don't list in player list (not that this player is in the list anyway). - ); - - ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - logger.log( - java.util.logging.Level.WARNING, - e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); - } - - return entity; + return null; + } + + private @NotNull ServerPlayer createNewPlayer( + @NotNull MinecraftServer server, + @NotNull ServerLevel worldServer, + @NotNull final OfflinePlayer offline) { + // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) + // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + + ClientInformation dummyInfo = new ClientInformation( + "en_us", + 1, // Reduce distance just in case. + ChatVisiblity.HIDDEN, // Don't accept chat. + false, + ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, + ServerPlayer.DEFAULT_MAIN_HAND, + true, + false // Don't list in player list (not that this player is in the list anyway). + ); + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); } - boolean loadData(@NotNull ServerPlayer player) { - // See CraftPlayer#loadData - CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); + return entity; + } + + boolean loadData(@NotNull ServerPlayer player) { + // See CraftPlayer#loadData + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); + + if (loadedData == null) { + // Exceptions with loading are logged by Mojang. + return false; + } - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return false; - } + // Read basic data into the player. + player.load(loadedData); + // Also read "extra" data. + player.readAdditionalSaveData(loadedData); + // Game type settings are also loaded separately. + player.loadGameTypes(loadedData); - // Read basic data into the player. - player.load(loadedData); - // Also read "extra" data. - player.readAdditionalSaveData(loadedData); - // Game type settings are also loaded separately. - player.loadGameTypes(loadedData); + if (paper) { + // Paper: world is not loaded by ServerPlayer#load(CompoundTag). + parseWorld(player, loadedData); + } - if (paper) { - // Paper: world is not loaded by ServerPlayer#load(CompoundTag). - parseWorld(player, loadedData); - } + return true; + } + + private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + // Modern Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(logger::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + return; + } + if (bukkitWorld == null) { + player.spawnIn(null); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); + } - return true; + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; } - private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { - // See PlayerList#placeNewPlayer - World bukkitWorld; - if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { - // Modern Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); - } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { - // Legacy Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); - } else { - // Vanilla player data. - DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(logger::warning) - .map(player.server::getLevel) - // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); - return; - } - if (bukkitWorld == null) { - player.spawnIn(null); - return; - } - player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); + } + + @Override + public @NotNull Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + return player; } + } - private void injectPlayer(ServerPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } + @Override + public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory) { + ServerPlayer player = getHandle(bukkitPlayer); - bukkitEntity.setAccessible(true); + if (player.connection == null) { + return null; + } - bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); + MenuProvider provider; + Component title; + if (inventory instanceof OpenInventory playerInv) { + provider = playerInv; + title = playerInv.getTitle(player); + } else if (inventory instanceof OpenEnderChest enderChest) { + provider = enderChest; + title = enderChest.getDisplayName(); + } else { + return null; } - @Override - public @NotNull Player inject(@NotNull Player player) { - try { - ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { - return openPlayer; - } - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - logger.log( - java.util.logging.Level.WARNING, - e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); - return player; - } + // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) + AbstractContainerMenu menu = provider.createMenu(player.nextContainerCounter(), player.getInventory(), player); + + // Should never happen, player is a ServerPlayer with an active connection. + if (menu == null) { + return null; } - @Override - public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory) { - ServerPlayer player = getHandle(bukkitPlayer); - - if (player.connection == null) { - return null; - } - - MenuProvider provider; - Component title; - if (inventory instanceof OpenInventory playerInv) { - provider = playerInv; - title = playerInv.getTitle(player); - } else if (inventory instanceof OpenEnderChest enderChest) { - provider = enderChest; - title = enderChest.getDisplayName(); - } else { - return null; - } - - // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) - AbstractContainerMenu menu = provider.createMenu(player.nextContainerCounter(), player.getInventory(), player); - - // Should never happen, player is a ServerPlayer with an active connection. - if (menu == null) { - return null; - } - - // Set up title (the whole reason we're not just using Player#openInventory(Inventory)). - // Title can only be set once for a menu, and is set during the open process. - menu.setTitle(title); - - menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); - - // Menu is null if event is cancelled. - if (menu == null) { - return null; - } - - player.containerMenu = menu; - player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); - player.initMenu(menu); - - return menu.getBukkitView(); + // Set up title (the whole reason we're not just using Player#openInventory(Inventory)). + // Title can only be set once for a menu, and is set during the open process. + menu.setTitle(title); + + menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); + + // Menu is null if event is cancelled. + if (menu == null) { + return null; } + player.containerMenu = menu; + player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); + player.initMenu(menu); + + return menu.getBukkitView(); + } + } From 0597d7d9db932f58f09e3bd06e30babcadbda138 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 12 Jul 2024 11:33:35 -0400 Subject: [PATCH 193/340] Improve plugin compatibility (#224) * Yield correct item for click event slot * Improve raw slot conversion --- .../v1_21_R1/container/OpenInventoryMenu.java | 50 ++++++++++++++++++- .../container/OpenPlayerInventorySelf.java | 25 ++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java index e9a1ff11..02317ec3 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java @@ -13,7 +13,10 @@ import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class OpenInventoryMenu extends OpenContainerMenu { @@ -21,15 +24,16 @@ public class OpenInventoryMenu extends OpenContainerMenu { private final ServerPlayer viewer; private final int topSize; private final int offset; + private final boolean ownInv; private CraftInventoryView bukkitEntity; protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) { super(getMenuType(inventory, viewer), i); this.inventory = inventory; this.viewer = viewer; + ownInv = inventory.getOwnerHandle().equals(viewer); int upperRows; - boolean ownInv = inventory.getOwnerHandle().equals(viewer); if (ownInv) { // Disallow duplicate access to own main inventory contents. offset = viewer.getInventory().items.size(); @@ -144,7 +148,14 @@ private static MenuType getMenuType(OpenInventory inventory, ServerPlayer vie @Override public CraftInventoryView getBukkitView() { if (bukkitEntity == null) { - bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), inventory.getBukkitInventory(), this) { + org.bukkit.inventory.Inventory bukkitInventory; + if (ownInv) { + bukkitInventory = new OpenPlayerInventorySelf(inventory, offset); + } else { + bukkitInventory = inventory.getBukkitInventory(); + } + + bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), bukkitInventory, this) { @Override public org.bukkit.inventory.ItemStack getItem(int index) { if (index < 0) { @@ -160,6 +171,36 @@ public boolean isInTop(int rawSlot) { return rawSlot < topSize; } + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (rawSlot < 0) { + return super.getInventory(rawSlot); + } + if (rawSlot > topSize) { + return super.getInventory(offset + rawSlot); + } + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return null; + } + return getTopInventory(); + } + + @Override + public int convertSlot(int rawSlot) { + if (rawSlot < 0) { + return rawSlot; + } + if (rawSlot < topSize) { + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return InventoryView.OUTSIDE; + } + return rawSlot; + } + return super.convertSlot(offset + rawSlot); + } + @Override public @NotNull InventoryType.SlotType getSlotType(int slot) { if (slot < 0) { @@ -170,6 +211,11 @@ public boolean isInTop(int rawSlot) { } return inventory.getSlotType(offset + slot); } + + @Override + public int countSlots() { + return topSize + getBottomInventory().getSize(); + } }; } return bukkitEntity; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java new file mode 100644 index 00000000..a64b61d8 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java @@ -0,0 +1,25 @@ +package com.lishid.openinv.internal.v1_21_R1.container; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class OpenPlayerInventorySelf extends OpenPlayerInventory { + + private final int offset; + + public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) { + super(inventory); + this.offset = offset; + } + + @Override + public ItemStack getItem(int index) { + return super.getItem(offset + index); + } + + @Override + public void setItem(int index, ItemStack item) { + super.setItem(offset + index, item); + } + +} From 7fbb9a9da6c8e48d45a3929ce369bce0a4d40608 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 12 Jul 2024 11:58:36 -0400 Subject: [PATCH 194/340] Only build resource pack on change/release --- .github/workflows/ci.yml | 9 ++------- .github/workflows/draft_release.yml | 10 ++++++++-- .github/workflows/resource_pack_ci.yml | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/resource_pack_ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 168cc55e..7c4f89bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,8 @@ on: - 'master' tags-ignore: - '**' + paths-ignore: + - resource-pack/** # Enable running CI via other Actions, i.e. for drafting releases and handling PRs. workflow_call: @@ -41,10 +43,3 @@ jobs: with: name: api path: ./api/target/openinvapi*.jar - - name: Build resource pack - id: upload-resource-pack - uses: actions/upload-artifact@v4 - with: - name: openinv-legibility-pack - path: ./resource-pack/ - compression-level: 9 diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 364186a6..d62e39b8 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -20,12 +20,18 @@ jobs: - name: Set Release Variables run: bash ./scripts/set_release_env.sh - - name: Download Artifact + - name: Download build uses: actions/download-artifact@v4 with: name: dist path: dist + - name: Package resource pack + run: |- + pushd resource-pack + zip -r ../dist/openinv-legibility-pack.zip . + popd + - name: Create Release id: create-release uses: softprops/action-gh-release@v2.0.6 @@ -36,4 +42,4 @@ jobs: body: ${{ env.GENERATED_CHANGELOG }} draft: true prerelease: false - files: ./dist/OpenInv.jar + files: ./dist/** diff --git a/.github/workflows/resource_pack_ci.yml b/.github/workflows/resource_pack_ci.yml new file mode 100644 index 00000000..557701ac --- /dev/null +++ b/.github/workflows/resource_pack_ci.yml @@ -0,0 +1,24 @@ +name: Resource Pack CI + +on: + push: + branches: + - 'master' + tags-ignore: + - '**' + paths: + - resource-pack/** + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build resource pack + id: upload-resource-pack + uses: actions/upload-artifact@v4 + with: + name: openinv-legibility-pack + path: ./resource-pack/ + compression-level: 9 From 7d63500c79de35a79916cda5f11e45801624cafb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 12 Jul 2024 12:06:35 -0400 Subject: [PATCH 195/340] Bump version to 5.0.0 for release --- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 09cf5b36..3e5c1a31 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.0.0-SNAPSHOT + 5.0.0 openinvapi diff --git a/common/pom.xml b/common/pom.xml index 09389665..db736aad 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.0.0-SNAPSHOT + 5.0.0 openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 53508df9..853f629e 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.0-SNAPSHOT + 5.0.0 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 5e8673a7..80d8f2e9 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.0-SNAPSHOT + 5.0.0 openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index b3aa9c15..d4a6b0e5 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.0-SNAPSHOT + 5.0.0 openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index ff10c880..64174f5e 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.0.0-SNAPSHOT + 5.0.0 openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.0.0-SNAPSHOT + 5.0.0 compile com.lishid openinvadapter1_20_R4 - 5.0.0-SNAPSHOT + 5.0.0 compile com.lishid openinvadapter1_20_R3 - 5.0.0-SNAPSHOT + 5.0.0 compile diff --git a/pom.xml b/pom.xml index c4ea08aa..6b0ded7d 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.0.0-SNAPSHOT + 5.0.0 pom From 09842fd1688a8c813a978207176ff1d99b72f7fb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 12 Jul 2024 12:07:12 -0400 Subject: [PATCH 196/340] Bump version to 5.0.1-SNAPSHOT for development --- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 3e5c1a31..b22bcca8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.0.0 + 5.0.1-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index db736aad..9218e348 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.0.0 + 5.0.1-SNAPSHOT openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 853f629e..9d04b233 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.0 + 5.0.1-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 80d8f2e9..5127d6c8 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.0 + 5.0.1-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index d4a6b0e5..7a14abf0 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.0 + 5.0.1-SNAPSHOT openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 64174f5e..edad9226 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.0.0 + 5.0.1-SNAPSHOT openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.0.0 + 5.0.1-SNAPSHOT compile com.lishid openinvadapter1_20_R4 - 5.0.0 + 5.0.1-SNAPSHOT compile com.lishid openinvadapter1_20_R3 - 5.0.0 + 5.0.1-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index 6b0ded7d..8c2a55a4 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.0.0 + 5.0.1-SNAPSHOT pom From bca080be7c9765b90fe696f5570d91410f6883d3 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 15 Jul 2024 10:01:42 -0400 Subject: [PATCH 197/340] Restrict view-only further (#226) * Top inventory now rejects edits entirely * This prevents potential issues with plugins ignoring cancelled event status and then doing a strange mix of mismatched queries (like getItem into setContents). * It is not feasible/worthwhile to do this with the bottom inventory; many plugins don't use `InventoryView#getBottomInventory`. They use `Player#getInventory` instead. --- .../v1_21_R1/container/OpenContainerMenu.java | 31 +++- .../v1_21_R1/container/OpenEnderChest.java | 4 + .../container/OpenEnderChestMenu.java | 64 ++++--- .../v1_21_R1/container/OpenInventoryMenu.java | 53 +++--- .../container/OpenPlayerInventory.java | 2 +- .../v1_21_R1/container/OpenViewInventory.java | 157 ++++++++++++++++++ 6 files changed, 263 insertions(+), 48 deletions(-) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java index 347cce48..64a42e8c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java @@ -2,9 +2,13 @@ import com.google.common.base.Suppliers; import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotPlaceholder; +import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ClickType; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.inventory.ContainerListener; import net.minecraft.world.inventory.ContainerSynchronizer; @@ -24,6 +28,10 @@ */ public abstract class OpenContainerMenu extends AbstractContainerMenu { + protected final ServerPlayer viewer; + protected final boolean viewOnly; + protected final boolean ownContainer; + // Syncher fields private @Nullable ContainerSynchronizer synchronizer; private final List dataSlots = new ArrayList<>(); private final IntList remoteDataSlots = new IntArrayList(); @@ -31,10 +39,15 @@ public abstract class OpenContainerMenu extends AbstractContainerMenu { private ItemStack remoteCarried = ItemStack.EMPTY; private boolean suppressRemoteUpdates; - protected OpenContainerMenu(@Nullable MenuType containers, int containerCounter) { + protected OpenContainerMenu(@Nullable MenuType containers, int containerCounter,ServerPlayer owner, ServerPlayer viewer) { super(containers, containerCounter); + this.viewer = viewer; + ownContainer = owner.equals(viewer); + viewOnly = checkViewOnly(); } + protected abstract boolean checkViewOnly(); + static @NotNull MenuType getContainers(int inventorySize) { return switch (inventorySize) { case 9 -> MenuType.GENERIC_9x1; @@ -128,16 +141,30 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { return true; } - // Overrides from here on are purely to modify the sync process to send placeholder items. + @Override + public void clicked(int i, int j, ClickType clickType, Player player) { + if (viewOnly) { + if (clickType == ClickType.QUICK_CRAFT) { + sendAllDataToRemote(); + } + return; + } + super.clicked(i, j, clickType, player); + } + @Override protected Slot addSlot(Slot slot) { slot.index = this.slots.size(); + if (viewOnly && !(slot instanceof SlotViewOnly)) { + slot = SlotViewOnly.wrap(slot); + } this.slots.add(slot); this.lastSlots.add(ItemStack.EMPTY); this.remoteSlots.add(ItemStack.EMPTY); return slot; } + // Overrides from here on are purely to modify the sync process to send placeholder items. @Override protected DataSlot addDataSlot(DataSlot dataSlot) { this.dataSlots.add(dataSlot); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java index 6959e8f4..fa038e7e 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java @@ -38,6 +38,10 @@ public OpenEnderChest(@NotNull org.bukkit.entity.Player player) { this.items = owner.getEnderChestInventory().items; } + public @NotNull ServerPlayer getOwnerHandle() { + return owner; + } + @Override public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { if (inventory == null) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java index 7bfb4b3a..cbd27eb9 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java @@ -8,24 +8,21 @@ import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; class OpenEnderChestMenu extends OpenContainerMenu { private final OpenEnderChest enderChest; - private final boolean viewOnly; - private final ServerPlayer viewer; private final int topSize; private CraftInventoryView view; OpenEnderChestMenu(OpenEnderChest enderChest, ServerPlayer viewer, int containerId) { - super(getMenuType(enderChest), containerId); + super(getMenuType(enderChest), containerId, enderChest.getOwnerHandle(), viewer); this.enderChest = enderChest; - this.viewer = viewer; - var bukkitViewer = viewer.getBukkitEntity(); - viewOnly = !(bukkitViewer.equals(enderChest.getPlayer()) - ? Permissions.ENDERCHEST_EDIT_SELF - : Permissions.ENDERCHEST_OPEN_OTHER) - .hasPermission(bukkitViewer); int upperRows = (int) Math.ceil(enderChest.getContainerSize() / 9.0); topSize = upperRows * 9; @@ -70,24 +67,47 @@ private static MenuType getMenuType(OpenEnderChest enderChest) { } @Override - public CraftInventoryView getBukkitView() { - if (view == null) { - view = new CraftInventoryView(viewer.getBukkitEntity(), enderChest.getBukkitInventory(), this); - } - return view; + protected boolean checkViewOnly() { + return !(ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER) + .hasPermission(viewer.getBukkitEntity()); } @Override - protected Slot addSlot(Slot slot) { - slot = super.addSlot(slot); + public CraftInventoryView getBukkitView() { + if (view == null) { + Inventory top; + if (viewOnly) { + top = new OpenViewInventory(enderChest); + } else { + top = enderChest.getBukkitInventory(); + } + view = new CraftInventoryView(viewer.getBukkitEntity(), top, this) { + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } + return super.getInventory(rawSlot); + } - // If view-only and slot is in upper inventory, wrap it. - if (viewOnly && slot.index < enderChest.getContainerSize()) { - slot = SlotViewOnly.wrap(slot); - slots.set(slot.index, slot); - } + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + return super.convertSlot(rawSlot); + } - return slot; + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly) { + return InventoryType.SlotType.OUTSIDE; + } + return super.getSlotType(slot); + } + }; + } + return view; } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java index 02317ec3..15bc17a8 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java @@ -21,20 +21,16 @@ public class OpenInventoryMenu extends OpenContainerMenu { private final OpenInventory inventory; - private final ServerPlayer viewer; private final int topSize; private final int offset; - private final boolean ownInv; private CraftInventoryView bukkitEntity; protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) { - super(getMenuType(inventory, viewer), i); + super(getMenuType(inventory, viewer), i, inventory.getOwnerHandle(), viewer); this.inventory = inventory; - this.viewer = viewer; - ownInv = inventory.getOwnerHandle().equals(viewer); int upperRows; - if (ownInv) { + if (ownContainer) { // Disallow duplicate access to own main inventory contents. offset = viewer.getInventory().items.size(); upperRows = ((int) Math.ceil((inventory.getContainerSize() - offset) / 9.0)); @@ -43,9 +39,6 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) upperRows = inventory.getContainerSize() / 9; } - boolean viewOnly = !(ownInv ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER) - .hasPermission(viewer.getBukkitEntity()); - // View's upper inventory - our container for (int row = 0; row < upperRows; ++row) { for (int col = 0; col < 9; ++col) { @@ -61,7 +54,7 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) continue; } - Slot slot = getUpperSlot(index, x, y, ownInv, viewOnly); + Slot slot = getUpperSlot(index, x, y); addSlot(slot); } @@ -86,7 +79,17 @@ protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) this.topSize = slots.size() - 36; } - private Slot getUpperSlot(int index, int x, int y, boolean ownInv, boolean viewOnly) { + private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + int size = inventory.getContainerSize(); + if (inventory.getOwnerHandle().equals(viewer)) { + size -= viewer.getInventory().items.size(); + size = ((int) Math.ceil(size / 9.0)) * 9; + } + + return OpenContainerMenu.getContainers(size); + } + + private Slot getUpperSlot(int index, int x, int y) { Slot slot = inventory.getMenuSlot(index, x, y); // If the slot is cannot be interacted with there's nothing to configure. @@ -124,7 +127,7 @@ private Slot getUpperSlot(int index, int x, int y, boolean ownInv, boolean viewO } // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). - if (ownInv && !(slot instanceof ContentDrop.SlotDrop)) { + if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) { return new SlotViewOnly(inventory, index, x, y); } @@ -135,21 +138,19 @@ private Slot getUpperSlot(int index, int x, int y, boolean ownInv, boolean viewO return slot; } - private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { - int size = inventory.getContainerSize(); - if (inventory.getOwnerHandle().equals(viewer)) { - size -= viewer.getInventory().items.size(); - size = ((int) Math.ceil(size / 9.0)) * 9; - } - - return OpenContainerMenu.getContainers(size); + @Override + protected boolean checkViewOnly() { + return !(ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER) + .hasPermission(viewer.getBukkitEntity()); } @Override public CraftInventoryView getBukkitView() { if (bukkitEntity == null) { org.bukkit.inventory.Inventory bukkitInventory; - if (ownInv) { + if (viewOnly) { + bukkitInventory = new OpenViewInventory(inventory); + } else if (ownContainer) { bukkitInventory = new OpenPlayerInventorySelf(inventory, offset); } else { bukkitInventory = inventory.getBukkitInventory(); @@ -158,7 +159,7 @@ public CraftInventoryView getBukkitView() { bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), bukkitInventory, this) { @Override public org.bukkit.inventory.ItemStack getItem(int index) { - if (index < 0) { + if (viewOnly || index < 0) { return null; } @@ -173,6 +174,9 @@ public boolean isInTop(int rawSlot) { @Override public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } if (rawSlot < 0) { return super.getInventory(rawSlot); } @@ -188,6 +192,9 @@ public boolean isInTop(int rawSlot) { @Override public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } if (rawSlot < 0) { return rawSlot; } @@ -203,7 +210,7 @@ public int convertSlot(int rawSlot) { @Override public @NotNull InventoryType.SlotType getSlotType(int slot) { - if (slot < 0) { + if (viewOnly || slot < 0) { return InventoryType.SlotType.OUTSIDE; } if (slot >= topSize) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java index 5295a625..196ee7d1 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java @@ -20,7 +20,7 @@ public OpenPlayerInventory(@NotNull OpenInventory inventory) { } @Override - public OpenInventory getInventory() { + public @NotNull OpenInventory getInventory() { return (OpenInventory) super.getInventory(); } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java new file mode 100644 index 00000000..b714a287 --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java @@ -0,0 +1,157 @@ +package com.lishid.openinv.internal.v1_21_R1.container; + +import net.minecraft.world.Container; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.ListIterator; + +/** + * A locked down "empty" inventory that rejects plugin interaction. + */ +public class OpenViewInventory extends CraftInventory { + + public OpenViewInventory(Container inventory) { + super(inventory); + } + + @Override + public @Nullable ItemStack getItem(int index) { + return null; + } + + @Override + public void setItem(int index, @Nullable ItemStack item) { + + } + + @Override + public @NotNull HashMap addItem(@NotNull ItemStack... items) throws IllegalArgumentException { + return arrayToHashMap(items); + } + + @Override + public @NotNull HashMap removeItem(@NotNull ItemStack... items) throws IllegalArgumentException { + return arrayToHashMap(items); + } + + private static @NotNull HashMap arrayToHashMap(@NotNull ItemStack[] items) { + HashMap ignored = new HashMap<>(); + for (int index = 0; index < items.length; ++index) { + ignored.put(index, items[index]); + } + return ignored; + } + + @Override + public ItemStack[] getContents() { + return new ItemStack[getSize()]; + } + + @Override + public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException { + + } + + @Override + public @NotNull ItemStack[] getStorageContents() { + return new ItemStack[getSize()]; + } + + @Override + public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException { + + } + + @Override + public boolean contains(@NotNull Material material) throws IllegalArgumentException { + return false; + } + + @Override + public boolean contains(@Nullable ItemStack item) { + return false; + } + + @Override + public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException { + return false; + } + + @Override + public boolean contains(@Nullable ItemStack item, int amount) { + return false; + } + + @Override + public boolean containsAtLeast(@Nullable ItemStack item, int amount) { + return false; + } + + @Override + public @NotNull HashMap all( + @NotNull Material material) throws IllegalArgumentException { + return new HashMap<>(); + } + + @Override + public @NotNull HashMap all(@Nullable ItemStack item) { + return new HashMap<>(); + } + + @Override + public int first(@NotNull Material material) throws IllegalArgumentException { + return -1; + } + + @Override + public int first(@NotNull ItemStack item) { + return -1; + } + + @Override + public int firstEmpty() { + return -1; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public void remove(@NotNull Material material) throws IllegalArgumentException { + + } + + @Override + public void remove(@NotNull ItemStack item) { + + } + + @Override + public void clear(int index) { + + } + + @Override + public void clear() { + + } + + @Override + public @NotNull ListIterator iterator() { + return Collections.emptyListIterator(); + } + + @Override + public @NotNull ListIterator iterator(int index) { + return Collections.emptyListIterator(); + } + +} From 64899e5c3dc585e24f77c4b052c3380faf541660 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 15 Jul 2024 11:30:34 -0400 Subject: [PATCH 198/340] Simplify inventory handling * Rename view-only inventory for clarity * Repackage menus and Bukkit implementations for clarity * Reduce duplicate code somewhat --- .../openinv/internal/InternalOwned.java | 7 + .../openinv/internal/PlaceholderParser.java | 9 - .../com/lishid/openinv/internal/ViewOnly.java | 4 + .../container/AnySilentContainer.java | 3 +- .../v1_21_R1/container/OpenEnderChest.java | 6 +- .../container/OpenEnderChestMenu.java | 147 ----------- .../v1_21_R1/container/OpenInventory.java | 14 +- .../OpenDummyInventory.java} | 12 +- .../{ => bukkit}/OpenPlayerInventory.java | 3 +- .../{ => bukkit}/OpenPlayerInventorySelf.java | 3 +- .../OpenChestMenu.java} | 172 +++++++++++-- .../container/menu/OpenEnderChestMenu.java | 55 ++++ .../{ => menu}/OpenInventoryMenu.java | 240 +++++++----------- .../com/lishid/openinv/InventoryListener.java | 35 +-- 14 files changed, 346 insertions(+), 364 deletions(-) create mode 100644 common/src/main/java/com/lishid/openinv/internal/InternalOwned.java delete mode 100644 common/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java create mode 100644 common/src/main/java/com/lishid/openinv/internal/ViewOnly.java delete mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/{OpenViewInventory.java => bukkit/OpenDummyInventory.java} (89%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/{ => bukkit}/OpenPlayerInventory.java (98%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/{ => bukkit}/OpenPlayerInventorySelf.java (79%) rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/{OpenContainerMenu.java => menu/OpenChestMenu.java} (67%) create mode 100644 internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java rename internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/{ => menu}/OpenInventoryMenu.java (50%) diff --git a/common/src/main/java/com/lishid/openinv/internal/InternalOwned.java b/common/src/main/java/com/lishid/openinv/internal/InternalOwned.java new file mode 100644 index 00000000..24f826d0 --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/internal/InternalOwned.java @@ -0,0 +1,7 @@ +package com.lishid.openinv.internal; + +public interface InternalOwned { + + T getOwnerHandle(); + +} diff --git a/common/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java b/common/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java deleted file mode 100644 index 05a8c17b..00000000 --- a/common/src/main/java/com/lishid/openinv/internal/PlaceholderParser.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.lishid.openinv.internal; - -import org.bukkit.configuration.ConfigurationSection; - -public interface PlaceholderParser { - - void load(ConfigurationSection section) throws Exception; - -} diff --git a/common/src/main/java/com/lishid/openinv/internal/ViewOnly.java b/common/src/main/java/com/lishid/openinv/internal/ViewOnly.java new file mode 100644 index 00000000..b8c5bfa6 --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/internal/ViewOnly.java @@ -0,0 +1,4 @@ +package com.lishid.openinv.internal; + +public interface ViewOnly { +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java index 05fb013a..d5b91cf2 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java @@ -17,6 +17,7 @@ package com.lishid.openinv.internal.v1_21_R1.container; import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenChestMenu; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; @@ -103,7 +104,7 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = OpenContainerMenu.getContainers(enderChest.getContainerSize()); + MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); }, Component.translatable("container.enderchest"))); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java index fa038e7e..eefaf3ed 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java @@ -1,6 +1,8 @@ package com.lishid.openinv.internal.v1_21_R1.container; import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenEnderChestMenu; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -25,7 +27,8 @@ import java.util.ArrayList; import java.util.List; -public class OpenEnderChest implements Container, StackedContentsCompatible, MenuProvider, ISpecialEnderChest { +public class OpenEnderChest implements Container, StackedContentsCompatible, MenuProvider, + InternalOwned, ISpecialEnderChest { private CraftInventory inventory; private @NotNull ServerPlayer owner; @@ -38,6 +41,7 @@ public OpenEnderChest(@NotNull org.bukkit.entity.Player player) { this.items = owner.getEnderChestInventory().items; } + @Override public @NotNull ServerPlayer getOwnerHandle() { return owner; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java deleted file mode 100644 index cbd27eb9..00000000 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChestMenu.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R1.container; - -import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; -import com.lishid.openinv.util.Permissions; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.player.Player; -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.inventory.CraftInventoryView; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -class OpenEnderChestMenu extends OpenContainerMenu { - - private final OpenEnderChest enderChest; - private final int topSize; - private CraftInventoryView view; - - OpenEnderChestMenu(OpenEnderChest enderChest, ServerPlayer viewer, int containerId) { - super(getMenuType(enderChest), containerId, enderChest.getOwnerHandle(), viewer); - this.enderChest = enderChest; - int upperRows = (int) Math.ceil(enderChest.getContainerSize() / 9.0); - topSize = upperRows * 9; - - // View's upper inventory - our container - for (int row = 0; row < upperRows; ++row) { - for (int col = 0; col < 9; ++col) { - // x and y for client purposes, but hey, we're thorough here. - // Adapted from net.minecraft.world.inventory.ChestMenu - int x = 8 + col * 18; - int y = 18 + row * 18; - int index = row * 9 + col; - - // Guard against weird inventory sizes. - if (index >= enderChest.getContainerSize()) { - addSlot(new SlotViewOnly(enderChest, index, x, y)); - continue; - } - - addSlot(new Slot(enderChest, index, x, y)); - } - } - - // View's lower inventory - viewer inventory - int playerInvPad = (upperRows - 4) * 18; - for (int row = 0; row < 3; ++row) { - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + row * 18 + 103; - addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); - } - } - // Hotbar - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + 161; - addSlot(new Slot(viewer.getInventory(), col, x, y)); - } - } - - private static MenuType getMenuType(OpenEnderChest enderChest) { - return OpenContainerMenu.getContainers(((int) Math.ceil(enderChest.getContainerSize() / 9.0)) * 9); - } - - @Override - protected boolean checkViewOnly() { - return !(ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER) - .hasPermission(viewer.getBukkitEntity()); - } - - @Override - public CraftInventoryView getBukkitView() { - if (view == null) { - Inventory top; - if (viewOnly) { - top = new OpenViewInventory(enderChest); - } else { - top = enderChest.getBukkitInventory(); - } - view = new CraftInventoryView(viewer.getBukkitEntity(), top, this) { - @Override - public @Nullable Inventory getInventory(int rawSlot) { - if (viewOnly) { - return null; - } - return super.getInventory(rawSlot); - } - - @Override - public int convertSlot(int rawSlot) { - if (viewOnly) { - return InventoryView.OUTSIDE; - } - return super.convertSlot(rawSlot); - } - - @Override - public @NotNull InventoryType.SlotType getSlotType(int slot) { - if (viewOnly) { - return InventoryType.SlotType.OUTSIDE; - } - return super.getSlotType(slot); - } - }; - } - return view; - } - - @Override - public ItemStack quickMoveStack(Player player, int index) { - // See ChestMenu - Slot slot = this.slots.get(index); - - if (slot.isFake() || !slot.hasItem()) { - return ItemStack.EMPTY; - } - - ItemStack itemStack = slot.getItem(); - ItemStack original = itemStack.copy(); - - if (index < topSize) { - if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { - return ItemStack.EMPTY; - } - } else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) { - return ItemStack.EMPTY; - } - - if (itemStack.isEmpty()) { - slot.setByPlayer(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - - return original; - } - - @Override - public boolean stillValid(Player entityhuman) { - return true; - } - -} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java index bec7a8a5..8461901c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java @@ -1,6 +1,9 @@ package com.lishid.openinv.internal.v1_21_R1.container; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventory; +import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenInventoryMenu; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.internal.v1_21_R1.container.slot.Content; import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCrafting; @@ -37,7 +40,7 @@ import java.util.ArrayList; import java.util.List; -public class OpenInventory implements Container, MenuProvider, ISpecialPlayerInventory { +public class OpenInventory implements Container, MenuProvider, InternalOwned, ISpecialPlayerInventory { private final List slots; private final int size; @@ -203,10 +206,6 @@ public InventoryType.SlotType getSlotType(int index) { return slots.get(index).getSlotType(); } - public ServerPlayer getOwnerHandle() { - return owner; - } - public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { MutableComponent component = Component.empty(); // Prefix for use with custom bitmap image fonts. @@ -230,6 +229,11 @@ public ServerPlayer getOwnerHandle() { return component; } + @Override + public ServerPlayer getOwnerHandle() { + return owner; + } + @Override public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { if (bukkitEntity == null) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenDummyInventory.java similarity index 89% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenDummyInventory.java index b714a287..c0eb1a38 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenViewInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenDummyInventory.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R1.container.bukkit; +import com.lishid.openinv.internal.ViewOnly; import net.minecraft.world.Container; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; @@ -14,9 +15,9 @@ /** * A locked down "empty" inventory that rejects plugin interaction. */ -public class OpenViewInventory extends CraftInventory { +public class OpenDummyInventory extends CraftInventory implements ViewOnly { - public OpenViewInventory(Container inventory) { + public OpenDummyInventory(Container inventory) { super(inventory); } @@ -30,16 +31,19 @@ public void setItem(int index, @Nullable ItemStack item) { } + @SuppressWarnings("NonApiType") @Override public @NotNull HashMap addItem(@NotNull ItemStack... items) throws IllegalArgumentException { return arrayToHashMap(items); } + @SuppressWarnings("NonApiType") @Override public @NotNull HashMap removeItem(@NotNull ItemStack... items) throws IllegalArgumentException { return arrayToHashMap(items); } + @SuppressWarnings("NonApiType") private static @NotNull HashMap arrayToHashMap(@NotNull ItemStack[] items) { HashMap ignored = new HashMap<>(); for (int index = 0; index < items.length; ++index) { @@ -93,12 +97,14 @@ public boolean containsAtLeast(@Nullable ItemStack item, int amount) { return false; } + @SuppressWarnings("NonApiType") @Override public @NotNull HashMap all( @NotNull Material material) throws IllegalArgumentException { return new HashMap<>(); } + @SuppressWarnings("NonApiType") @Override public @NotNull HashMap all(@Nullable ItemStack item) { return new HashMap<>(); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventory.java similarity index 98% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventory.java index 196ee7d1..de323f5a 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventory.java @@ -1,6 +1,7 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R1.container.bukkit; import com.google.common.base.Preconditions; +import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventorySelf.java similarity index 79% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventorySelf.java index a64b61d8..f9826ed1 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenPlayerInventorySelf.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventorySelf.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R1.container.bukkit; +import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java similarity index 67% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java index 64a42e8c..ca969983 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenContainerMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java @@ -1,13 +1,18 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R1.container.menu; import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenDummyInventory; import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotPlaceholder; import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.ClickType; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.inventory.ContainerListener; @@ -16,6 +21,10 @@ 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.inventory.CraftInventoryView; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,11 +35,17 @@ /** * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. */ -public abstract class OpenContainerMenu extends AbstractContainerMenu { +public abstract class OpenChestMenu> + extends AbstractContainerMenu { + protected static final int BOTTOM_INVENTORY_SIZE = 36; + + protected final T container; protected final ServerPlayer viewer; protected final boolean viewOnly; protected final boolean ownContainer; + protected final int topSize; + private CraftInventoryView bukkitEntity; // Syncher fields private @Nullable ContainerSynchronizer synchronizer; private final List dataSlots = new ArrayList<>(); @@ -39,25 +54,142 @@ public abstract class OpenContainerMenu extends AbstractContainerMenu { private ItemStack remoteCarried = ItemStack.EMPTY; private boolean suppressRemoteUpdates; - protected OpenContainerMenu(@Nullable MenuType containers, int containerCounter,ServerPlayer owner, ServerPlayer viewer) { - super(containers, containerCounter); + protected OpenChestMenu(MenuType type, int containerCounter, T container, ServerPlayer viewer) { + super(type, containerCounter); + this.container = container; this.viewer = viewer; - ownContainer = owner.equals(viewer); + ownContainer = container.getOwnerHandle().equals(viewer); + topSize = getTopSize(viewer); viewOnly = checkViewOnly(); + + preSlotSetup(); + + int upperRows = topSize / 9; + // View's upper inventory - our container + for (int row = 0; row < upperRows; ++row) { + for (int col = 0; col < 9; ++col) { + // x and y for client purposes, but hey, we're thorough here. + // Adapted from net.minecraft.world.inventory.ChestMenu + int x = 8 + col * 18; + int y = 18 + row * 18; + int index = row * 9 + col; + + // Guard against weird inventory sizes. + if (index >= container.getContainerSize()) { + addSlot(new SlotViewOnly(container, index, x, y)); + continue; + } + + Slot slot = getUpperSlot(index, x, y); + + addSlot(slot); + } + } + + // View's lower inventory - viewer inventory + int playerInvPad = (upperRows - 4) * 18; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + row * 18 + 103; + addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); + } + } + // Hotbar + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + 161; + addSlot(new Slot(viewer.getInventory(), col, x, y)); + } + } + + public static @NotNull MenuType getChestMenuType(int inventorySize) { + inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9; + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 27 -> MenuType.GENERIC_9x3; + case 36 -> MenuType.GENERIC_9x4; + case 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize); + }; } protected abstract boolean checkViewOnly(); - static @NotNull MenuType getContainers(int inventorySize) { - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; - case 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - // Default to 27-slot inventory - default -> MenuType.GENERIC_9x3; - }; + protected void preSlotSetup() {} + + protected @NotNull Slot getUpperSlot(int index, int x, int y) { + if (viewOnly) { + return new SlotViewOnly(container, index, x, y); + } + return new Slot(container, index, x, y); + } + + + @Override + public final @NotNull CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + bukkitEntity = createBukkitEntity(); + } + + return bukkitEntity; + } + + protected @NotNull CraftInventoryView createBukkitEntity() { + Inventory top; + if (viewOnly) { + top = new OpenDummyInventory(container); + } else { + top = container.getBukkitInventory(); + } + return new CraftInventoryView(viewer.getBukkitEntity(), top, this) { + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } + return super.getInventory(rawSlot); + } + + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + return super.convertSlot(rawSlot); + } + + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly) { + return InventoryType.SlotType.OUTSIDE; + } + return super.getSlotType(slot); + } + }; + } + + private int getTopSize(ServerPlayer viewer) { + MenuType menuType = getType(); + if (menuType == null) { + throw new IllegalStateException("MenuType cannot be null!"); + } else if (menuType == MenuType.GENERIC_9x1) { + return 9; + } else if (menuType == MenuType.GENERIC_9x2) { + return 18; + } else if (menuType == MenuType.GENERIC_9x3) { + return 27; + } else if (menuType == MenuType.GENERIC_9x4) { + return 36; + } else if (menuType == MenuType.GENERIC_9x5) { + return 45; + } else if (menuType == MenuType.GENERIC_9x6) { + return 54; + } + // This is a bit gross, but allows us a safe fallthrough. + return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE; } /** @@ -152,19 +284,21 @@ public void clicked(int i, int j, ClickType clickType, Player player) { super.clicked(i, j, clickType, player); } + @Override + public boolean stillValid(Player player) { + return true; + } + + // Overrides from here on are purely to modify the sync process to send placeholder items. @Override protected Slot addSlot(Slot slot) { slot.index = this.slots.size(); - if (viewOnly && !(slot instanceof SlotViewOnly)) { - slot = SlotViewOnly.wrap(slot); - } this.slots.add(slot); this.lastSlots.add(ItemStack.EMPTY); this.remoteSlots.add(ItemStack.EMPTY); return slot; } - // Overrides from here on are purely to modify the sync process to send placeholder items. @Override protected DataSlot addDataSlot(DataSlot dataSlot) { this.dataSlots.add(dataSlot); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java new file mode 100644 index 00000000..2f65b8af --- /dev/null +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java @@ -0,0 +1,55 @@ +package com.lishid.openinv.internal.v1_21_R1.container.menu; + +import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; +import com.lishid.openinv.util.Permissions; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +public class OpenEnderChestMenu extends OpenChestMenu { + + public OpenEnderChestMenu(OpenEnderChest enderChest, ServerPlayer viewer, int containerId) { + super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer); + } + + @Override + protected boolean checkViewOnly() { + return !(ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER) + .hasPermission(viewer.getBukkitEntity()); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + if (viewOnly) { + return ItemStack.EMPTY; + } + + // See ChestMenu + Slot slot = this.slots.get(index); + + if (slot.isFake() || !slot.hasItem()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack original = itemStack.copy(); + + if (index < topSize) { + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) { + return ItemStack.EMPTY; + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return original; + } + +} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java similarity index 50% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java rename to internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java index 15bc17a8..c80af6e2 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java @@ -1,5 +1,8 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R1.container.menu; +import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventorySelf; import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop; import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment; import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; @@ -7,6 +10,7 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -18,81 +22,36 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenInventoryMenu extends OpenContainerMenu { +public class OpenInventoryMenu extends OpenChestMenu { - private final OpenInventory inventory; - private final int topSize; - private final int offset; - private CraftInventoryView bukkitEntity; + private int offset; - protected OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) { - super(getMenuType(inventory, viewer), i, inventory.getOwnerHandle(), viewer); - this.inventory = inventory; - - int upperRows; - if (ownContainer) { - // Disallow duplicate access to own main inventory contents. - offset = viewer.getInventory().items.size(); - upperRows = ((int) Math.ceil((inventory.getContainerSize() - offset) / 9.0)); - } else { - offset = 0; - upperRows = inventory.getContainerSize() / 9; - } - - // View's upper inventory - our container - for (int row = 0; row < upperRows; ++row) { - for (int col = 0; col < 9; ++col) { - // x and y for client purposes, but hey, we're thorough here. - // Adapted from net.minecraft.world.inventory.ChestMenu - int x = 8 + col * 18; - int y = 18 + row * 18; - int index = offset + row * 9 + col; - - // Guard against weird inventory sizes. - if (index >= inventory.getContainerSize()) { - addSlot(new SlotViewOnly(inventory, index, x, y)); - continue; - } - - Slot slot = getUpperSlot(index, x, y); - - addSlot(slot); - } - } - - // View's lower inventory - viewer inventory - int playerInvPad = (upperRows - 4) * 18; - for (int row = 0; row < 3; ++row) { - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + row * 18 + 103; - addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); - } - } - // Hotbar - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + 161; - addSlot(new Slot(viewer.getInventory(), col, x, y)); - } - - this.topSize = slots.size() - 36; + public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) { + super(getMenuType(inventory, viewer), i, inventory, viewer); } - private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { int size = inventory.getContainerSize(); + // Disallow duplicate access to own main inventory contents. if (inventory.getOwnerHandle().equals(viewer)) { size -= viewer.getInventory().items.size(); size = ((int) Math.ceil(size / 9.0)) * 9; } - return OpenContainerMenu.getContainers(size); + return getChestMenuType(size); } - private Slot getUpperSlot(int index, int x, int y) { - Slot slot = inventory.getMenuSlot(index, x, y); + @Override + protected void preSlotSetup() { + offset = ownContainer ? viewer.getInventory().items.size() : 0; + } - // If the slot is cannot be interacted with there's nothing to configure. + @Override + protected @NotNull Slot getUpperSlot(int index, int x, int y) { + index += offset; + Slot slot = container.getMenuSlot(index, x, y); + + // If the slot cannot be interacted with there's nothing to configure. if (slot.getClass().equals(SlotViewOnly.class)) { return slot; } @@ -100,7 +59,7 @@ private Slot getUpperSlot(int index, int x, int y) { // Remove drop slot if viewer is not allowed to use it. if (slot instanceof ContentDrop.SlotDrop && (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) { - return new SlotViewOnly(inventory, index, x, y); + return new SlotViewOnly(container, index, x, y); } if (slot instanceof ContentEquipment.SlotEquipment equipment) { @@ -119,7 +78,7 @@ private Slot getUpperSlot(int index, int x, int y) { // If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot. if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) { - equipment.onlyEquipmentFor(inventory.getOwnerHandle()); + equipment.onlyEquipmentFor(container.getOwnerHandle()); } // Equipment slots are a core part of the inventory, so they will always be shown. @@ -128,7 +87,7 @@ private Slot getUpperSlot(int index, int x, int y) { // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) { - return new SlotViewOnly(inventory, index, x, y); + return new SlotViewOnly(container, index, x, y); } if (viewOnly) { @@ -145,91 +104,92 @@ protected boolean checkViewOnly() { } @Override - public CraftInventoryView getBukkitView() { - if (bukkitEntity == null) { - org.bukkit.inventory.Inventory bukkitInventory; - if (viewOnly) { - bukkitInventory = new OpenViewInventory(inventory); - } else if (ownContainer) { - bukkitInventory = new OpenPlayerInventorySelf(inventory, offset); - } else { - bukkitInventory = inventory.getBukkitInventory(); + protected @NotNull CraftInventoryView createBukkitEntity() { + org.bukkit.inventory.Inventory bukkitInventory; + if (viewOnly) { + bukkitInventory = new OpenDummyInventory(container); + } else if (ownContainer) { + bukkitInventory = new OpenPlayerInventorySelf(container, offset); + } else { + bukkitInventory = container.getBukkitInventory(); + } + + return new CraftInventoryView(viewer.getBukkitEntity(), bukkitInventory, this) { + @Override + public org.bukkit.inventory.ItemStack getItem(int index) { + if (viewOnly || index < 0) { + return null; + } + + Slot slot = slots.get(index); + return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY); } - bukkitEntity = new CraftInventoryView(viewer.getBukkitEntity(), bukkitInventory, this) { - @Override - public org.bukkit.inventory.ItemStack getItem(int index) { - if (viewOnly || index < 0) { - return null; - } + @Override + public boolean isInTop(int rawSlot) { + return rawSlot < topSize; + } - Slot slot = slots.get(index); - return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY); + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; } - - @Override - public boolean isInTop(int rawSlot) { - return rawSlot < topSize; + if (rawSlot < 0) { + return super.getInventory(rawSlot); + } + if (rawSlot > topSize) { + return super.getInventory(offset + rawSlot); + } + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return null; } + return getTopInventory(); + } - @Override - public @Nullable Inventory getInventory(int rawSlot) { - if (viewOnly) { - return null; - } - if (rawSlot < 0) { - return super.getInventory(rawSlot); - } - if (rawSlot > topSize) { - return super.getInventory(offset + rawSlot); - } + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + if (rawSlot < 0) { + return rawSlot; + } + if (rawSlot < topSize) { Slot slot = slots.get(rawSlot); if (slot.isFake()) { - return null; - } - return getTopInventory(); - } - - @Override - public int convertSlot(int rawSlot) { - if (viewOnly) { return InventoryView.OUTSIDE; } - if (rawSlot < 0) { - return rawSlot; - } - if (rawSlot < topSize) { - Slot slot = slots.get(rawSlot); - if (slot.isFake()) { - return InventoryView.OUTSIDE; - } - return rawSlot; - } - return super.convertSlot(offset + rawSlot); + return rawSlot; } + return super.convertSlot(offset + rawSlot); + } - @Override - public @NotNull InventoryType.SlotType getSlotType(int slot) { - if (viewOnly || slot < 0) { - return InventoryType.SlotType.OUTSIDE; - } - if (slot >= topSize) { - return super.getSlotType(offset + slot); - } - return inventory.getSlotType(offset + slot); + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly || slot < 0) { + return InventoryType.SlotType.OUTSIDE; } - - @Override - public int countSlots() { - return topSize + getBottomInventory().getSize(); + if (slot >= topSize) { + return super.getSlotType(offset + slot); } - }; - } - return bukkitEntity; + return container.getSlotType(offset + slot); + } + + @Override + public int countSlots() { + return topSize + getBottomInventory().getSize(); + } + }; } @Override public ItemStack quickMoveStack(Player player, int index) { + if (viewOnly) { + return ItemStack.EMPTY; + } + // See ChestMenu and InventoryMenu Slot slot = this.slots.get(index); @@ -251,7 +211,7 @@ public ItemStack quickMoveStack(Player player, int index) { // If this is gear, try to move it to the correct slot first. case OFFHAND, FEET, LEGS, CHEST, HEAD -> { // Locate the correct slot in the contents following the main inventory. - for (int extra = inventory.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { + for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { Slot extraSlot = getSlot(extra); if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot && equipSlot.getEquipmentSlot() == equipmentSlot) { @@ -274,7 +234,7 @@ public ItemStack quickMoveStack(Player player, int index) { } } else { // If we didn't move to a gear slot, try to move to a main inventory slot. - if (!movedGear && !this.moveItemStackTo(itemStack, 0, inventory.getOwnerHandle().getInventory().items.size(), true)) { + if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) { return ItemStack.EMPTY; } } @@ -289,14 +249,4 @@ public ItemStack quickMoveStack(Player player, int index) { return originalStack; } - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean canDragTo(Slot slot) { - return !(slot instanceof ContentDrop.SlotDrop || slot instanceof SlotViewOnly); - } - } diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java index 390277b2..1a0b29d9 100644 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java @@ -17,9 +17,8 @@ package com.lishid.openinv; import com.google.errorprone.annotations.Keep; -import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.ViewOnly; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; import org.bukkit.GameMode; @@ -36,8 +35,6 @@ import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; -import java.util.Objects; - /** * Listener for inventory-related events to prevent modification of inventories where not allowed. * @@ -100,34 +97,8 @@ private void handleInventoryInteract(@NotNull final InventoryInteractEvent event } Inventory inventory = event.getView().getTopInventory(); - ISpecialInventory backing = InventoryAccess.getInventory(inventory); - - // Not a special inventory. - if (backing == null) { - return; - } - - Permissions editSelf; - Permissions editOther; - if (backing instanceof ISpecialEnderChest) { - editSelf = Permissions.ENDERCHEST_EDIT_SELF; - editOther = Permissions.ENDERCHEST_EDIT_OTHER; - } else if (backing instanceof ISpecialPlayerInventory) { - editSelf = Permissions.INVENTORY_EDIT_SELF; - editOther = Permissions.INVENTORY_EDIT_OTHER; - } else { - // Unknown implementation. - return; - } - - if (Objects.equals(entity, backing.getPlayer())) { - if (!editSelf.hasPermission(entity)) { - event.setCancelled(true); - } - } else { - if (!editOther.hasPermission(entity)) { - event.setCancelled(true); - } + if (inventory instanceof ViewOnly) { + event.setCancelled(true); } } From dbceff086a2b33cbb3ede2ffbecfb961c8ca9458 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 25 Jul 2024 11:57:34 -0400 Subject: [PATCH 199/340] Reduce responsibility of main class This should make the plugin a ton more testable (though I don't have it in me right now to write tests). --- .../openinv/event/OpenPlayerSaveEvent.java | 13 + .../com/lishid/openinv/event/OpenEvents.java | 4 +- .../openinv/util/lang/LanguageManager.java | 2 +- .../internal/v1_20_R3/PlayerManager.java | 5 - .../internal/v1_20_R4/PlayerManager.java | 5 - .../v1_21_R1/player/PlayerManager.java | 5 - .../com/lishid/openinv/InventoryListener.java | 105 ---- .../com/lishid/openinv/OfflineHandler.java | 42 -- .../main/java/com/lishid/openinv/OpenInv.java | 460 +++--------------- .../com/lishid/openinv/PlayerListener.java | 102 ---- .../ContainerSettingCommand.java | 29 +- .../{commands => command}/OpenInvCommand.java | 30 +- .../SearchContainerCommand.java | 2 +- .../SearchEnchantCommand.java | 6 +- .../SearchInvCommand.java | 6 +- .../openinv/listener/ContainerListener.java | 146 ++++++ .../LegacyInventoryListener.java | 84 ++-- .../lishid/openinv/util/InternalAccessor.java | 65 +-- .../lishid/openinv/util/InventoryManager.java | 271 +++++++++++ .../com/lishid/openinv/util/PlayerLoader.java | 222 +++++++++ .../lishid/openinv/util/config/Config.java | 31 ++ .../util/{ => config}/ConfigUpdater.java | 7 +- .../openinv/util/{ => lang}/LangMigrator.java | 2 +- .../openinv/util/setting/PlayerToggle.java | 11 + .../lishid/openinv/util/setting/Toggles.java | 59 +++ 25 files changed, 938 insertions(+), 776 deletions(-) delete mode 100644 plugin/src/main/java/com/lishid/openinv/InventoryListener.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/OfflineHandler.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/PlayerListener.java rename plugin/src/main/java/com/lishid/openinv/{commands => command}/ContainerSettingCommand.java (74%) rename plugin/src/main/java/com/lishid/openinv/{commands => command}/OpenInvCommand.java (88%) rename plugin/src/main/java/com/lishid/openinv/{commands => command}/SearchContainerCommand.java (99%) rename plugin/src/main/java/com/lishid/openinv/{commands => command}/SearchEnchantCommand.java (97%) rename plugin/src/main/java/com/lishid/openinv/{commands => command}/SearchInvCommand.java (96%) create mode 100644 plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java rename plugin/src/main/java/com/lishid/openinv/{ => listener}/LegacyInventoryListener.java (76%) create mode 100644 plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java create mode 100644 plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java create mode 100644 plugin/src/main/java/com/lishid/openinv/util/config/Config.java rename plugin/src/main/java/com/lishid/openinv/util/{ => config}/ConfigUpdater.java (99%) rename plugin/src/main/java/com/lishid/openinv/util/{ => lang}/LangMigrator.java (98%) create mode 100644 plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java create mode 100644 plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java diff --git a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java index f20c635a..b6da22dc 100644 --- a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java @@ -3,6 +3,7 @@ import com.google.errorprone.annotations.RestrictedApi; import com.lishid.openinv.internal.ISpecialInventory; import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -11,6 +12,8 @@ */ public class OpenPlayerSaveEvent extends PlayerSaveEvent { + private static final HandlerList HANDLERS = new HandlerList(); + private final ISpecialInventory inventory; /** @@ -40,4 +43,14 @@ public class OpenPlayerSaveEvent extends PlayerSaveEvent { return inventory; } + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + } diff --git a/common/src/main/java/com/lishid/openinv/event/OpenEvents.java b/common/src/main/java/com/lishid/openinv/event/OpenEvents.java index 77349ccf..1ad3a8a8 100644 --- a/common/src/main/java/com/lishid/openinv/event/OpenEvents.java +++ b/common/src/main/java/com/lishid/openinv/event/OpenEvents.java @@ -16,8 +16,8 @@ public static boolean saveCancelled(@NotNull Player player) { return call(new PlayerSaveEvent(player)); } - public static boolean saveCancelled(@NotNull Player player, @NotNull ISpecialInventory inventory) { - return call(new OpenPlayerSaveEvent(player, inventory)); + public static boolean saveCancelled(@NotNull ISpecialInventory inventory) { + return call(new OpenPlayerSaveEvent((Player) inventory.getPlayer(), inventory)); } private static boolean call(T event) { diff --git a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java index 49dc55ac..7696e909 100644 --- a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java +++ b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -274,7 +274,7 @@ private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull if (sender instanceof Player) { return ((Player) sender).getLocale(); } else { - return plugin.getConfig().getString("settings.locale", "en_us"); + return plugin.getConfig().getString("settings.locale", "en"); } } diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java index 7f02bb7d..db93c97f 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java @@ -105,11 +105,6 @@ public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java index 2acab9a3..1ff8b4e9 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java @@ -105,11 +105,6 @@ public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java index 350270bc..64fe5bb6 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java @@ -85,11 +85,6 @@ public PlayerManager(@NotNull Logger logger) { @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - // Ensure player has data - if (!offline.hasPlayedBefore()) { - return null; - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); diff --git a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/InventoryListener.java deleted file mode 100644 index 1a0b29d9..00000000 --- a/plugin/src/main/java/com/lishid/openinv/InventoryListener.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv; - -import com.google.errorprone.annotations.Keep; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.ViewOnly; -import com.lishid.openinv.util.InventoryAccess; -import com.lishid.openinv.util.Permissions; -import org.bukkit.GameMode; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryInteractEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; - -/** - * Listener for inventory-related events to prevent modification of inventories where not allowed. - * - * @author Jikoo - */ -record InventoryListener(OpenInv plugin) implements Listener { - - @Keep - @EventHandler - private void onInventoryClose(@NotNull final InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player player)) { - return; - } - - InventoryHolder holder = event.getInventory().getHolder(); - if (this.plugin.getSilentContainerStatus(player) - && holder != null - && this.plugin.getAnySilentContainer().isAnySilentContainer(holder)) { - this.plugin.getAnySilentContainer().deactivateContainer(player); - } - - ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory()); - if (specialInventory != null) { - this.plugin.handleCloseInventory(specialInventory); - } else { - specialInventory = InventoryAccess.getPlayerInventory(event.getInventory()); - if (specialInventory != null) { - this.plugin.handleCloseInventory(specialInventory); - } - } - } - - @Keep - @EventHandler(priority = EventPriority.LOWEST) - private void onInventoryClick(@NotNull final InventoryClickEvent event) { - handleInventoryInteract(event); - } - - @Keep - @EventHandler(priority = EventPriority.LOWEST) - private void onInventoryDrag(@NotNull final InventoryDragEvent event) { - handleInventoryInteract(event); - } - - /** - * Handle common InventoryInteractEvent functions. - * - * @param event the InventoryInteractEvent - */ - private void handleInventoryInteract(@NotNull final InventoryInteractEvent event) { - HumanEntity entity = event.getWhoClicked(); - - // Un-cancel spectator interactions. - if (entity.getGameMode() == GameMode.SPECTATOR && Permissions.SPECTATE_CLICK.hasPermission(entity)) { - event.setCancelled(false); - } - - if (event.isCancelled()) { - return; - } - - Inventory inventory = event.getView().getTopInventory(); - if (inventory instanceof ViewOnly) { - event.setCancelled(true); - } - } - -} diff --git a/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java b/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java deleted file mode 100644 index d2dee95c..00000000 --- a/plugin/src/main/java/com/lishid/openinv/OfflineHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv; - -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.util.Permissions; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.UUID; -import java.util.function.BiFunction; -import java.util.function.Consumer; - -record OfflineHandler( - @NotNull BiFunction, UUID, ISpecialInventory> fetch, - @NotNull Consumer<@NotNull ISpecialInventory> handle) { - - static final OfflineHandler REMOVE_AND_CLOSE = new OfflineHandler( - Map::remove, - inventory -> OpenInv.ejectViewers(inventory, viewer -> true) - ); - - static final OfflineHandler REQUIRE_PERMISSIONS = new OfflineHandler( - Map::get, - inventory -> OpenInv.ejectViewers(inventory, viewer -> !Permissions.ACCESS_OFFLINE.hasPermission(viewer)) - ); - -} diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index b8ad1ad9..457bff17 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -18,77 +18,62 @@ import com.github.jikoo.planarwrappers.util.version.BukkitVersions; import com.github.jikoo.planarwrappers.util.version.Version; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.lishid.openinv.commands.ContainerSettingCommand; -import com.lishid.openinv.commands.OpenInvCommand; -import com.lishid.openinv.commands.SearchContainerCommand; -import com.lishid.openinv.commands.SearchEnchantCommand; -import com.lishid.openinv.commands.SearchInvCommand; -import com.lishid.openinv.event.OpenEvents; +import com.lishid.openinv.command.ContainerSettingCommand; +import com.lishid.openinv.command.OpenInvCommand; +import com.lishid.openinv.command.SearchContainerCommand; +import com.lishid.openinv.command.SearchEnchantCommand; +import com.lishid.openinv.command.SearchInvCommand; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.util.ConfigUpdater; +import com.lishid.openinv.listener.ContainerListener; +import com.lishid.openinv.listener.LegacyInventoryListener; +import com.lishid.openinv.util.config.Config; +import com.lishid.openinv.util.config.ConfigUpdater; import com.lishid.openinv.util.InternalAccessor; -import com.lishid.openinv.util.LangMigrator; -import com.lishid.openinv.util.Permissions; -import com.lishid.openinv.util.StringMetric; +import com.lishid.openinv.util.InventoryManager; +import com.lishid.openinv.util.lang.LangMigrator; +import com.lishid.openinv.util.PlayerLoader; +import com.lishid.openinv.util.setting.Toggles; import com.lishid.openinv.util.lang.LanguageManager; -import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.logging.Level; -import java.util.stream.Stream; /** - * Open other player's inventory - * - * @author lishid + * The main class for OpenInv. */ public class OpenInv extends JavaPlugin implements IOpenInv { - private final Cache offlineLookUpCache = CacheBuilder.newBuilder().maximumSize(10).build(); - private final Map inventories = new ConcurrentHashMap<>(); - private final Map enderChests = new ConcurrentHashMap<>(); - private InternalAccessor accessor; + private Config config; + private Toggles toggles; + private InventoryManager inventoryManager; private LanguageManager languageManager; + private PlayerLoader playerLoader; private boolean isSpigot = false; - private OfflineHandler offlineHandler; @Override public void reloadConfig() { super.reloadConfig(); + config.reload(getConfig()); + toggles.reload(getConfig()); languageManager.reload(); - this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS; - if (this.accessor != null && this.accessor.isSupported()) { - this.accessor.reload(this.getConfig()); + if (accessor != null && accessor.isSupported()) { + accessor.reload(getConfig()); } } @@ -103,27 +88,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command @Override public void onDisable() { - if (this.disableSaving()) { - return; - } - - Stream.concat(inventories.values().stream(), enderChests.values().stream()) - .map(inventory -> { - // Cheat a bit - rather than stream twice, evict all viewers during remapping. - ejectViewers(inventory, viewer -> true); - if (inventory.getPlayer() instanceof Player player) { - return player; - } - return null; - }) - .filter(Objects::nonNull) - .distinct() - .forEach(player -> { - if (!player.isOnline()) { - player = accessor.getPlayerDataManager().inject(player); - } - player.saveData(); - }); + inventoryManager.evictAll(); } @Override @@ -135,8 +100,23 @@ public void onEnable() { Path dataFolder = getDataFolder().toPath(); new LangMigrator(dataFolder, dataFolder.resolve("locale"), getLogger()).migrate(); - this.languageManager = new LanguageManager(this, "en"); - this.accessor = new InternalAccessor(getLogger(), languageManager); + // Set up configurable features. Note that #reloadConfig is called on the first call to #getConfig! + // Configuration values should not be accessed until after all of these have been set up. + config = new Config(); + toggles = new Toggles() { + @Override + public void save() { + saveConfig(); + } + }; + languageManager = new LanguageManager(this, "en"); + accessor = new InternalAccessor(getLogger(), languageManager); + + // Perform initial config load. + reloadConfig(); + + inventoryManager = new InventoryManager(this, accessor); + playerLoader = new PlayerLoader(this, config, inventoryManager, accessor, getLogger()); try { Class.forName("org.bukkit.entity.Player$Spigot"); @@ -157,17 +137,17 @@ public void onEnable() { // Register listeners if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { pm.registerEvents(new LegacyInventoryListener(this), this); - } else { - pm.registerEvents(new PlayerListener(this, languageManager), this); } - pm.registerEvents(new InventoryListener(this), this); + pm.registerEvents(playerLoader, this); + pm.registerEvents(inventoryManager, this); + pm.registerEvents(new ContainerListener(accessor, toggles, languageManager), this); // Register commands to their executors - this.setCommandExecutor(new OpenInvCommand(this, languageManager), "openinv", "openender"); + this.setCommandExecutor(new OpenInvCommand(this, config, inventoryManager, languageManager, playerLoader), "openinv", "openender"); this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); - this.setCommandExecutor(new ContainerSettingCommand(this, languageManager), "silentcontainer", "anycontainer"); + this.setCommandExecutor(new ContainerSettingCommand(toggles, languageManager), "silentcontainer", "anycontainer"); } else { this.sendVersionError(this.getLogger()::warning); @@ -210,17 +190,17 @@ public boolean isSupportedVersion() { @Override public boolean disableSaving() { - return this.getConfig().getBoolean("settings.disable-saving", false); + return config.isSaveDisabled(); } @Override public boolean disableOfflineAccess() { - return this.getConfig().getBoolean("settings.disable-offline-access", false); + return config.isOfflineDisabled(); } @Override public boolean noArgsOpensSelf() { - return this.getConfig().getBoolean("settings.command.open.no-args-opens-self", false); + return config.doesNoArgsOpenSelf(); } @Override @@ -230,379 +210,57 @@ public boolean noArgsOpensSelf() { @Override public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) { - return this.getConfig().getBoolean("toggles.any-chest." + offline.getUniqueId(), false); + return toggles.any().is(offline.getUniqueId()); } @Override public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { - this.getConfig().set("toggles.any-chest." + offline.getUniqueId(), status); - this.saveConfig(); + toggles.any().set(offline.getUniqueId(), status); } @Override public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) { - return this.getConfig().getBoolean("toggles.silent-chest." + offline.getUniqueId(), false); + return toggles.silent().is(offline.getUniqueId()); } @Override public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { - this.getConfig().set("toggles.silent-chest." + offline.getUniqueId(), status); - this.saveConfig(); + toggles.silent().set(offline.getUniqueId(), status); } @Override public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) { - UUID key = player.getUniqueId(); - - if (this.enderChests.containsKey(key)) { - return this.enderChests.get(key); - } - - ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player); - this.enderChests.put(key, inv); - return inv; + return inventoryManager.getEnderChest(player); } @Override public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) { - UUID key = player.getUniqueId(); - - if (this.inventories.containsKey(key)) { - return this.inventories.get(key); - } - - ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player); - this.inventories.put(key, inv); - return inv; + return inventoryManager.getInventory(player); } @Override public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - return this.accessor.getPlayerDataManager().openInventory(player, inventory); + return this.accessor.openInventory(player, inventory); } @Override public boolean isPlayerLoaded(@NotNull UUID playerUuid) { - return this.inventories.containsKey(playerUuid) || this.enderChests.containsKey(playerUuid); + return inventoryManager.getLoadedPlayer(playerUuid) != null; } @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - UUID key = offline.getUniqueId(); - - if (this.inventories.containsKey(key)) { - return (Player) this.inventories.get(key).getPlayer(); - } - - if (this.enderChests.containsKey(key)) { - return (Player) this.enderChests.get(key).getPlayer(); - } - - Player player = offline.getPlayer(); - if (player != null) { - return player; - } - - if (disableOfflineAccess() || !this.isSupportedVersion()) { - return null; - } - - if (Bukkit.isPrimaryThread()) { - return this.accessor.getPlayerDataManager().loadPlayer(offline); - } - - Future future = Bukkit.getScheduler().callSyncMethod(this, - () -> OpenInv.this.accessor.getPlayerDataManager().loadPlayer(offline)); - - try { - player = future.get(); - } catch (InterruptedException | ExecutionException e) { - getLogger().log(Level.WARNING, e.getMessage(), e); - return null; - } - - return player; + return playerLoader.load(offline); } @Override public @Nullable OfflinePlayer matchPlayer(@NotNull String name) { - - // Warn if called on the main thread - if we resort to searching offline players, this may take several seconds. - if (Bukkit.getServer().isPrimaryThread()) { - this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!"); - this.getLogger().warning("This can cause the server to hang, potentially severely."); - this.getLogger().log(Level.WARNING, "Current stack trace", new Throwable("Current stack trace")); - } - - OfflinePlayer player; - - try { - UUID uuid = UUID.fromString(name); - player = Bukkit.getOfflinePlayer(uuid); - // Ensure player is an existing player. - if (player.hasPlayedBefore() || player.isOnline()) { - return player; - } - // Return null otherwise. - return null; - } catch (IllegalArgumentException ignored) { - // Not a UUID - } - - // Exact online match first. - player = Bukkit.getServer().getPlayerExact(name); - - if (player != null) { - return player; - } - - // Cached offline match. - PlayerProfile cachedResult = offlineLookUpCache.getIfPresent(name); - if (cachedResult != null && cachedResult.getUniqueId() != null) { - player = Bukkit.getOfflinePlayer(cachedResult.getUniqueId()); - // Ensure player is an existing player. - if (player.hasPlayedBefore() || player.isOnline()) { - return player; - } - // Return null otherwise. - return null; - } - - // Exact offline match second - ensure offline access works when matchable users are online. - player = Bukkit.getServer().getOfflinePlayer(name); - - if (player.hasPlayedBefore()) { - offlineLookUpCache.put(name, player.getPlayerProfile()); - return player; - } - - // Inexact online match. - player = Bukkit.getServer().getPlayer(name); - - if (player != null) { - return player; - } - - // Finally, inexact offline match. - float bestMatch = 0; - for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) { - if (offline.getName() == null) { - // Loaded by UUID only, name has never been looked up. - continue; - } - - float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName()); - - if (currentMatch == 1.0F) { - return offline; - } - - if (currentMatch > bestMatch) { - bestMatch = currentMatch; - player = offline; - } - } - - if (player != null) { - // If a match was found, store it. - offlineLookUpCache.put(name, player.getPlayerProfile()); - return player; - } - - // No players have ever joined the server. - return null; + return playerLoader.match(name); } @Override public void unload(@NotNull final OfflinePlayer offline) { - setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE); - } - - /** - * Evict all viewers lacking cross-world permissions when a {@link Player} changes worlds. - * - * @param player the Player - */ - void changeWorld(@NotNull Player player) { - UUID key = player.getUniqueId(); - - if (this.inventories.containsKey(key)) { - kickCrossWorldViewers(player, this.inventories.get(key)); - } - - if (this.enderChests.containsKey(key)) { - kickCrossWorldViewers(player, this.enderChests.get(key)); - } - } - - private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInventory inventory) { - ejectViewers( - inventory, - viewer -> - !Permissions.ACCESS_CROSSWORLD.hasPermission(viewer) - && !Objects.equals(viewer.getWorld(), player.getWorld())); - } - - /** - * Method for handling a Player going offline. - * - * @param player the Player - */ - void setPlayerOffline(@NotNull Player player) { - setPlayerOffline(player, offlineHandler); - } - - private void setPlayerOffline(@NotNull OfflinePlayer player, @NotNull OfflineHandler handler) { - UUID key = player.getUniqueId(); - - setPlayerOffline(inventories, key, handler); - setPlayerOffline(enderChests, key, handler); - } - - private void setPlayerOffline( - @NotNull Map map, - @NotNull UUID key, - @NotNull OfflineHandler handler) { - ISpecialInventory inventory = handler.fetch().apply(map, key); - if (inventory == null) { - return; - } - inventory.setPlayerOffline(); - if (!inventory.isInUse()) { - map.remove(key); - } else { - handler.handle().accept(inventory); - } - } - - void handleCloseInventory(@NotNull ISpecialInventory inventory) { - Map map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests; - UUID key = inventory.getPlayer().getUniqueId(); - @Nullable ISpecialInventory loaded = map.get(key); - - if (loaded == null) { - // Loaded inventory has already been removed. Removal will handle saving if necessary. - return; - } - - // This should only be possible if a plugin is doing funky things with our inventories. - if (loaded != inventory) { - Inventory bukkitInventory = inventory.getBukkitInventory(); - // Just in case, respect contents of the inventory that was just used. - loaded.getBukkitInventory().setContents(bukkitInventory.getContents()); - // We need to close this inventory to reduce risk of duplication bugs if the user is offline. - // We don't want to risk recursively closing the same inventory repeatedly, so we schedule dumping viewers. - // Worst case we schedule a couple redundant tasks if several people had the inventory open. - if (inventory.isInUse()) { - getServer().getScheduler().runTask(this, () -> ejectViewers(inventory, viewer -> true)); - } - } - - // Schedule task to check in use status later this tick. Closing user is still in viewer list. - getServer().getScheduler().runTask(this, () -> { - if (loaded.isInUse()) { - return; - } - - // Re-fetch from map - prevents duplicate saves on multi-close. - ISpecialInventory current = map.remove(key); - - if (disableSaving() - || current == null - || !(current.getPlayer() instanceof Player player) - || player.isOnline()) { - return; - } - - if (!OpenEvents.saveCancelled(player, current)) { - this.accessor.getPlayerDataManager().inject(player).saveData(); - } - }); - } - - /** - * Method for handling a Player coming online. - * - * @param player the Player - * @throws IllegalStateException if the server version is unsupported - */ - void setPlayerOnline(@NotNull Player player) { - setPlayerOnline(inventories, player); - setPlayerOnline(enderChests, player); - - if (player.hasPlayedBefore()) { - return; - } - - // New player may have a name that already points to someone else in lookup cache. - String name = player.getName(); - this.offlineLookUpCache.invalidate(name); - - // If no offline matches are mapped, don't hit scheduler. - if (this.offlineLookUpCache.size() == 0) { - return; - } - - // New player may also be a more exact match than one already in the cache. - // I.e. new player "lava1" is a better match for "lava" than "lava123" - // Player joins are already quite intensive, so this is run on a delay. - this.getServer().getScheduler().runTaskLaterAsynchronously(this, () -> { - Iterator> iterator = this.offlineLookUpCache.asMap().entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - String oldMatch = entry.getValue().getName(); - - // Shouldn't be possible - all profiles should be complete. - if (oldMatch == null) { - iterator.remove(); - continue; - } - - String lookup = entry.getKey(); - float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch); - float newMatchScore = StringMetric.compareJaroWinkler(lookup, name); - - // If new match exceeds old match, delete old match. - if (newMatchScore > oldMatchScore) { - iterator.remove(); - } - } - }, 101L); // Odd delay for pseudo load balancing; Player tasks are usually scheduled with full seconds. - } - - private void setPlayerOnline( - @NotNull Map map, - @NotNull Player player) { - ISpecialInventory inventory = map.get(player.getUniqueId()); - - if (inventory == null) { - // Inventory not open. - return; - } - - inventory.setPlayerOnline(player); - - // Eject viewers lacking permission. - ejectViewers( - inventory, - viewer -> - !Permissions.ACCESS_ONLINE.hasPermission(viewer) - || (!Permissions.ACCESS_CROSSWORLD.hasPermission(viewer) - && !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld()))); - } - - static void ejectViewers(@NotNull ISpecialInventory inventory, @NotNull Predicate<@NotNull HumanEntity> predicate) { - Inventory bukkitInventory = inventory.getBukkitInventory(); - for (HumanEntity viewer : new ArrayList<>(bukkitInventory.getViewers())) { - if (viewer.getUniqueId().equals(inventory.getPlayer().getUniqueId()) - && !viewer.getOpenInventory().getTopInventory().equals(bukkitInventory)) { - // Skip owner with other inventory open. They aren't actually a viewer. - continue; - } - if (predicate.test(viewer)) { - viewer.closeInventory(); - } - } + inventoryManager.unload(offline.getUniqueId()); } } diff --git a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java b/plugin/src/main/java/com/lishid/openinv/PlayerListener.java deleted file mode 100644 index ddf868ad..00000000 --- a/plugin/src/main/java/com/lishid/openinv/PlayerListener.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv; - -import com.google.errorprone.annotations.Keep; -import com.lishid.openinv.util.Permissions; -import com.lishid.openinv.util.lang.LanguageManager; -import org.bukkit.entity.Player; -import org.bukkit.event.Event.Result; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerChangedWorldEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.jetbrains.annotations.NotNull; - -class PlayerListener implements Listener { - - private final @NotNull OpenInv plugin; - private final @NotNull LanguageManager lang; - - PlayerListener(@NotNull OpenInv plugin, @NotNull LanguageManager lang) { - this.plugin = plugin; - this.lang = lang; - } - - @Keep - @EventHandler(priority = EventPriority.LOWEST) - private void onPlayerJoin(@NotNull PlayerJoinEvent event) { - plugin.setPlayerOnline(event.getPlayer()); - } - - @Keep - @EventHandler(priority = EventPriority.MONITOR) - private void onPlayerQuit(@NotNull PlayerQuitEvent event) { - plugin.setPlayerOffline(event.getPlayer()); - } - - @Keep - @EventHandler - private void onWorldChange(@NotNull PlayerChangedWorldEvent event) { - plugin.changeWorld(event.getPlayer()); - } - - @Keep - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - private void onPlayerInteract(@NotNull PlayerInteractEvent event) { - - // Do not cancel 3rd party plugins' custom events - if (!PlayerInteractEvent.class.equals(event.getClass())) { - return; - } - - if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking() - || event.useInteractedBlock() == Result.DENY || event.getClickedBlock() == null - || !plugin.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) { - return; - } - - Player player = event.getPlayer(); - boolean any = Permissions.CONTAINER_ANY.hasPermission(player) && plugin.getAnyContainerStatus(player); - boolean needsAny = plugin.getAnySilentContainer().isAnyContainerNeeded(event.getClickedBlock()); - - if (!any && needsAny) { - return; - } - - boolean silent = Permissions.CONTAINER_SILENT.hasPermission(player) && plugin.getSilentContainerStatus(player); - - // If anycontainer or silentcontainer is active - if (any || silent) { - if (plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) { - if (silent && needsAny) { - lang.sendSystemMessage(player, "messages.info.containerBlockedSilent"); - } else if (needsAny) { - lang.sendSystemMessage(player, "messages.info.containerBlocked"); - } else if (silent) { - lang.sendSystemMessage(player, "messages.info.containerSilent"); - } - } - event.setCancelled(true); - } - } - -} diff --git a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java similarity index 74% rename from plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java rename to plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java index 02e5b759..bc2f7906 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java @@ -14,13 +14,13 @@ * along with this program. If not, see . */ -package com.lishid.openinv.commands; +package com.lishid.openinv.command; -import com.lishid.openinv.IOpenInv; +import com.lishid.openinv.util.setting.PlayerToggle; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.setting.Toggles; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; -import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -30,16 +30,15 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.function.BiConsumer; -import java.util.function.Predicate; +import java.util.UUID; public class ContainerSettingCommand implements TabExecutor { - private final @NotNull IOpenInv plugin; + private final @NotNull Toggles toggles; private final @NotNull LanguageManager lang; - public ContainerSettingCommand(@NotNull IOpenInv plugin, @NotNull LanguageManager lang) { - this.plugin = plugin; + public ContainerSettingCommand(@NotNull Toggles toggles, @NotNull LanguageManager lang) { + this.toggles = toggles; this.lang = lang; } @@ -51,28 +50,28 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } boolean any = command.getName().startsWith("any"); - Predicate getSetting = any ? plugin::getAnyContainerStatus : plugin::getSilentContainerStatus; - BiConsumer setSetting = any ? plugin::setAnyContainerStatus : plugin::setSilentContainerStatus; + PlayerToggle toggle = any ? toggles.any() : toggles.silent(); + UUID playerId = player.getUniqueId(); if (args.length > 0) { args[0] = args[0].toLowerCase(Locale.ENGLISH); if (args[0].equals("on")) { - setSetting.accept(player, true); + toggle.set(playerId, true); } else if (args[0].equals("off")) { - setSetting.accept(player, false); + toggle.set(playerId, false); } else if (!args[0].equals("check")) { // Invalid argument, show usage. return false; } } else { - setSetting.accept(player, !getSetting.test(player)); + toggle.set(playerId, !toggle.is(playerId)); } - String onOff = lang.getLocalizedMessage(player, getSetting.test(player) ? "messages.info.on" : "messages.info.off"); + String onOff = lang.getLocalizedMessage(player, toggle.is(playerId) ? "messages.info.on" : "messages.info.off"); if (onOff == null) { - onOff = String.valueOf(getSetting.test(player)); + onOff = String.valueOf(toggle.is(playerId)); } lang.sendMessage( diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java similarity index 88% rename from plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java rename to plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index f082c959..2050b384 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -14,12 +14,15 @@ * along with this program. If not, see . */ -package com.lishid.openinv.commands; +package com.lishid.openinv.command; import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.util.InventoryManager; import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.PlayerLoader; import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.config.Config; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; import org.bukkit.OfflinePlayer; @@ -40,13 +43,24 @@ public class OpenInvCommand implements TabExecutor { private final @NotNull OpenInv plugin; + private final @NotNull Config config; + private final @NotNull InventoryManager manager; private final @NotNull LanguageManager lang; + private final @NotNull PlayerLoader playerLoader; private final HashMap openInvHistory = new HashMap<>(); private final HashMap openEnderHistory = new HashMap<>(); - public OpenInvCommand(@NotNull OpenInv plugin, @NotNull LanguageManager lang) { + public OpenInvCommand( + @NotNull OpenInv plugin, + @NotNull Config config, + @NotNull InventoryManager manager, + @NotNull LanguageManager lang, + @NotNull PlayerLoader playerLoader) { this.plugin = plugin; + this.config = config; + this.manager = manager; this.lang = lang; + this.playerLoader = playerLoader; } @Override @@ -64,7 +78,7 @@ public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Com } String noArgValue; - if (plugin.noArgsOpensSelf()) { + if (config.doesNoArgsOpenSelf()) { noArgValue = player.getUniqueId().toString(); } else { // History management @@ -87,7 +101,7 @@ public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Com new BukkitRunnable() { @Override public void run() { - final OfflinePlayer offlinePlayer = OpenInvCommand.this.plugin.matchPlayer(name); + final OfflinePlayer offlinePlayer = playerLoader.match(name); if (offlinePlayer == null || (!offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline())) { lang.sendMessage(player, "messages.error.invalidPlayer"); @@ -143,9 +157,9 @@ private void openInventory(final Player player, final OfflinePlayer target, bool boolean online = target.isOnline(); if (!online) { - if (!plugin.disableOfflineAccess() && Permissions.ACCESS_OFFLINE.hasPermission(player)) { + if (!config.isOfflineDisabled() && Permissions.ACCESS_OFFLINE.hasPermission(player)) { // Try loading the player's data - onlineTarget = this.plugin.loadPlayer(target); + onlineTarget = playerLoader.load(target); } else { lang.sendMessage(player, "messages.error.permissionPlayerOffline"); return; @@ -203,7 +217,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool } } - if (!plugin.noArgsOpensSelf()) { + if (!config.doesNoArgsOpenSelf()) { // Record the target (openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); } @@ -211,7 +225,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool // Create the inventory final ISpecialInventory inv; try { - inv = openinv ? this.plugin.getSpecialInventory(onlineTarget, online) : this.plugin.getSpecialEnderChest(onlineTarget, online); + inv = openinv ? manager.getInventory(onlineTarget) : manager.getEnderChest(onlineTarget); } catch (Exception e) { lang.sendMessage(player, "messages.error.commandException"); plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java similarity index 99% rename from plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java rename to plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java index fd61fc22..e0a7a56d 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.commands; +package com.lishid.openinv.command; import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.LanguageManager; diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java similarity index 97% rename from plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java rename to plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java index 3e66acca..5dcf1b69 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.commands; +package com.lishid.openinv.command; import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.LanguageManager; @@ -46,9 +46,9 @@ */ public class SearchEnchantCommand implements TabExecutor { - private final LanguageManager lang; + private final @NotNull LanguageManager lang; - public SearchEnchantCommand(LanguageManager lang) { + public SearchEnchantCommand(@NotNull LanguageManager lang) { this.lang = lang; } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java similarity index 96% rename from plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java rename to plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java index 234d4ead..f45034bc 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.lishid.openinv.commands; +package com.lishid.openinv.command; import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.LanguageManager; @@ -34,9 +34,9 @@ public class SearchInvCommand implements TabExecutor { - private final LanguageManager lang; + private final @NotNull LanguageManager lang; - public SearchInvCommand(LanguageManager lang) { + public SearchInvCommand(@NotNull LanguageManager lang) { this.lang = lang; } diff --git a/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java b/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java new file mode 100644 index 00000000..ecc708da --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2011-2022 lishid. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.listener; + +import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.internal.ViewOnly; +import com.lishid.openinv.util.InternalAccessor; +import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.setting.Toggles; +import com.lishid.openinv.util.lang.LanguageManager; +import org.bukkit.GameMode; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryInteractEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +/** + * A listener managing AnyContainer, SilentContainer, and more. + */ +public class ContainerListener implements Listener { + + private final @NotNull InternalAccessor accessor; + private final @NotNull Toggles toggles; + private final @NotNull LanguageManager lang; + + public ContainerListener(@NotNull InternalAccessor accessor, @NotNull Toggles toggles, @NotNull LanguageManager lang) { + this.accessor = accessor; + this.toggles = toggles; + this.lang = lang; + } + + @Keep + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + private void onPlayerInteract(@NotNull PlayerInteractEvent event) { + // Ignore events from other plugins. + if (!PlayerInteractEvent.class.equals(event.getClass())) { + return; + } + + if (event.getAction() != Action.RIGHT_CLICK_BLOCK + || event.getPlayer().isSneaking() + || event.useInteractedBlock() == Result.DENY + || event.getClickedBlock() == null + || !accessor.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) { + return; + } + + Player player = event.getPlayer(); + UUID playerId = player.getUniqueId(); + boolean any = Permissions.CONTAINER_ANY.hasPermission(player) && toggles.any().is(playerId); + boolean needsAny = accessor.getAnySilentContainer().isAnyContainerNeeded(event.getClickedBlock()); + + if (!any && needsAny) { + return; + } + + boolean silent = Permissions.CONTAINER_SILENT.hasPermission(player) && toggles.silent().is(playerId); + + // If anycontainer or silentcontainer is active + if (any || silent) { + if (accessor.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) { + if (silent && needsAny) { + lang.sendSystemMessage(player, "messages.info.containerBlockedSilent"); + } else if (needsAny) { + lang.sendSystemMessage(player, "messages.info.containerBlocked"); + } else if (silent) { + lang.sendSystemMessage(player, "messages.info.containerSilent"); + } + } + event.setCancelled(true); + } + } + + @Keep + @EventHandler + private void onInventoryClose(@NotNull final InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player player)) { + return; + } + + InventoryHolder holder = event.getInventory().getHolder(); + if (toggles.silent().is(player.getUniqueId()) + && holder != null + && this.accessor.getAnySilentContainer().isAnySilentContainer(holder)) { + this.accessor.getAnySilentContainer().deactivateContainer(player); + } + } + + @Keep + @EventHandler(priority = EventPriority.LOWEST) + private void onInventoryClick(@NotNull final InventoryClickEvent event) { + handleInventoryInteract(event); + } + + @Keep + @EventHandler(priority = EventPriority.LOWEST) + private void onInventoryDrag(@NotNull final InventoryDragEvent event) { + handleInventoryInteract(event); + } + + private void handleInventoryInteract(@NotNull final InventoryInteractEvent event) { + HumanEntity entity = event.getWhoClicked(); + + // Un-cancel spectator interactions. + if (entity.getGameMode() == GameMode.SPECTATOR && Permissions.SPECTATE_CLICK.hasPermission(entity)) { + event.setCancelled(false); + } + + if (event.isCancelled()) { + return; + } + + Inventory inventory = event.getView().getTopInventory(); + if (inventory instanceof ViewOnly) { + event.setCancelled(true); + } + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java b/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java similarity index 76% rename from plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java rename to plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java index a92c92e6..b503788a 100644 --- a/plugin/src/main/java/com/lishid/openinv/LegacyInventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java @@ -1,12 +1,12 @@ -package com.lishid.openinv; +package com.lishid.openinv.listener; import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; -import org.bukkit.GameMode; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -14,11 +14,9 @@ import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.inventory.InventoryInteractEvent; import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -29,35 +27,19 @@ import java.util.Set; import java.util.stream.Collectors; -record LegacyInventoryListener(OpenInv plugin) implements Listener { +/** + * A listener used to enable functionality and prevent issues on versions < 1.21. + */ +public class LegacyInventoryListener implements Listener { - @Keep - @EventHandler - private void onInventoryClose(@NotNull final InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player player)) { - return; - } + private final @NotNull OpenInv plugin; - InventoryHolder holder = event.getInventory().getHolder(); - if (this.plugin.getSilentContainerStatus(player) - && holder != null - && this.plugin.getAnySilentContainer().isAnySilentContainer(holder)) { - this.plugin.getAnySilentContainer().deactivateContainer(player); - } - - ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory()); - if (specialInventory != null) { - this.plugin.handleCloseInventory(specialInventory); - } else { - specialInventory = InventoryAccess.getPlayerInventory(event.getInventory()); - if (specialInventory != null) { - this.plugin.handleCloseInventory(specialInventory); - } - } + public LegacyInventoryListener(@NotNull OpenInv plugin) { + this.plugin = plugin; } @Keep - @EventHandler(priority = EventPriority.LOWEST) + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) private void onInventoryClick(@NotNull final InventoryClickEvent event) { if (handleInventoryInteract(event)) { return; @@ -86,13 +68,18 @@ private void onInventoryClick(@NotNull final InventoryClickEvent event) { } @Keep - @EventHandler(priority = EventPriority.LOWEST) + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) private void onInventoryDrag(@NotNull final InventoryDragEvent event) { if (handleInventoryInteract(event)) { return; } InventoryView view = event.getView(); + + if (view.getCursor() == null) { + return; + } + int topSize = view.getTopInventory().getSize(); // Get bottom inventory active slots as player inventory slots. @@ -123,15 +110,29 @@ private void onInventoryDrag(@NotNull final InventoryDragEvent event) { return; } - ItemStack cursor = event.getCursor(); - if (cursor != null) { - cursor.setAmount(cursor.getAmount() + overlapLosses); - } else { - cursor = event.getOldCursor().clone(); - cursor.setAmount(overlapLosses); - } + final ItemStack lost = view.getCursor().clone(); + lost.setAmount(overlapLosses); + + // Re-add the lost items in the same tick after the event has completed. + plugin.getServer().getScheduler().runTask(plugin, () -> { + InventoryView currentOpen = event.getWhoClicked().getOpenInventory(); + + if (!currentOpen.equals(view)) { + event.getWhoClicked().getWorld().dropItem(event.getWhoClicked().getLocation(), lost).setPickupDelay(0); + return; + } + + ItemStack cursor = currentOpen.getCursor(); - event.setCursor(cursor); + if (cursor == null) { + currentOpen.setCursor(lost); + } else if (lost.isSimilar(cursor)) { + cursor.setAmount(cursor.getAmount() + lost.getAmount()); + currentOpen.setCursor(cursor); + } else { + event.getWhoClicked().getWorld().dropItem(event.getWhoClicked().getLocation(), lost).setPickupDelay(0); + } + }); } private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result) { @@ -151,15 +152,6 @@ private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) { HumanEntity entity = event.getWhoClicked(); - // Un-cancel spectator interactions. - if (Permissions.SPECTATE_CLICK.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) { - event.setCancelled(false); - } - - if (event.isCancelled()) { - return true; - } - Inventory inventory = event.getView().getTopInventory(); ISpecialInventory backing = InventoryAccess.getInventory(inventory); Permissions editSelf; diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 55cea7b0..1a207585 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -21,11 +21,13 @@ import com.lishid.openinv.internal.Accessor; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -121,32 +123,6 @@ public String getReleasesLink() { return "https://github.com/Jikoo/OpenInv/releases"; } - /** - * Creates an instance of the IAnySilentContainer implementation for the current server version. - * - * @return the IAnySilentContainer - * @throws IllegalStateException if server version is unsupported - */ - public @NotNull IAnySilentContainer getAnySilentContainer() { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.getAnySilentContainer(); - } - - /** - * Creates an instance of the IPlayerDataManager implementation for the current server version. - * - * @return the IPlayerDataManager - * @throws IllegalStateException if server version is unsupported - */ - public @NotNull PlayerManager getPlayerDataManager() { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.getPlayerManager(); - } - /** * Reload internal features. */ @@ -174,13 +150,46 @@ public boolean isSupported() { return internal != null; } + /** + * Get the instance of the IAnySilentContainer implementation for the current server version. + * + * @return the IAnySilentContainer + * @throws IllegalStateException if server version is unsupported + */ + public @NotNull IAnySilentContainer getAnySilentContainer() { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); + } + return internal.getAnySilentContainer(); + } + + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); + } + return internal.getPlayerManager().openInventory(player, inventory); + } + + /** + * Get the instance of the IPlayerDataManager implementation for the current server version. + * + * @return the IPlayerDataManager + * @throws IllegalStateException if server version is unsupported + */ + @NotNull PlayerManager getPlayerDataManager() { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); + } + return internal.getPlayerManager(); + } + /** * Creates an instance of the ISpecialEnderChest implementation for the given Player. * * @param player the Player * @return the ISpecialEnderChest created */ - public ISpecialEnderChest newSpecialEnderChest(final Player player) { + ISpecialEnderChest createEnderChest(final Player player) { if (internal == null) { throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } @@ -193,7 +202,7 @@ public ISpecialEnderChest newSpecialEnderChest(final Player player) { * @param player the Player * @return the ISpecialPlayerInventory created */ - public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player) { + ISpecialPlayerInventory createInventory(final Player player) { if (internal == null) { throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java new file mode 100644 index 00000000..254a53e0 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -0,0 +1,271 @@ +package com.lishid.openinv.util; + +import com.github.jikoo.planarwrappers.util.version.BukkitVersions; +import com.github.jikoo.planarwrappers.util.version.Version; +import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.event.OpenEvents; +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.stream.Stream; + +/** + * A manager for special inventories. Delegates creation and tracks copies in use. + */ +public class InventoryManager implements Listener { + + private final Map inventories = new ConcurrentHashMap<>(); + private final Map enderChests = new ConcurrentHashMap<>(); + private final Set expectedCloses = new HashSet<>(); + private final @NotNull OpenInv plugin; + private final @NotNull InternalAccessor accessor; + + public InventoryManager(@NotNull OpenInv plugin, @NotNull InternalAccessor accessor) { + this.plugin = plugin; + this.accessor = accessor; + } + + public void evictAll() { + Stream.concat(inventories.values().stream(), enderChests.values().stream()) + .map(inventory -> { + // Rather than iterate twice, evict all viewers during remapping. + for (HumanEntity viewer : List.copyOf(inventory.getBukkitInventory().getViewers())) { + expectedCloses.add(viewer.getUniqueId()); + viewer.closeInventory(); + } + // If saving is prevented, return a null value for the player to save. + if (plugin.disableSaving() || OpenEvents.saveCancelled(inventory)) { + return null; + } + if (inventory.getPlayer() instanceof Player player) { + return player; + } + return null; + }) + .filter(Objects::nonNull) + .distinct() + .forEach(player -> { + if (!player.isOnline()) { + accessor.getPlayerDataManager().inject(player).saveData(); + } + }); + inventories.clear(); + enderChests.clear(); + expectedCloses.clear(); + } + + public @NotNull ISpecialPlayerInventory getInventory(@NotNull Player player) { + return inventories.computeIfAbsent(player.getUniqueId(), uuid -> accessor.createInventory(player)); + } + + public @NotNull ISpecialEnderChest getEnderChest(@NotNull Player player) { + return enderChests.computeIfAbsent(player.getUniqueId(), uuid -> accessor.createEnderChest(player)); + } + + public @Nullable Player getLoadedPlayer(@NotNull UUID uuid) { + ISpecialInventory inUse = inventories.get(uuid); + if (inUse != null) { + return (Player) inUse.getPlayer(); + } + inUse = enderChests.get(uuid); + if (inUse != null) { + return (Player) inUse.getPlayer(); + } + return null; + } + + public void unload(@NotNull UUID uuid) { + inventories.computeIfPresent(uuid, this::remove); + enderChests.computeIfPresent(uuid, this::remove); + } + + @Keep + @EventHandler(priority = EventPriority.LOWEST) + private void onPlayerJoin(@NotNull PlayerJoinEvent event) { + consumeLoaded(event.getPlayer().getUniqueId(), inventory -> checkViewerAccess(inventory, true)); + } + + @Keep + @EventHandler(priority = EventPriority.MONITOR) + private void onPlayerQuit(@NotNull PlayerQuitEvent event) { + consumeLoaded(event.getPlayer().getUniqueId(), inventory -> checkViewerAccess(inventory, false)); + } + + @Keep + @EventHandler + private void onWorldChanged(@NotNull PlayerChangedWorldEvent event) { + Player player = event.getPlayer(); + consumeLoaded(player.getUniqueId(), inventory -> checkViewerAccess(inventory, player.isOnline())); + } + + @Keep + @EventHandler + private void onInventoryClose(@NotNull InventoryCloseEvent event) { + ISpecialInventory inventory = InventoryAccess.getInventory(event.getInventory()); + + // If this is not an ISpecialInventory or the inventory was closed elsewhere internally, don't handle. + if (inventory == null || expectedCloses.remove(event.getPlayer().getUniqueId())) { + return; + } + + // Fetch the active ISpecialInventory of this type. + Map map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests; + UUID key = inventory.getPlayer().getUniqueId(); + ISpecialInventory loaded = map.get(key); + + // If there is no loaded inventory, it has already been removed and saved. + if (loaded == null) { + return; + } + + // This should only be possible if a plugin is going to extreme lengths to mess with our inventories. + if (loaded != inventory) { + // Immediately remove affected inventory, then dump all viewers. We don't want to risk duplication bugs. + map.remove(key); + remove(key, loaded); + remove(key, inventory); + // The loaded one is "correct" as far as we're concerned, so save that. + save(loaded); + } + + // Schedule task to check in use status later this tick. Closing user is still in viewer list. + plugin.getServer().getScheduler().runTask(plugin, () -> { + if (loaded.isInUse()) { + return; + } + + // Re-fetch from map to reduce odds of a duplicate save. + ISpecialInventory current = map.remove(key); + + if (current != null) { + save(current); + } + }); + } + + @Keep + @EventHandler(priority = EventPriority.HIGHEST) + private void onInventoryOpen(@NotNull InventoryOpenEvent event) { + ISpecialInventory inventory = InventoryAccess.getInventory(event.getInventory()); + if (inventory == null) { + return; + } + + Map map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests; + UUID key = inventory.getPlayer().getUniqueId(); + ISpecialInventory loaded = map.get(key); + + if (!inventory.equals(loaded)) { + event.setCancelled(true); + plugin.getLogger().log( + Level.WARNING, + "Prevented a plugin from opening an untracked ISpecialInventory!", + new Throwable("Untracked ISpecialInventory")); + } + } + + private void checkViewerAccess(@NotNull T inventory, boolean online) { + + Player owner = (Player) inventory.getPlayer(); + Permissions connectedState = online ? Permissions.ACCESS_ONLINE : Permissions.ACCESS_OFFLINE; + boolean alwaysDenied = !online && plugin.disableOfflineAccess(); + + // Copy viewers so we don't modify the list we're iterating over when closing inventories. + List viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers()); + // Legacy: Owner is always a viewer of own inventory. + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { + if (inventory instanceof ISpecialPlayerInventory) { + Inventory active = owner.getOpenInventory().getTopInventory(); + if (!active.equals(inventory.getBukkitInventory())) { + viewers.remove(owner); + } + } + } + + for (HumanEntity viewer : viewers) { + if (alwaysDenied + || !connectedState.hasPermission(viewer) + || (!Objects.equals(owner.getWorld(), viewer.getWorld()) && !Permissions.ACCESS_CROSSWORLD.hasPermission(viewer))) { + expectedCloses.add(viewer.getUniqueId()); + viewer.closeInventory(); + } + } + } + + private void consumeLoaded(@NotNull UUID key, @NotNull Consumer<@NotNull ISpecialInventory> consumer) { + boolean saved = consumeLoaded(inventories, key, false, consumer); + consumeLoaded(enderChests, key, saved, consumer); + } + + private boolean consumeLoaded( + @NotNull Map map, + @NotNull UUID key, + boolean saved, + @NotNull Consumer<@NotNull ISpecialInventory> consumer) { + T inventory = map.get(key); + + if (inventory == null) { + return saved; + } + + consumer.accept(inventory); + if (!inventory.isInUse()) { + map.remove(key); + + if (!saved) { + save(inventory); + return true; + } + } + + return saved; + } + + private void save(@NotNull ISpecialInventory inventory) { + if (plugin.disableSaving()) { + return; + } + + Player player = (Player) inventory.getPlayer(); + + if (!player.isOnline() && !OpenEvents.saveCancelled(inventory)) { + accessor.getPlayerDataManager().inject(player).saveData(); + } + } + + @Contract("_, _ -> null") + private @Nullable T remove(@NotNull UUID key, @NotNull T inventory) { + for (HumanEntity viewer : List.copyOf(inventory.getBukkitInventory().getViewers())) { + expectedCloses.add(viewer.getUniqueId()); + viewer.closeInventory(); + } + return null; + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java new file mode 100644 index 00000000..4ecf788d --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java @@ -0,0 +1,222 @@ +package com.lishid.openinv.util; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.util.config.Config; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.profile.PlayerProfile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A utility for looking up and loading players. + */ +public class PlayerLoader implements Listener { + + private final @NotNull Plugin plugin; + private final @NotNull Config config; + private final @NotNull InventoryManager inventoryManager; + private final @NotNull InternalAccessor internalAccessor; + private final @NotNull Logger logger; + private final @NotNull Cache lookupCache; + + public PlayerLoader( + @NotNull Plugin plugin, + @NotNull Config config, + @NotNull InventoryManager inventoryManager, + @NotNull InternalAccessor internalAccessor, + @NotNull Logger logger) { + this.plugin = plugin; + this.config = config; + this.inventoryManager = inventoryManager; + this.internalAccessor = internalAccessor; + this.logger = logger; + this.lookupCache = CacheBuilder.newBuilder().maximumSize(20).build(); + } + + /** + * Load a {@link Player} from an {@link OfflinePlayer}. If the user has not played before or the default world for + * the server is not loaded, this will return {@code null}. + * + * @param offline the {@code OfflinePlayer} to load a {@code Player} for + * @return the loaded {@code Player} + * @throws IllegalStateException if the server version is unsupported + */ + public @Nullable Player load(@NotNull OfflinePlayer offline) { + UUID key = offline.getUniqueId(); + + Player player = offline.getPlayer(); + if (player != null) { + return player; + } + + player = inventoryManager.getLoadedPlayer(key); + if (player != null) { + return player; + } + + if (config.isOfflineDisabled() || !internalAccessor.isSupported()) { + return null; + } + + if (Bukkit.isPrimaryThread()) { + return internalAccessor.getPlayerDataManager().loadPlayer(offline); + } + + Future future = Bukkit.getScheduler().callSyncMethod(plugin, + () -> internalAccessor.getPlayerDataManager().loadPlayer(offline)); + + try { + player = future.get(); + } catch (InterruptedException | ExecutionException e) { + logger.log(Level.WARNING, e.getMessage(), e); + return null; + } + + return player; + } + + public @Nullable OfflinePlayer match(@NotNull String name) { + // Warn if called on the main thread - if we resort to searching offline players, this may take several seconds. + if (Bukkit.getServer().isPrimaryThread()) { + logger.warning("Call to PlayerSearchCache#matchPlayer made on the main thread!"); + logger.warning("This can cause the server to hang, potentially severely."); + logger.log(Level.WARNING, "Current stack trace", new Throwable("Current stack trace")); + } + + OfflinePlayer player; + + try { + UUID uuid = UUID.fromString(name); + player = Bukkit.getOfflinePlayer(uuid); + // Ensure player is an existing player. + if (player.hasPlayedBefore() || player.isOnline()) { + return player; + } + // Return null otherwise. + return null; + } catch (IllegalArgumentException ignored) { + // Not a UUID + } + + // Exact online match first. + player = Bukkit.getServer().getPlayerExact(name); + + if (player != null) { + return player; + } + + // Cached offline match. + PlayerProfile cachedResult = lookupCache.getIfPresent(name); + if (cachedResult != null && cachedResult.getUniqueId() != null) { + player = Bukkit.getOfflinePlayer(cachedResult.getUniqueId()); + // Ensure player is an existing player. + if (player.hasPlayedBefore() || player.isOnline()) { + return player; + } + // Return null otherwise. + return null; + } + + // Exact offline match second - ensure offline access works when matchable users are online. + player = Bukkit.getServer().getOfflinePlayer(name); + + if (player.hasPlayedBefore()) { + lookupCache.put(name, player.getPlayerProfile()); + return player; + } + + // Inexact online match. + player = Bukkit.getServer().getPlayer(name); + + if (player != null) { + return player; + } + + // Finally, inexact offline match. + float bestMatch = 0; + for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) { + if (offline.getName() == null) { + // Loaded by UUID only, name has never been looked up. + continue; + } + + float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName()); + + if (currentMatch == 1.0F) { + return offline; + } + + if (currentMatch > bestMatch) { + bestMatch = currentMatch; + player = offline; + } + } + + if (player != null) { + // If a match was found, store it. + lookupCache.put(name, player.getPlayerProfile()); + return player; + } + + // No players have ever joined the server. + return null; + } + + @Keep + @EventHandler + private void updateMatches(@NotNull PlayerJoinEvent event) { + // If player is not new, any cached values are valid. + if (event.getPlayer().hasPlayedBefore()) { + return; + } + + // New player may have a name that already points to someone else in lookup cache. + String name = event.getPlayer().getName(); + lookupCache.invalidate(name); + + // If the cache is empty, nothing to do. Don't hit scheduler. + if (lookupCache.size() == 0) { + return; + } + + plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, () -> { + Iterator> iterator = lookupCache.asMap().entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String oldMatch = entry.getValue().getName(); + + // Shouldn't be possible - all profiles should be complete. + if (oldMatch == null) { + iterator.remove(); + continue; + } + + String lookup = entry.getKey(); + float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch); + float newMatchScore = StringMetric.compareJaroWinkler(lookup, name); + + // If new match exceeds old match, delete old match. + if (newMatchScore > oldMatchScore) { + iterator.remove(); + } + } + }, 7L); + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/config/Config.java b/plugin/src/main/java/com/lishid/openinv/util/config/Config.java new file mode 100644 index 00000000..159ca8b3 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/config/Config.java @@ -0,0 +1,31 @@ +package com.lishid.openinv.util.config; + +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.MemoryConfiguration; +import org.jetbrains.annotations.NotNull; + +public class Config { + + private @NotNull Configuration root; + + public Config() { + root = new MemoryConfiguration(); + } + + public void reload(@NotNull Configuration configuration) { + root = configuration; + } + + public boolean isSaveDisabled() { + return root.getBoolean("settings.disable-saving", false); + } + + public boolean isOfflineDisabled() { + return root.getBoolean("settings.disable-offline-access", false); + } + + public boolean doesNoArgsOpenSelf() { + return root.getBoolean("settings.command.open.no-args-opens-self", false); + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java similarity index 99% rename from plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java rename to plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java index a657f849..6e86ee66 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java @@ -14,16 +14,17 @@ * along with this program. If not, see . */ -package com.lishid.openinv.util; +package com.lishid.openinv.util.config; import com.lishid.openinv.OpenInv; +import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.ConfigurationSection; + import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.ConfigurationSection; public record ConfigUpdater(OpenInv plugin) { diff --git a/plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java b/plugin/src/main/java/com/lishid/openinv/util/lang/LangMigrator.java similarity index 98% rename from plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java rename to plugin/src/main/java/com/lishid/openinv/util/lang/LangMigrator.java index 348e0d5a..2ae68159 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/LangMigrator.java +++ b/plugin/src/main/java/com/lishid/openinv/util/lang/LangMigrator.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.util; +package com.lishid.openinv.util.lang; import org.jetbrains.annotations.NotNull; diff --git a/plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java b/plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java new file mode 100644 index 00000000..85c1eb3a --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java @@ -0,0 +1,11 @@ +package com.lishid.openinv.util.setting; + +import java.util.UUID; + +public interface PlayerToggle { + + boolean is(UUID uuid); + + void set(UUID uuid, boolean value); + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java b/plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java new file mode 100644 index 00000000..a830ecdf --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java @@ -0,0 +1,59 @@ +package com.lishid.openinv.util.setting; + +import org.bukkit.configuration.MemoryConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public abstract class Toggles { + + private static final String TOGGLE_ANYCONTAINER_BASE = "toggles.any-chest."; + private static final String TOGGLE_SILENTCONTAINER_BASE = "toggles.silent-chest."; + + private final PlayerToggle any = new PlayerToggle() { + @Override + public boolean is(UUID uuid) { + return configuration.getBoolean(TOGGLE_ANYCONTAINER_BASE + uuid, false); + } + + @Override + public void set(UUID uuid, boolean value) { + configuration.set(TOGGLE_ANYCONTAINER_BASE + uuid, value); + save(); + } + }; + + private final PlayerToggle silent = new PlayerToggle() { + @Override + public boolean is(UUID uuid) { + return configuration.getBoolean(TOGGLE_SILENTCONTAINER_BASE + uuid, false); + } + + @Override + public void set(UUID uuid, boolean value) { + configuration.set(TOGGLE_SILENTCONTAINER_BASE + uuid, value); + save(); + } + }; + + private @NotNull MemoryConfiguration configuration; + + public Toggles() { + this.configuration = new MemoryConfiguration(); + } + + public PlayerToggle any() { + return any; + } + + public PlayerToggle silent() { + return silent; + } + + public void reload(@NotNull MemoryConfiguration configuration) { + this.configuration = configuration; + } + + public abstract void save(); + +} From 5fea7f3e02b17f170247aab02197ab0a9a38c037 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 28 Jul 2024 11:03:44 -0400 Subject: [PATCH 200/340] Make toggles transient (#227) The addon OITogglePersist restores old behavior. --- .github/workflows/ci.yml | 2 +- addon/togglepersist/pom.xml | 72 ++++++++ .../openinv/togglepersist/TogglePersist.java | 143 +++++++++++++++ .../src/main/resources/plugin.yml | 6 + api/pom.xml | 2 +- .../openinv/event/PlayerToggledEvent.java | 71 ++++++++ .../openinv/util/setting/PlayerToggle.java | 36 ++++ .../openinv/util/setting/PlayerToggles.java | 106 +++++++++++ common/pom.xml | 2 +- .../com/lishid/openinv/event/OpenEvents.java | 7 + internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- .../container/menu/OpenChestMenu.java | 8 +- .../container/menu/OpenInventoryMenu.java | 6 +- plugin/pom.xml | 8 +- .../main/java/com/lishid/openinv/OpenInv.java | 64 ++++--- .../command/ContainerSettingCommand.java | 32 ++-- .../openinv/listener/ContainerListener.java | 12 +- .../openinv/listener/ToggleListener.java | 22 +++ .../openinv/util/config/ConfigUpdater.java | 165 +++++++----------- .../openinv/util/setting/PlayerToggle.java | 11 -- .../lishid/openinv/util/setting/Toggles.java | 59 ------- plugin/src/main/resources/config.yml | 4 +- pom.xml | 3 +- 25 files changed, 608 insertions(+), 239 deletions(-) create mode 100644 addon/togglepersist/pom.xml create mode 100644 addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java create mode 100644 addon/togglepersist/src/main/resources/plugin.yml create mode 100644 api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java create mode 100644 api/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java create mode 100644 api/src/main/java/com/lishid/openinv/util/setting/PlayerToggles.java create mode 100644 plugin/src/main/java/com/lishid/openinv/listener/ToggleListener.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c4f89bc..3fe7fd56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: dist - path: ./target/OpenInv.jar + path: ./target/*.jar - name: Upload API Jar id: upload-api uses: actions/upload-artifact@v4 diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml new file mode 100644 index 00000000..8d59bea6 --- /dev/null +++ b/addon/togglepersist/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + openinvparent + com.lishid + ../../pom.xml + 5.1.0-SNAPSHOT + + + openinvtogglepersist + + + + annotations + org.jetbrains + + + org.spigotmc + spigot-api + + + openinvapi + com.lishid + provided + + + + + OITogglePersist + + + src/main/resources + true + + + + + + maven-compiler-plugin + + + maven-resources-plugin + + + copy-final-jar + package + + copy-resources + + + ${project.parent.build.directory} + + + ${project.build.directory} + + ${project.build.finalName}.jar + + + + + + + + + + + + diff --git a/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java b/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java new file mode 100644 index 00000000..070ff081 --- /dev/null +++ b/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java @@ -0,0 +1,143 @@ +package com.github.jikoo.openinv.togglepersist; + +import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.event.PlayerToggledEvent; +import com.lishid.openinv.util.setting.PlayerToggle; +import com.lishid.openinv.util.setting.PlayerToggles; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +public class TogglePersist extends JavaPlugin implements Listener { + + private final Map> enabledToggles = new HashMap<>(); + + @Override + public void onEnable() { + getServer().getPluginManager().registerEvents(this, this); + + File file = new File(getDataFolder(), "toggles.yml"); + + // If there's no save file, there's nothing to load. + if (!file.exists()) { + return; + } + + Configuration loaded = YamlConfiguration.loadConfiguration(file); + + // For each toggle, enable loaded players. + for (String toggleName : loaded.getKeys(false)) { + PlayerToggle toggle = PlayerToggles.get(toggleName); + // Ensure toggle exists. + if (toggle == null) { + continue; + } + + for (String idString : loaded.getStringList(toggleName)) { + // Ensure valid UUID. + UUID uuid; + try { + uuid = UUID.fromString(idString); + } catch (IllegalArgumentException e) { + continue; + } + + // Track that toggle is enabled. + set(uuid, toggleName); + } + } + } + + private void set(UUID playerId, String toggleName) { + enabledToggles.compute(playerId, (uuid, toggles) -> { + if (toggles == null) { + toggles = new HashSet<>(); + } + toggles.add(toggleName); + return toggles; + }); + } + + @Override + public void onDisable() { + Map> converted = getSaveData(); + + YamlConfiguration data = new YamlConfiguration(); + for (Map.Entry> playerToggle : converted.entrySet()) { + data.set(playerToggle.getKey(), playerToggle.getValue()); + } + + File file = new File(getDataFolder(), "toggles.yml"); + try { + data.save(file); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Unable to save player toggle states", e); + } + } + + private @NotNull Map> getSaveData() { + Map> converted = new HashMap<>(); + + for (Map.Entry> playerToggles : enabledToggles.entrySet()) { + String idString = playerToggles.getKey().toString(); + for (String toggleName : playerToggles.getValue()) { + // Add player ID to listing for each enabled toggle. + converted.compute(toggleName, (name, ids) -> { + if (ids == null) { + ids = new ArrayList<>(); + } + ids.add(idString); + return ids; + }); + } + } + return converted; + } + + @Keep + @EventHandler + private void onPlayerJoin(@NotNull PlayerJoinEvent event) { + UUID playerId = event.getPlayer().getUniqueId(); + Set toggleNames = enabledToggles.get(playerId); + + if (toggleNames == null) { + return; + } + + for (String toggleName : toggleNames) { + PlayerToggle toggle = PlayerToggles.get(toggleName); + if (toggle != null) { + toggle.set(playerId, true); + } + } + } + + @Keep + @EventHandler + private void onToggleSet(@NotNull PlayerToggledEvent event) { + if (event.isEnabled()) { + set(event.getPlayerId(), event.getToggle().getName()); + } else { + enabledToggles.computeIfPresent(event.getPlayerId(), (uuid, toggles) -> { + toggles.remove(event.getToggle().getName()); + return toggles.isEmpty() ? null : toggles; + }); + } + } + +} diff --git a/addon/togglepersist/src/main/resources/plugin.yml b/addon/togglepersist/src/main/resources/plugin.yml new file mode 100644 index 00000000..cf84916e --- /dev/null +++ b/addon/togglepersist/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +name: OITogglePersist +main: com.github.jikoo.openinv.togglepersist.TogglePersist +version: ${project.version} +author: Jikoo +description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions. +depend: [ OpenInv ] diff --git a/api/pom.xml b/api/pom.xml index b22bcca8..6469a94b 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT openinvapi diff --git a/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java b/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java new file mode 100644 index 00000000..82f23228 --- /dev/null +++ b/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java @@ -0,0 +1,71 @@ +package com.lishid.openinv.event; + +import com.google.errorprone.annotations.RestrictedApi; +import com.lishid.openinv.util.setting.PlayerToggle; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +/** + * Event fired after OpenInv modifies a toggleable setting for a player. + */ +public class PlayerToggledEvent extends Event { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final @NotNull PlayerToggle toggle; + private final @NotNull UUID uuid; + private final boolean enabled; + + @RestrictedApi( + explanation = "Constructor is not considered part of the API and may be subject to change.", + link = "", + allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java") + @ApiStatus.Internal + PlayerToggledEvent(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean enabled) { + this.toggle = toggle; + this.uuid = uuid; + this.enabled = enabled; + } + + /** + * Get the {@link PlayerToggle} affected. + * + * @return the toggle + */ + public @NotNull PlayerToggle getToggle() { + return toggle; + } + + /** + * Get the {@link UUID} of the player whose setting was changed. + * + * @return the player ID + */ + public @NotNull UUID getPlayerId() { + return uuid; + } + + /** + * Get whether the toggle is enabled. + * + * @return true if the toggle is enabled + */ + public boolean isEnabled() { + return enabled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + +} diff --git a/api/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java b/api/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java new file mode 100644 index 00000000..6e8dc880 --- /dev/null +++ b/api/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java @@ -0,0 +1,36 @@ +package com.lishid.openinv.util.setting; + +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +/** + * A per-player setting that may be enabled or disabled. + */ +public interface PlayerToggle { + + /** + * Get the name of the setting. + * + * @return the setting name + */ + @NotNull String getName(); + + /** + * Get the state of the toggle for a particular player ID. + * + * @param uuid the player ID + * @return true if the setting is enabled + */ + boolean is(@NotNull UUID uuid); + + /** + * Set the state of the toggle for a particular player ID. + * + * @param uuid the player ID + * @param enabled whether the setting is enabled + * @return true if the setting changed as a result of being set + */ + boolean set(@NotNull UUID uuid, boolean enabled); + +} diff --git a/api/src/main/java/com/lishid/openinv/util/setting/PlayerToggles.java b/api/src/main/java/com/lishid/openinv/util/setting/PlayerToggles.java new file mode 100644 index 00000000..d61cd115 --- /dev/null +++ b/api/src/main/java/com/lishid/openinv/util/setting/PlayerToggles.java @@ -0,0 +1,106 @@ +package com.lishid.openinv.util.setting; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Utility class containing all of OpenInv's {@link PlayerToggle PlayerToggles}. + */ +public final class PlayerToggles { + + private static final Map TOGGLES = new HashMap<>(); + private static final PlayerToggle ANY = add(new MemoryToggle("AnyContainer")); + private static final PlayerToggle SILENT = add(new MemoryToggle("SilentContainer")); + + /** + * Get the AnyContainer toggle. + * + * @return the AnyContainer toggle + */ + public static @NotNull PlayerToggle any() { + return ANY; + } + + /** + * Get the SilentContainer toggle. + * + * @return the SilentContainer toggle + */ + public static @NotNull PlayerToggle silent() { + return SILENT; + } + + /** + * Get a toggle by name. + * + * @param toggleName the name of the toggle + * @return the toggle, or null if no such toggle exists. + */ + public static @Nullable PlayerToggle get(@NotNull String toggleName) { + PlayerToggle toggle = TOGGLES.get(toggleName); + if (toggle == null) { + toggle = TOGGLES.get(toggleName.toLowerCase(Locale.ENGLISH)); + } + return toggle; + } + + /** + * Get an unmodifable view of all toggles available. + * + * @return a view of all toggles available + */ + public static @UnmodifiableView @NotNull Collection get() { + return Collections.unmodifiableCollection(TOGGLES.values()); + } + + private static @NotNull PlayerToggle add(@NotNull PlayerToggle toggle) { + TOGGLES.put(toggle.getName().toLowerCase(Locale.ENGLISH), toggle); + return toggle; + } + + private PlayerToggles() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + + private static class MemoryToggle implements PlayerToggle { + + private final @NotNull Set enabled; + private final @NotNull String name; + + private MemoryToggle(@NotNull String name) { + enabled = new HashSet<>(); + this.name = name; + } + + @Override + public @NotNull String getName() { + return this.name; + } + + @Override + public boolean is(@NotNull UUID uuid) { + return enabled.contains(uuid); + } + + @Override + public boolean set(@NotNull UUID uuid, boolean enabled) { + if (enabled) { + return this.enabled.add(uuid); + } else { + return this.enabled.remove(uuid); + } + } + + } + +} diff --git a/common/pom.xml b/common/pom.xml index 9218e348..caaaa9a0 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT openinvcommon diff --git a/common/src/main/java/com/lishid/openinv/event/OpenEvents.java b/common/src/main/java/com/lishid/openinv/event/OpenEvents.java index 1ad3a8a8..626372c2 100644 --- a/common/src/main/java/com/lishid/openinv/event/OpenEvents.java +++ b/common/src/main/java/com/lishid/openinv/event/OpenEvents.java @@ -1,12 +1,15 @@ package com.lishid.openinv.event; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.util.setting.PlayerToggle; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; +import java.util.UUID; + /** * Construct and call events. */ @@ -20,6 +23,10 @@ public static boolean saveCancelled(@NotNull ISpecialInventory inventory) { return call(new OpenPlayerSaveEvent((Player) inventory.getPlayer(), inventory)); } + public static void notifyPlayerToggle(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) { + Bukkit.getPluginManager().callEvent(new PlayerToggledEvent(toggle, uuid, state)); + } + private static boolean call(T event) { Bukkit.getPluginManager().callEvent(event); return event.isCancelled(); diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 9d04b233..7b4819bd 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 5127d6c8..db450049 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 7a14abf0..3b8cff46 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT openinvadapter1_21_R1 diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java index ca969983..a3b331ed 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java @@ -45,7 +45,7 @@ public abstract class OpenChestMenu> bukkitEntity; // Syncher fields private @Nullable ContainerSynchronizer synchronizer; private final List dataSlots = new ArrayList<>(); @@ -129,7 +129,7 @@ protected void preSlotSetup() {} @Override - public final @NotNull CraftInventoryView getBukkitView() { + public final @NotNull CraftInventoryView> getBukkitView() { if (bukkitEntity == null) { bukkitEntity = createBukkitEntity(); } @@ -137,14 +137,14 @@ protected void preSlotSetup() {} return bukkitEntity; } - protected @NotNull CraftInventoryView createBukkitEntity() { + protected @NotNull CraftInventoryView> createBukkitEntity() { Inventory top; if (viewOnly) { top = new OpenDummyInventory(container); } else { top = container.getBukkitInventory(); } - return new CraftInventoryView(viewer.getBukkitEntity(), top, this) { + return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) { @Override public @Nullable Inventory getInventory(int rawSlot) { if (viewOnly) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java index c80af6e2..3c11fe91 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java @@ -104,7 +104,7 @@ protected boolean checkViewOnly() { } @Override - protected @NotNull CraftInventoryView createBukkitEntity() { + protected @NotNull CraftInventoryView> createBukkitEntity() { org.bukkit.inventory.Inventory bukkitInventory; if (viewOnly) { bukkitInventory = new OpenDummyInventory(container); @@ -114,7 +114,7 @@ protected boolean checkViewOnly() { bukkitInventory = container.getBukkitInventory(); } - return new CraftInventoryView(viewer.getBukkitEntity(), bukkitInventory, this) { + return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) { @Override public org.bukkit.inventory.ItemStack getItem(int index) { if (viewOnly || index < 0) { @@ -174,7 +174,7 @@ public int convertSlot(int rawSlot) { if (slot >= topSize) { return super.getSlotType(offset + slot); } - return container.getSlotType(offset + slot); + return OpenInventoryMenu.this.container.getSlotType(offset + slot); } @Override diff --git a/plugin/pom.xml b/plugin/pom.xml index edad9226..9a4b4a36 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT compile com.lishid openinvadapter1_20_R4 - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT compile com.lishid openinvadapter1_20_R3 - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT compile diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 457bff17..3f74b3ec 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -29,13 +29,15 @@ import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.listener.ContainerListener; import com.lishid.openinv.listener.LegacyInventoryListener; +import com.lishid.openinv.listener.ToggleListener; import com.lishid.openinv.util.config.Config; import com.lishid.openinv.util.config.ConfigUpdater; import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.InventoryManager; import com.lishid.openinv.util.lang.LangMigrator; import com.lishid.openinv.util.PlayerLoader; -import com.lishid.openinv.util.setting.Toggles; +import com.lishid.openinv.util.setting.PlayerToggle; +import com.lishid.openinv.util.setting.PlayerToggles; import com.lishid.openinv.util.lang.LanguageManager; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; @@ -50,6 +52,7 @@ import org.jetbrains.annotations.Nullable; import java.nio.file.Path; +import java.util.Locale; import java.util.UUID; import java.util.function.Consumer; @@ -60,7 +63,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv { private InternalAccessor accessor; private Config config; - private Toggles toggles; private InventoryManager inventoryManager; private LanguageManager languageManager; private PlayerLoader playerLoader; @@ -70,7 +72,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv { public void reloadConfig() { super.reloadConfig(); config.reload(getConfig()); - toggles.reload(getConfig()); languageManager.reload(); if (accessor != null && accessor.isSupported()) { accessor.reload(getConfig()); @@ -103,12 +104,6 @@ public void onEnable() { // Set up configurable features. Note that #reloadConfig is called on the first call to #getConfig! // Configuration values should not be accessed until after all of these have been set up. config = new Config(); - toggles = new Toggles() { - @Override - public void save() { - saveConfig(); - } - }; languageManager = new LanguageManager(this, "en"); accessor = new InternalAccessor(getLogger(), languageManager); @@ -132,22 +127,11 @@ public void save() { // Update existing configuration. May require internal access. new ConfigUpdater(this).checkForUpdates(); - // Get plugin manager - PluginManager pm = this.getServer().getPluginManager(); - // Register listeners - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { - pm.registerEvents(new LegacyInventoryListener(this), this); - } - pm.registerEvents(playerLoader, this); - pm.registerEvents(inventoryManager, this); - pm.registerEvents(new ContainerListener(accessor, toggles, languageManager), this); + // Register relevant event listeners. + registerEvents(); - // Register commands to their executors - this.setCommandExecutor(new OpenInvCommand(this, config, inventoryManager, languageManager, playerLoader), "openinv", "openender"); - this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); - this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); - this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); - this.setCommandExecutor(new ContainerSettingCommand(toggles, languageManager), "silentcontainer", "anycontainer"); + // Register commands to their executors. + registerCommands(); } else { this.sendVersionError(this.getLogger()::warning); @@ -155,6 +139,30 @@ public void save() { } + private void registerEvents() { + PluginManager pluginManager = this.getServer().getPluginManager(); + // Legacy: extra listener for permission handling and self-view issue prevention. + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { + pluginManager.registerEvents(new LegacyInventoryListener(this), this); + } + pluginManager.registerEvents(playerLoader, this); + pluginManager.registerEvents(inventoryManager, this); + pluginManager.registerEvents(new ContainerListener(accessor, languageManager), this); + pluginManager.registerEvents(new ToggleListener(), this); + } + + private void registerCommands() { + this.setCommandExecutor(new OpenInvCommand(this, config, inventoryManager, languageManager, playerLoader), "openinv", "openender"); + this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); + this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); + this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); + + ContainerSettingCommand settingCommand = new ContainerSettingCommand(languageManager); + for (PlayerToggle toggle : PlayerToggles.get()) { + setCommandExecutor(settingCommand, toggle.getName().toLowerCase(Locale.ENGLISH)); + } + } + private void setCommandExecutor(@NotNull CommandExecutor executor, String @NotNull ... commands) { for (String commandName : commands) { PluginCommand command = this.getCommand(commandName); @@ -210,22 +218,22 @@ public boolean noArgsOpensSelf() { @Override public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) { - return toggles.any().is(offline.getUniqueId()); + return PlayerToggles.any().is(offline.getUniqueId()); } @Override public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { - toggles.any().set(offline.getUniqueId(), status); + PlayerToggles.any().set(offline.getUniqueId(), status); } @Override public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) { - return toggles.silent().is(offline.getUniqueId()); + return PlayerToggles.silent().is(offline.getUniqueId()); } @Override public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { - toggles.silent().set(offline.getUniqueId(), status); + PlayerToggles.silent().set(offline.getUniqueId(), status); } @Override diff --git a/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java index bc2f7906..af00131b 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java @@ -16,15 +16,17 @@ package com.lishid.openinv.command; +import com.lishid.openinv.event.OpenEvents; import com.lishid.openinv.util.setting.PlayerToggle; import com.lishid.openinv.util.TabCompleter; -import com.lishid.openinv.util.setting.Toggles; +import com.lishid.openinv.util.setting.PlayerToggles; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -34,11 +36,9 @@ public class ContainerSettingCommand implements TabExecutor { - private final @NotNull Toggles toggles; private final @NotNull LanguageManager lang; - public ContainerSettingCommand(@NotNull Toggles toggles, @NotNull LanguageManager lang) { - this.toggles = toggles; + public ContainerSettingCommand(@NotNull LanguageManager lang) { this.lang = lang; } @@ -49,24 +49,30 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command return true; } - boolean any = command.getName().startsWith("any"); - PlayerToggle toggle = any ? toggles.any() : toggles.silent(); + PlayerToggle toggle = PlayerToggles.get(command.getName()); + + // Shouldn't be possible. + if (toggle == null) { + JavaPlugin.getProvidingPlugin(getClass()).getLogger().warning("Command /" + command.getName() + " registered with no corresponding toggle!"); + return false; + } + UUID playerId = player.getUniqueId(); if (args.length > 0) { args[0] = args[0].toLowerCase(Locale.ENGLISH); if (args[0].equals("on")) { - toggle.set(playerId, true); + set(toggle, playerId, true); } else if (args[0].equals("off")) { - toggle.set(playerId, false); + set(toggle, playerId, false); } else if (!args[0].equals("check")) { // Invalid argument, show usage. return false; } } else { - toggle.set(playerId, !toggle.is(playerId)); + set(toggle, playerId, !toggle.is(playerId)); } String onOff = lang.getLocalizedMessage(player, toggle.is(playerId) ? "messages.info.on" : "messages.info.off"); @@ -77,12 +83,18 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command lang.sendMessage( sender, "messages.info.settingState", - new Replacement("%setting%", any ? "AnyContainer" : "SilentContainer"), + new Replacement("%setting%", toggle.getName()), new Replacement("%state%", onOff)); return true; } + private void set(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) { + if (toggle.set(uuid, state)) { + OpenEvents.notifyPlayerToggle(toggle, uuid, state); + } + } + @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!command.testPermissionSilent(sender) || args.length != 1) { diff --git a/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java b/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java index ecc708da..d93e7f54 100644 --- a/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java @@ -20,7 +20,7 @@ import com.lishid.openinv.internal.ViewOnly; import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.Permissions; -import com.lishid.openinv.util.setting.Toggles; +import com.lishid.openinv.util.setting.PlayerToggles; import com.lishid.openinv.util.lang.LanguageManager; import org.bukkit.GameMode; import org.bukkit.entity.HumanEntity; @@ -47,12 +47,10 @@ public class ContainerListener implements Listener { private final @NotNull InternalAccessor accessor; - private final @NotNull Toggles toggles; private final @NotNull LanguageManager lang; - public ContainerListener(@NotNull InternalAccessor accessor, @NotNull Toggles toggles, @NotNull LanguageManager lang) { + public ContainerListener(@NotNull InternalAccessor accessor, @NotNull LanguageManager lang) { this.accessor = accessor; - this.toggles = toggles; this.lang = lang; } @@ -74,14 +72,14 @@ private void onPlayerInteract(@NotNull PlayerInteractEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); - boolean any = Permissions.CONTAINER_ANY.hasPermission(player) && toggles.any().is(playerId); + boolean any = Permissions.CONTAINER_ANY.hasPermission(player) && PlayerToggles.any().is(playerId); boolean needsAny = accessor.getAnySilentContainer().isAnyContainerNeeded(event.getClickedBlock()); if (!any && needsAny) { return; } - boolean silent = Permissions.CONTAINER_SILENT.hasPermission(player) && toggles.silent().is(playerId); + boolean silent = Permissions.CONTAINER_SILENT.hasPermission(player) && PlayerToggles.silent().is(playerId); // If anycontainer or silentcontainer is active if (any || silent) { @@ -106,7 +104,7 @@ private void onInventoryClose(@NotNull final InventoryCloseEvent event) { } InventoryHolder holder = event.getInventory().getHolder(); - if (toggles.silent().is(player.getUniqueId()) + if (PlayerToggles.silent().is(player.getUniqueId()) && holder != null && this.accessor.getAnySilentContainer().isAnySilentContainer(holder)) { this.accessor.getAnySilentContainer().deactivateContainer(player); diff --git a/plugin/src/main/java/com/lishid/openinv/listener/ToggleListener.java b/plugin/src/main/java/com/lishid/openinv/listener/ToggleListener.java new file mode 100644 index 00000000..b39535ae --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/listener/ToggleListener.java @@ -0,0 +1,22 @@ +package com.lishid.openinv.listener; + +import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.util.setting.PlayerToggles; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class ToggleListener implements Listener { + + @Keep + @EventHandler + private void onPlayerQuit(@NotNull PlayerQuitEvent event) { + UUID playerId = event.getPlayer().getUniqueId(); + PlayerToggles.any().set(playerId, false); + PlayerToggles.silent().set(playerId, false); + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java index 6e86ee66..c5203d69 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java @@ -16,17 +16,15 @@ package com.lishid.openinv.util.config; -import com.lishid.openinv.OpenInv; -import org.bukkit.OfflinePlayer; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -public record ConfigUpdater(OpenInv plugin) { +public record ConfigUpdater(@NotNull Plugin plugin) { public void checkForUpdates() { final int version = plugin.getConfig().getInt("config-version", 1); @@ -45,119 +43,78 @@ public void checkForUpdates() { plugin.getLogger().warning("Could not back up config.yml before updating!"); } - plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { - if (version < 2) { - updateConfig1To2(); - } - if (version < 3) { - updateConfig2To3(); - } - if (version < 4) { - updateConfig3To4(); - } - if (version < 5) { - updateConfig4To5(); - } - if (version < 6) { - updateConfig5To6(); - } - - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.saveConfig(); - plugin.getLogger().info("Configuration update complete!"); - }); - }); + if (version < 2) { + updateConfig1To2(); + } + if (version < 3) { + updateConfig2To3(); + } + if (version < 4) { + updateConfig3To4(); + } + if (version < 5) { + updateConfig4To5(); + } + if (version < 6) { + updateConfig5To6(); + } + if (version < 7) { + updateConfig6To7(); + } + + plugin.saveConfig(); + plugin.getLogger().info("Configuration update complete!"); + } + + private void updateConfig6To7() { + FileConfiguration config = plugin.getConfig(); + config.set("toggles", null); + String consoleLocale = config.getString("settings.locale", "en"); + if (consoleLocale.isBlank() || consoleLocale.equalsIgnoreCase("en_us")) { + consoleLocale = "en"; + } + config.set("settings.console-locale", consoleLocale); + config.set("settings.locale", null); + config.set("config-version", 7); } private void updateConfig5To6() { - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getConfig().set("settings.command.open.no-args-opens-self", false); - plugin.getConfig().set("settings.command.searchcontainer.max-radius", 10); - plugin.getConfig().set("config-version", 6); - }); + FileConfiguration config = plugin.getConfig(); + config.set("settings.command.open.no-args-opens-self", false); + config.set("settings.command.searchcontainer.max-radius", 10); + config.set("config-version", 6); } private void updateConfig4To5() { - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getConfig().set("settings.disable-offline-access", false); - plugin.getConfig().set("config-version", 5); - }); + FileConfiguration config = plugin.getConfig(); + config.set("settings.disable-offline-access", false); + config.set("config-version", 5); } private void updateConfig3To4() { - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getConfig().set("notify", null); - plugin.getConfig().set("settings.locale", "en_US"); - plugin.getConfig().set("config-version", 4); - }); + FileConfiguration config = plugin.getConfig(); + config.set("notify", null); + config.set("config-version", 4); } private void updateConfig2To3() { - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getConfig().set("config-version", 3); - plugin.getConfig().set("items.open-inv", null); - plugin.getConfig().set("ItemOpenInv", null); - plugin.getConfig().set("toggles.items.open-inv", null); - plugin.getConfig().set("settings.disable-saving", - plugin.getConfig().getBoolean("DisableSaving", false)); - plugin.getConfig().set("DisableSaving", null); - }); + FileConfiguration config = plugin.getConfig(); + config.set("items", null); + config.set("ItemOpenInv", null); + config.set("toggles", null); + config.set("settings.disable-saving", config.getBoolean("DisableSaving", false)); + config.set("DisableSaving", null); + config.set("config-version", 3); } private void updateConfig1To2() { - plugin.getServer().getScheduler().runTask(plugin, () -> { - // Get the old config settings - boolean notifySilentChest = plugin.getConfig().getBoolean("NotifySilentChest", true); - boolean notifyAnyChest = plugin.getConfig().getBoolean("NotifyAnyChest", true); - plugin.getConfig().set("ItemOpenInvItemID", null); - plugin.getConfig().set("NotifySilentChest", null); - plugin.getConfig().set("NotifyAnyChest", null); - plugin.getConfig().set("config-version", 2); - plugin.getConfig().set("notify.any-chest", notifyAnyChest); - plugin.getConfig().set("notify.silent-chest", notifySilentChest); - }); - - updateToggles("AnyChest", "toggles.any-chest"); - updateToggles("SilentChest", "toggles.silent-chest"); - } - - private void updateToggles(final String sectionName, final String newSectionName) { - ConfigurationSection section = plugin.getConfig().getConfigurationSection(sectionName); - // Ensure section exists - if (section == null) { - return; - } - - Set keys = section.getKeys(false); - - // Ensure section has content - if (keys.isEmpty()) { - return; - } - - final Map toggles = new HashMap<>(); - - for (String playerName : keys) { - OfflinePlayer player = plugin.matchPlayer(playerName); - if (player != null) { - toggles.put(player.getUniqueId().toString(), section.getBoolean(playerName + ".toggle", false)); - } - } - - plugin.getServer().getScheduler().runTask(plugin, () -> { - // Wipe old ConfigurationSection - plugin.getConfig().set(sectionName, null); - - // Prepare new ConfigurationSection - ConfigurationSection newSection = plugin.getConfig().getConfigurationSection(newSectionName); - if (newSection == null) { - newSection = plugin.getConfig().createSection(newSectionName); - } - // Set new values - for (Map.Entry entry : toggles.entrySet()) { - newSection.set(entry.getKey(), entry.getValue()); - } - }); + FileConfiguration config = plugin.getConfig(); + config.set("ItemOpenInvItemID", null); + config.set("NotifySilentChest", null); + config.set("NotifyAnyChest", null); + config.set("AnyChest", null); + config.set("SilentChest", null); + config.set("config-version", 2); } } diff --git a/plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java b/plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java deleted file mode 100644 index 85c1eb3a..00000000 --- a/plugin/src/main/java/com/lishid/openinv/util/setting/PlayerToggle.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.lishid.openinv.util.setting; - -import java.util.UUID; - -public interface PlayerToggle { - - boolean is(UUID uuid); - - void set(UUID uuid, boolean value); - -} diff --git a/plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java b/plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java deleted file mode 100644 index a830ecdf..00000000 --- a/plugin/src/main/java/com/lishid/openinv/util/setting/Toggles.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.lishid.openinv.util.setting; - -import org.bukkit.configuration.MemoryConfiguration; -import org.jetbrains.annotations.NotNull; - -import java.util.UUID; - -public abstract class Toggles { - - private static final String TOGGLE_ANYCONTAINER_BASE = "toggles.any-chest."; - private static final String TOGGLE_SILENTCONTAINER_BASE = "toggles.silent-chest."; - - private final PlayerToggle any = new PlayerToggle() { - @Override - public boolean is(UUID uuid) { - return configuration.getBoolean(TOGGLE_ANYCONTAINER_BASE + uuid, false); - } - - @Override - public void set(UUID uuid, boolean value) { - configuration.set(TOGGLE_ANYCONTAINER_BASE + uuid, value); - save(); - } - }; - - private final PlayerToggle silent = new PlayerToggle() { - @Override - public boolean is(UUID uuid) { - return configuration.getBoolean(TOGGLE_SILENTCONTAINER_BASE + uuid, false); - } - - @Override - public void set(UUID uuid, boolean value) { - configuration.set(TOGGLE_SILENTCONTAINER_BASE + uuid, value); - save(); - } - }; - - private @NotNull MemoryConfiguration configuration; - - public Toggles() { - this.configuration = new MemoryConfiguration(); - } - - public PlayerToggle any() { - return any; - } - - public PlayerToggle silent() { - return silent; - } - - public void reload(@NotNull MemoryConfiguration configuration) { - this.configuration = configuration; - } - - public abstract void save(); - -} diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 6a68c9cb..a9133fe3 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,4 +1,4 @@ -config-version: 6 +config-version: 7 settings: command: open: @@ -7,4 +7,4 @@ settings: max-radius: 10 disable-offline-access: false disable-saving: false - locale: 'en_us' + console-locale: 'en' diff --git a/pom.xml b/pom.xml index 8c2a55a4..063ab17c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT pom @@ -41,6 +41,7 @@ all api + addon/togglepersist common internal/v1_21_R1 internal/v1_20_R4 From fe11217a2748e45395929b7e8d845fad72f7c0b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:36:34 +0000 Subject: [PATCH 201/340] Bump com.google.errorprone:error_prone_core from 2.28.0 to 2.29.2 (#230) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 063ab17c..dd536699 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ com.google.errorprone error_prone_core - 2.28.0 + 2.29.2 From 0fb9e5eed324e500943635ed72608022d5ad6da5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:36:47 +0000 Subject: [PATCH 202/340] Bump org.apache.maven.plugins:maven-jar-plugin from 3.4.1 to 3.4.2 (#229) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd536699..afed8d80 100644 --- a/pom.xml +++ b/pom.xml @@ -135,7 +135,7 @@ maven-jar-plugin org.apache.maven.plugins - 3.4.1 + 3.4.2 From 335d849525c151c10a3df5a3dbb51cb9765ecf2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:57:37 -0400 Subject: [PATCH 203/340] Bump softprops/action-gh-release from 2.0.6 to 2.0.8 (#231) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.6 to 2.0.8. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.6...v2.0.8) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index d62e39b8..3dca4b01 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -34,7 +34,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.0.6 + uses: softprops/action-gh-release@v2.0.8 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 8c15b3d07893c706fe6df005ad63797fcee51e21 Mon Sep 17 00:00:00 2001 From: Newwind Date: Mon, 5 Aug 2024 18:25:08 +0100 Subject: [PATCH 204/340] Try to get block state without taking snapshots (#234) --- .../openinv/internal/IAnySilentContainer.java | 8 ++++-- .../lishid/openinv/util/InventoryAccess.java | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index d4bccb91..81822da6 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -31,6 +31,10 @@ import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.Method; + +import static com.lishid.openinv.util.InventoryAccess.getBlockState; + public interface IAnySilentContainer { /** @@ -58,7 +62,7 @@ public interface IAnySilentContainer { * @return true if the container is blocked */ default boolean isAnyContainerNeeded(@NotNull Block block) { - BlockState blockState = block.getState(); + BlockState blockState = getBlockState(block); // Barrels do not require AnyContainer. if (blockState instanceof Barrel) { @@ -148,7 +152,7 @@ default boolean isChestBlocked(@NotNull Block chest) { * @return true if the type is a supported container */ default boolean isAnySilentContainer(@NotNull Block block) { - return isAnySilentContainer(block.getState()); + return isAnySilentContainer(getBlockState(block)); } /** diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index 466cae61..0ac69779 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -20,16 +20,42 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.function.BiFunction; public final class InventoryAccess { private static @Nullable BiFunction, ISpecialInventory> provider; + private static Method blockGetStateNoSnapshotMethod; + + static { + try { + blockGetStateNoSnapshotMethod = Block.class.getMethod("getState", boolean.class); + } catch (NoSuchMethodException e) { + blockGetStateNoSnapshotMethod = null; + } + } + + public static BlockState getBlockState(Block block) { + // Try to get block state without snapshot (only available in paper currently) + if (blockGetStateNoSnapshotMethod != null) { + try { + return (BlockState) blockGetStateNoSnapshotMethod.invoke(block, false); + } catch (InvocationTargetException | IllegalAccessException e) { + return block.getState(); + } + } else { + return block.getState(); + } + } public static boolean isUsable() { return provider != null; From 4198b7f22a046bd6853e9adced663e73521176db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E7=A0=9A=E7=82=BD?= Date: Tue, 6 Aug 2024 21:36:02 +0800 Subject: [PATCH 205/340] Fix white stained glass pane texture (#235) --- .../assets/minecraft/models/item/white_stained_glass_pane.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json b/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json index 66dbd7d2..e4edacdb 100644 --- a/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json +++ b/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json @@ -1,7 +1,7 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "minecraft:item/white_stained_glass_pane" + "layer0": "minecraft:block/white_stained_glass" }, "overrides": [ { From 0f0bde3e49b1be85c7cd1637563ca6adba4be73c Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 7 Aug 2024 10:49:37 -0400 Subject: [PATCH 206/340] Move no-snapshot method out of API (#236) --- .../openinv/internal/IAnySilentContainer.java | 73 +----------- .../lishid/openinv/util/InventoryAccess.java | 26 ----- .../internal/AnySilentContainerBase.java | 106 ++++++++++++++++++ .../internal/v1_20_R3/AnySilentContainer.java | 4 +- .../internal/v1_20_R4/AnySilentContainer.java | 4 +- .../container/AnySilentContainer.java | 4 +- 6 files changed, 118 insertions(+), 99 deletions(-) create mode 100644 common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 81822da6..fe470a70 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -16,25 +16,17 @@ package com.lishid.openinv.internal; -import org.bukkit.block.Barrel; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.EnderChest; -import org.bukkit.block.ShulkerBox; -import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; -import org.bukkit.block.data.type.Chest; import org.bukkit.entity.Cat; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Method; - -import static com.lishid.openinv.util.InventoryAccess.getBlockState; - public interface IAnySilentContainer { /** @@ -61,62 +53,7 @@ public interface IAnySilentContainer { * @param block the {@link Block} of the container * @return true if the container is blocked */ - default boolean isAnyContainerNeeded(@NotNull Block block) { - BlockState blockState = getBlockState(block); - - // Barrels do not require AnyContainer. - if (blockState instanceof Barrel) { - return false; - } - - // Enderchests require a non-occluding block on top to open. - if (blockState instanceof EnderChest) { - return block.getRelative(0, 1, 0).getType().isOccluding(); - } - - // Shulker boxes require half a block clear in the direction they open. - if (blockState instanceof ShulkerBox) { - return isShulkerBlocked(block); - } - - if (!(blockState instanceof org.bukkit.block.Chest)) { - return false; - } - - if (isChestBlocked(block)) { - return true; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) { - return false; - } - - BlockFace relativeFace = switch (chest.getFacing()) { - case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST; - case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH; - case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST; - case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH; - default -> BlockFace.SELF; - }; - Block relative = block.getRelative(relativeFace); - - if (relative.getType() != block.getType()) { - return false; - } - - BlockData relativeData = relative.getBlockData(); - if (!(relativeData instanceof Chest relativeChest)) { - return false; - } - - if (relativeChest.getFacing() != chest.getFacing() - || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { - return false; - } - - return isChestBlocked(relative); - } + boolean isAnyContainerNeeded(@NotNull Block block); /** * Check if a shulker box block cannot be opened under ordinary circumstances. @@ -127,9 +64,13 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { default boolean isShulkerBlocked(@NotNull Block shulkerBox) { Directional directional = (Directional) shulkerBox.getBlockData(); BlockFace facing = directional.getFacing(); + // Construct a new 1-block bounding box at the origin. BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1); + // Expand the box in the direction the shulker will open. box.expand(facing, 0.5); + // Move the box away from the origin by a block so only the expansion intersects with a box around the origin. box.shift(facing.getOppositeFace().getDirection()); + // Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box. return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box); } @@ -151,9 +92,7 @@ default boolean isChestBlocked(@NotNull Block chest) { * @param block the potential container * @return true if the type is a supported container */ - default boolean isAnySilentContainer(@NotNull Block block) { - return isAnySilentContainer(getBlockState(block)); - } + boolean isAnySilentContainer(@NotNull Block block); /** * Check if the given {@link BlockState} is a container which can be unblocked or silenced. diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index 0ac69779..466cae61 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -20,42 +20,16 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.function.BiFunction; public final class InventoryAccess { private static @Nullable BiFunction, ISpecialInventory> provider; - private static Method blockGetStateNoSnapshotMethod; - - static { - try { - blockGetStateNoSnapshotMethod = Block.class.getMethod("getState", boolean.class); - } catch (NoSuchMethodException e) { - blockGetStateNoSnapshotMethod = null; - } - } - - public static BlockState getBlockState(Block block) { - // Try to get block state without snapshot (only available in paper currently) - if (blockGetStateNoSnapshotMethod != null) { - try { - return (BlockState) blockGetStateNoSnapshotMethod.invoke(block, false); - } catch (InvocationTargetException | IllegalAccessException e) { - return block.getState(); - } - } else { - return block.getState(); - } - } public static boolean isUsable() { return provider != null; diff --git a/common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java b/common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java new file mode 100644 index 00000000..df477629 --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java @@ -0,0 +1,106 @@ +package com.lishid.openinv.internal; + +import org.bukkit.block.Barrel; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.EnderChest; +import org.bukkit.block.ShulkerBox; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Chest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; + +public abstract class AnySilentContainerBase implements IAnySilentContainer { + + private static final @Nullable Method BLOCK_GET_STATE_BOOLEAN; + + static { + @Nullable Method getState; + try { + //noinspection JavaReflectionMemberAccess + getState = Block.class.getMethod("getState", boolean.class); + } catch (NoSuchMethodException e) { + getState = null; + } + BLOCK_GET_STATE_BOOLEAN = getState; + } + + private static BlockState getBlockState(Block block) { + // Paper: Get state without snapshotting. + if (BLOCK_GET_STATE_BOOLEAN != null) { + try { + return (BlockState) BLOCK_GET_STATE_BOOLEAN.invoke(block, false); + } catch (ReflectiveOperationException ignored) { + // If we encounter an issue, fall through to regular snapshotting method. + } + } + return block.getState(); + } + + @Override + public boolean isAnyContainerNeeded(@NotNull Block block) { + BlockState blockState = getBlockState(block); + + // Barrels do not require AnyContainer. + if (blockState instanceof Barrel) { + return false; + } + + // Enderchests require a non-occluding block on top to open. + if (blockState instanceof EnderChest) { + return block.getRelative(0, 1, 0).getType().isOccluding(); + } + + // Shulker boxes require half a block clear in the direction they open. + if (blockState instanceof ShulkerBox) { + return isShulkerBlocked(block); + } + + if (!(blockState instanceof org.bukkit.block.Chest)) { + return false; + } + + if (isChestBlocked(block)) { + return true; + } + + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) { + return false; + } + + BlockFace relativeFace = switch (chest.getFacing()) { + case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST; + case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH; + case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST; + case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH; + default -> BlockFace.SELF; + }; + Block relative = block.getRelative(relativeFace); + + if (relative.getType() != block.getType()) { + return false; + } + + BlockData relativeData = relative.getBlockData(); + if (!(relativeData instanceof Chest relativeChest)) { + return false; + } + + if (relativeChest.getFacing() != chest.getFacing() + || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { + return false; + } + + return isChestBlocked(relative); + } + + @Override + public boolean isAnySilentContainer(@NotNull Block block) { + return isAnySilentContainer(getBlockState(block)); + } + +} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java index 403b3ed8..aaddac01 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java @@ -16,7 +16,7 @@ package com.lishid.openinv.internal.v1_20_R3; -import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.AnySilentContainerBase; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -48,7 +48,7 @@ import java.lang.reflect.Field; import java.util.logging.Logger; -public class AnySilentContainer implements IAnySilentContainer { +public class AnySilentContainer extends AnySilentContainerBase { private final @NotNull Logger logger; private final @NotNull LanguageManager lang; diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java index f04de59b..9ea9065f 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java @@ -16,7 +16,7 @@ package com.lishid.openinv.internal.v1_20_R4; -import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.AnySilentContainerBase; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -48,7 +48,7 @@ import java.lang.reflect.Field; import java.util.logging.Logger; -public class AnySilentContainer implements IAnySilentContainer { +public class AnySilentContainer extends AnySilentContainerBase { private final @NotNull Logger logger; private final @NotNull LanguageManager lang; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java index d5b91cf2..f23f1edc 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java @@ -16,7 +16,7 @@ package com.lishid.openinv.internal.v1_21_R1.container; -import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.AnySilentContainerBase; import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenChestMenu; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; @@ -50,7 +50,7 @@ import java.lang.reflect.Field; import java.util.logging.Logger; -public class AnySilentContainer implements IAnySilentContainer { +public class AnySilentContainer extends AnySilentContainerBase { private final @NotNull Logger logger; private final @NotNull LanguageManager lang; From 5e2cf56d6f4a3a1565e56c812dfe1118924da7a0 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 7 Aug 2024 12:43:06 -0400 Subject: [PATCH 207/340] Fix "adding" 0 items to last slot Closes #232 --- .../v1_21_R1/container/menu/OpenChestMenu.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java index a3b331ed..f19b2aec 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java @@ -255,8 +255,15 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { return false; } - int total = existing.getCount() + itemStack.getCount(); int max = slot.getMaxStackSize(existing); + int existingCount = existing.getCount(); + + // If the stack is already full, we can't add more. + if (existingCount >= max) { + return false; + } + + int total = existingCount + itemStack.getCount(); // If the existing item can accept the entirety of our item, we're done! if (total <= max) { @@ -267,7 +274,7 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { } // Otherwise, add as many as we can. - itemStack.shrink(max - existing.getCount()); + itemStack.shrink(max - existingCount); existing.setCount(max); slot.setChanged(); return true; From 5eaf6243fba13233401130ba28b1e87606b4bbee Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 7 Aug 2024 12:54:08 -0400 Subject: [PATCH 208/340] Fix modification state being clobbered --- .../openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java index f19b2aec..9f42d07f 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java @@ -224,7 +224,7 @@ protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHi continue; } // Otherwise, add as many as we can from our stack to the slot. - modified = addToExistingStack(itemStack, slot); + modified |= addToExistingStack(itemStack, slot); } else { // If this is the first empty slot, keep track of it for later use. if (firstEmpty == null) { From e8ab490d95e5e8cde11584240c2b285a162f128e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 7 Aug 2024 12:54:16 -0400 Subject: [PATCH 209/340] Declare API version for persistence addon --- addon/togglepersist/src/main/resources/plugin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/togglepersist/src/main/resources/plugin.yml b/addon/togglepersist/src/main/resources/plugin.yml index cf84916e..50a77051 100644 --- a/addon/togglepersist/src/main/resources/plugin.yml +++ b/addon/togglepersist/src/main/resources/plugin.yml @@ -3,4 +3,5 @@ main: com.github.jikoo.openinv.togglepersist.TogglePersist version: ${project.version} author: Jikoo description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions. +api-version: "1.20" depend: [ OpenInv ] From e64261669b3a1fc79063987a948fbc4ba5432e28 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 7 Aug 2024 12:54:50 -0400 Subject: [PATCH 210/340] More responsibility reduction --- .../src/main/java/com/lishid/openinv/OpenInv.java | 2 +- .../com/lishid/openinv/util/InventoryManager.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 3f74b3ec..e8a0bee5 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -110,7 +110,7 @@ public void onEnable() { // Perform initial config load. reloadConfig(); - inventoryManager = new InventoryManager(this, accessor); + inventoryManager = new InventoryManager(this, config, accessor); playerLoader = new PlayerLoader(this, config, inventoryManager, accessor, getLogger()); try { diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index 254a53e0..4210777a 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -3,11 +3,11 @@ import com.github.jikoo.planarwrappers.util.version.BukkitVersions; import com.github.jikoo.planarwrappers.util.version.Version; import com.google.errorprone.annotations.Keep; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.event.OpenEvents; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.util.config.Config; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -19,6 +19,7 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,11 +44,13 @@ public class InventoryManager implements Listener { private final Map inventories = new ConcurrentHashMap<>(); private final Map enderChests = new ConcurrentHashMap<>(); private final Set expectedCloses = new HashSet<>(); - private final @NotNull OpenInv plugin; + private final @NotNull Plugin plugin; + private final @NotNull Config config; private final @NotNull InternalAccessor accessor; - public InventoryManager(@NotNull OpenInv plugin, @NotNull InternalAccessor accessor) { + public InventoryManager(@NotNull Plugin plugin, @NotNull Config config, @NotNull InternalAccessor accessor) { this.plugin = plugin; + this.config = config; this.accessor = accessor; } @@ -60,7 +63,7 @@ public void evictAll() { viewer.closeInventory(); } // If saving is prevented, return a null value for the player to save. - if (plugin.disableSaving() || OpenEvents.saveCancelled(inventory)) { + if (config.isSaveDisabled() || OpenEvents.saveCancelled(inventory)) { return null; } if (inventory.getPlayer() instanceof Player player) { @@ -194,7 +197,7 @@ private void checkViewerAccess(@NotNull T inventor Player owner = (Player) inventory.getPlayer(); Permissions connectedState = online ? Permissions.ACCESS_ONLINE : Permissions.ACCESS_OFFLINE; - boolean alwaysDenied = !online && plugin.disableOfflineAccess(); + boolean alwaysDenied = !online && config.isOfflineDisabled(); // Copy viewers so we don't modify the list we're iterating over when closing inventories. List viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers()); @@ -248,7 +251,7 @@ private boolean consumeLoaded( } private void save(@NotNull ISpecialInventory inventory) { - if (plugin.disableSaving()) { + if (config.isSaveDisabled()) { return; } From d50b7433c7b5603945fb13d1f2f2d67f569fc7b4 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 10 Aug 2024 11:41:39 -0400 Subject: [PATCH 211/340] Add equal access behavior config (#237) --- .../openinv/internal/PlayerManager.java | 2 +- .../internal/v1_20_R3/PlayerManager.java | 2 +- .../internal/v1_20_R4/PlayerManager.java | 2 +- .../v1_21_R1/container/OpenEnderChest.java | 14 ++-- .../v1_21_R1/container/OpenInventory.java | 15 ++--- .../container/menu/OpenChestMenu.java | 11 +-- .../container/menu/OpenEnderChestMenu.java | 16 ++--- .../container/menu/OpenInventoryMenu.java | 10 +-- .../v1_21_R1/player/PlayerManager.java | 20 +++--- .../main/java/com/lishid/openinv/OpenInv.java | 52 ++++++++++++-- .../openinv/command/OpenInvCommand.java | 3 +- .../listener/LegacyInventoryListener.java | 67 +++++++++++++------ .../lishid/openinv/util/AccessEqualMode.java | 23 +++++++ .../lishid/openinv/util/InternalAccessor.java | 4 +- .../lishid/openinv/util/config/Config.java | 12 ++++ .../openinv/util/config/ConfigUpdater.java | 9 +++ plugin/src/main/resources/config.yml | 3 +- 17 files changed, 178 insertions(+), 87 deletions(-) create mode 100644 plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java diff --git a/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java b/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java index d1d3c6c0..e999eda5 100644 --- a/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java +++ b/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java @@ -49,6 +49,6 @@ public interface PlayerManager { *` * @return the InventoryView opened */ - @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); + @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly); } diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java index db93c97f..0df4c729 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java @@ -239,7 +239,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { } @Override - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { ServerPlayer nmsPlayer = getHandle(player); diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java index 1ff8b4e9..755500db 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java @@ -239,7 +239,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { } @Override - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { ServerPlayer nmsPlayer = getHandle(player); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java index eefaf3ed..3d495735 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java @@ -9,8 +9,6 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -27,8 +25,8 @@ import java.util.ArrayList; import java.util.List; -public class OpenEnderChest implements Container, StackedContentsCompatible, MenuProvider, - InternalOwned, ISpecialEnderChest { +public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned, + ISpecialEnderChest { private CraftInventory inventory; private @NotNull ServerPlayer owner; @@ -182,17 +180,15 @@ public void fillStackedContents(StackedContents stackedContents) { } } - @Override - public Component getDisplayName() { + public Component getTitle() { return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName()) .append(Component.translatable("container.enderchest")) .append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName())); } - @Override - public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { - return new OpenEnderChestMenu(this, serverPlayer, i); + return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly); } return null; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java index 8461901c..748038a6 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java @@ -4,7 +4,6 @@ import com.lishid.openinv.internal.InternalOwned; import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventory; import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenInventoryMenu; -import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.internal.v1_21_R1.container.slot.Content; import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCrafting; import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCraftingResult; @@ -15,6 +14,7 @@ import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentOffHand; import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentViewOnly; import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -22,7 +22,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -40,7 +39,7 @@ import java.util.ArrayList; import java.util.List; -public class OpenInventory implements Container, MenuProvider, InternalOwned, ISpecialPlayerInventory { +public class OpenInventory implements Container, InternalOwned, ISpecialPlayerInventory { private final List slots; private final int size; @@ -355,15 +354,9 @@ public void clearContent() { owner.containerMenu.setCarried(ItemStack.EMPTY); } - @Override - public Component getDisplayName() { - return getTitle(null); - } - - @Override - public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { - return new OpenInventoryMenu(this, serverPlayer, i); + return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); } return null; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java index 9f42d07f..cf95822c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java @@ -54,13 +54,18 @@ public abstract class OpenChestMenu type, int containerCounter, T container, ServerPlayer viewer) { + protected OpenChestMenu( + @NotNull MenuType type, + int containerCounter, + @NotNull T container, + @NotNull ServerPlayer viewer, + boolean viewOnly) { super(type, containerCounter); this.container = container; this.viewer = viewer; + this.viewOnly = viewOnly; ownContainer = container.getOwnerHandle().equals(viewer); topSize = getTopSize(viewer); - viewOnly = checkViewOnly(); preSlotSetup(); @@ -116,8 +121,6 @@ protected OpenChestMenu(MenuType type, int containerCounter, T contai }; } - protected abstract boolean checkViewOnly(); - protected void preSlotSetup() {} protected @NotNull Slot getUpperSlot(int index, int x, int y) { diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java index 2f65b8af..172f42ea 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java @@ -1,22 +1,20 @@ package com.lishid.openinv.internal.v1_21_R1.container.menu; import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; -import com.lishid.openinv.util.Permissions; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; public class OpenEnderChestMenu extends OpenChestMenu { - public OpenEnderChestMenu(OpenEnderChest enderChest, ServerPlayer viewer, int containerId) { - super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer); - } - - @Override - protected boolean checkViewOnly() { - return !(ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER) - .hasPermission(viewer.getBukkitEntity()); + public OpenEnderChestMenu( + @NotNull OpenEnderChest enderChest, + @NotNull ServerPlayer viewer, + int containerId, + boolean viewOnly) { + super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly); } @Override diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java index 3c11fe91..ffbea9d0 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java @@ -26,8 +26,8 @@ public class OpenInventoryMenu extends OpenChestMenu { private int offset; - public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i) { - super(getMenuType(inventory, viewer), i, inventory, viewer); + public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) { + super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly); } private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { @@ -97,12 +97,6 @@ protected void preSlotSetup() { return slot; } - @Override - protected boolean checkViewOnly() { - return !(ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER) - .hasPermission(viewer.getBukkitEntity()); - } - @Override protected @NotNull CraftInventoryView> createBukkitEntity() { org.bukkit.inventory.Inventory bukkitInventory; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java index 64fe5bb6..1eae5f04 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java @@ -13,7 +13,6 @@ import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -219,35 +218,34 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { } @Override - public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory) { + public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) { ServerPlayer player = getHandle(bukkitPlayer); if (player.connection == null) { return null; } - MenuProvider provider; + // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) + AbstractContainerMenu menu; Component title; if (inventory instanceof OpenInventory playerInv) { - provider = playerInv; + menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly); title = playerInv.getTitle(player); } else if (inventory instanceof OpenEnderChest enderChest) { - provider = enderChest; - title = enderChest.getDisplayName(); + menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly); + title = enderChest.getTitle(); } else { return null; } - // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) - AbstractContainerMenu menu = provider.createMenu(player.nextContainerCounter(), player.getInventory(), player); - // Should never happen, player is a ServerPlayer with an active connection. if (menu == null) { return null; } - // Set up title (the whole reason we're not just using Player#openInventory(Inventory)). - // Title can only be set once for a menu, and is set during the open process. + // Set up title. Title can only be set once for a menu, and is set during the open process. + // Further title changes are a hack where the client is sent a "new" inventory with the same ID, + // resulting in a title change but no other state modifications (like cursor position). menu.setTitle(title); menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index e8a0bee5..45a2129f 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -30,20 +30,23 @@ import com.lishid.openinv.listener.ContainerListener; import com.lishid.openinv.listener.LegacyInventoryListener; import com.lishid.openinv.listener.ToggleListener; -import com.lishid.openinv.util.config.Config; -import com.lishid.openinv.util.config.ConfigUpdater; +import com.lishid.openinv.util.AccessEqualMode; import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.InventoryManager; -import com.lishid.openinv.util.lang.LangMigrator; +import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.PlayerLoader; +import com.lishid.openinv.util.config.Config; +import com.lishid.openinv.util.config.ConfigUpdater; +import com.lishid.openinv.util.lang.LangMigrator; +import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.setting.PlayerToggle; import com.lishid.openinv.util.setting.PlayerToggles; -import com.lishid.openinv.util.lang.LanguageManager; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.bukkit.plugin.PluginManager; @@ -143,7 +146,7 @@ private void registerEvents() { PluginManager pluginManager = this.getServer().getPluginManager(); // Legacy: extra listener for permission handling and self-view issue prevention. if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { - pluginManager.registerEvents(new LegacyInventoryListener(this), this); + pluginManager.registerEvents(new LegacyInventoryListener(this, config), this); } pluginManager.registerEvents(playerLoader, this); pluginManager.registerEvents(inventoryManager, this); @@ -248,7 +251,44 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final @Override public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - return this.accessor.openInventory(player, inventory); + Permissions edit = null; + HumanEntity target = inventory.getPlayer(); + boolean ownContainer = player.equals(target); + if (inventory instanceof ISpecialPlayerInventory) { + edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; + } else if (inventory instanceof ISpecialEnderChest) { + edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER; + } + + boolean viewOnly = edit != null && !edit.hasPermission(player); + + if (!ownContainer && !viewOnly) { + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + // If the target doesn't have this access level... + if (!target.hasPermission(permission)) { + // If the viewer does have the access level, all good. + if (player.hasPermission(permission)) { + break; + } + // Otherwise check next access level. + continue; + } + + // If the player doesn't have an equal access level or equal access is a denial, deny. + if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) { + return null; + } + + // Since this is a tie, setting decides view state. + if (config.getAccessEqualMode() == AccessEqualMode.VIEW) { + viewOnly = true; + } + break; + } + } + + return this.accessor.openInventory(player, inventory, viewOnly); } @Override diff --git a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index 2050b384..b45b9717 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -18,6 +18,7 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.util.AccessEqualMode; import com.lishid.openinv.util.InventoryManager; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.PlayerLoader; @@ -197,7 +198,7 @@ private void openInventory(final Player player, final OfflinePlayer target, bool for (int level = 4; level > 0; --level) { String permission = "openinv.access.level." + level; if (onlineTarget.hasPermission(permission) - && !player.hasPermission(permission)) { + && (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY)) { lang.sendMessage( player, "messages.error.permissionExempt", diff --git a/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java b/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java index b503788a..38728dad 100644 --- a/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java @@ -1,12 +1,13 @@ package com.lishid.openinv.listener; import com.google.errorprone.annotations.Keep; -import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.util.AccessEqualMode; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.config.Config; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -19,11 +20,11 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -32,10 +33,12 @@ */ public class LegacyInventoryListener implements Listener { - private final @NotNull OpenInv plugin; + private final @NotNull Plugin plugin; + private final @NotNull Config config; - public LegacyInventoryListener(@NotNull OpenInv plugin) { + public LegacyInventoryListener(@NotNull Plugin plugin, @NotNull Config config) { this.plugin = plugin; + this.config = config; } @Keep @@ -150,35 +153,55 @@ private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result * @return true unless the top inventory is the holder's own inventory */ private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) { - HumanEntity entity = event.getWhoClicked(); + HumanEntity viewer = event.getWhoClicked(); Inventory inventory = event.getView().getTopInventory(); ISpecialInventory backing = InventoryAccess.getInventory(inventory); - Permissions editSelf; - Permissions editOther; - if (backing instanceof ISpecialEnderChest) { - editSelf = Permissions.ENDERCHEST_EDIT_SELF; - editOther = Permissions.ENDERCHEST_EDIT_OTHER; - } else if (backing instanceof ISpecialPlayerInventory) { - editSelf = Permissions.INVENTORY_EDIT_SELF; - editOther = Permissions.INVENTORY_EDIT_OTHER; + + if (backing == null) { + return true; + } + + Permissions edit; + HumanEntity target = backing.getPlayer(); + boolean ownContainer = viewer.equals(target); + if (backing instanceof ISpecialPlayerInventory) { + edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; + } else if (backing instanceof ISpecialEnderChest) { + edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER; } else { // Unknown implementation. return true; } - if (Objects.equals(entity, backing.getPlayer())) { - if (!editSelf.hasPermission(entity)) { - event.setCancelled(true); - return true; - } - return !(backing instanceof ISpecialPlayerInventory); - } else { - if (!editOther.hasPermission(entity)) { - event.setCancelled(true); + if (!edit.hasPermission(viewer)) { + event.setCancelled(true); + return true; + } + + // If access ties aren't view-only mode, don't bother with permission checks. + if (config.getAccessEqualMode() != AccessEqualMode.VIEW) { + return !ownContainer || !(backing instanceof ISpecialPlayerInventory); + } + + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + // If the target doesn't have this access level... + if (!target.hasPermission(permission)) { + // If the viewer does have the access level, all good. + if (viewer.hasPermission(permission)) { + break; + } + // Otherwise check next access level. + continue; } + + // Either the viewer lacks access (which shouldn't be possible) or this is a tie. View-only. + event.setCancelled(true); return true; } + + return !ownContainer || !(backing instanceof ISpecialPlayerInventory); } private static int convertToPlayerSlot(InventoryView view, int rawSlot) { diff --git a/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java b/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java new file mode 100644 index 00000000..8d36652a --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java @@ -0,0 +1,23 @@ +package com.lishid.openinv.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Locale; + +public enum AccessEqualMode { + + DENY, ALLOW, VIEW; + + public static @NotNull AccessEqualMode of(@Nullable String value) { + if (value == null) { + return VIEW; + } + return switch (value.toLowerCase(Locale.ENGLISH)) { + case "deny", "false" -> DENY; + case "allow", "true" -> ALLOW; + default -> VIEW; + }; + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 1a207585..1e177d9b 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -163,11 +163,11 @@ public boolean isSupported() { return internal.getAnySilentContainer(); } - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { if (internal == null) { throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } - return internal.getPlayerManager().openInventory(player, inventory); + return internal.getPlayerManager().openInventory(player, inventory, viewOnly); } /** diff --git a/plugin/src/main/java/com/lishid/openinv/util/config/Config.java b/plugin/src/main/java/com/lishid/openinv/util/config/Config.java index 159ca8b3..ad5f8f92 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/config/Config.java +++ b/plugin/src/main/java/com/lishid/openinv/util/config/Config.java @@ -1,12 +1,15 @@ package com.lishid.openinv.util.config; +import com.lishid.openinv.util.AccessEqualMode; import org.bukkit.configuration.Configuration; import org.bukkit.configuration.MemoryConfiguration; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class Config { private @NotNull Configuration root; + private @Nullable AccessEqualMode accessEqualMode; public Config() { root = new MemoryConfiguration(); @@ -14,6 +17,7 @@ public Config() { public void reload(@NotNull Configuration configuration) { root = configuration; + accessEqualMode = null; } public boolean isSaveDisabled() { @@ -28,4 +32,12 @@ public boolean doesNoArgsOpenSelf() { return root.getBoolean("settings.command.open.no-args-opens-self", false); } + public @NotNull AccessEqualMode getAccessEqualMode() { + if (accessEqualMode == null) { + accessEqualMode = AccessEqualMode.of(root.getString("settings.equal-access")); + } + + return accessEqualMode; + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java index c5203d69..a4b15147 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java @@ -61,11 +61,20 @@ public void checkForUpdates() { if (version < 7) { updateConfig6To7(); } + if (version < 8) { + updateConfig7To8(); + } plugin.saveConfig(); plugin.getLogger().info("Configuration update complete!"); } + private void updateConfig7To8() { + FileConfiguration config = plugin.getConfig(); + config.set("settings.equal-access", "view"); + config.set("config-version", 8); + } + private void updateConfig6To7() { FileConfiguration config = plugin.getConfig(); config.set("toggles", null); diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index a9133fe3..fb8b4b08 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,5 +1,6 @@ -config-version: 7 +config-version: 8 settings: + equal-access: view command: open: no-args-opens-self: false From 336aee88e266f3e0c306701695e757f8ae4da9da Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Aug 2024 12:50:46 -0400 Subject: [PATCH 212/340] Remove reliance on CB conversions Closes #238 --- .../container/menu/OpenInventoryMenu.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java index ffbea9d0..d5567f62 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java @@ -1,5 +1,6 @@ package com.lishid.openinv.internal.v1_21_R1.container.menu; +import com.google.common.base.Preconditions; import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenDummyInventory; import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventorySelf; @@ -129,11 +130,13 @@ public boolean isInTop(int rawSlot) { if (viewOnly) { return null; } - if (rawSlot < 0) { - return super.getInventory(rawSlot); + if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) { + return null; } + Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, + "Slot %s outside of inventory", rawSlot); if (rawSlot > topSize) { - return super.getInventory(offset + rawSlot); + return getBottomInventory(); } Slot slot = slots.get(rawSlot); if (slot.isFake()) { @@ -157,7 +160,16 @@ public int convertSlot(int rawSlot) { } return rawSlot; } - return super.convertSlot(offset + rawSlot); + + int slot = rawSlot - topSize; + + if (slot >= 27) { + slot -= 27; + } else { + slot += 9; + } + + return slot; } @Override @@ -166,14 +178,18 @@ public int convertSlot(int rawSlot) { return InventoryType.SlotType.OUTSIDE; } if (slot >= topSize) { - return super.getSlotType(offset + slot); + slot -= topSize; + if (slot >= 27) { + return InventoryType.SlotType.QUICKBAR; + } + return InventoryType.SlotType.CONTAINER; } return OpenInventoryMenu.this.container.getSlotType(offset + slot); } @Override public int countSlots() { - return topSize + getBottomInventory().getSize(); + return topSize + BOTTOM_INVENTORY_SIZE; } }; } From fbdda9637e195b4cb4c264c6955ab5bd3a6e5f40 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Aug 2024 12:53:50 -0400 Subject: [PATCH 213/340] Bump version to 5.1.0 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index 8d59bea6..cb2aa44a 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0-SNAPSHOT + 5.1.0 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 6469a94b..8880f2c1 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.0-SNAPSHOT + 5.1.0 openinvapi diff --git a/common/pom.xml b/common/pom.xml index caaaa9a0..9db78dc7 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.0-SNAPSHOT + 5.1.0 openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 7b4819bd..be64e302 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0-SNAPSHOT + 5.1.0 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index db450049..6b38913d 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0-SNAPSHOT + 5.1.0 openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 3b8cff46..9a999ce5 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0-SNAPSHOT + 5.1.0 openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 9a4b4a36..2b01bf3b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.0-SNAPSHOT + 5.1.0 openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.1.0-SNAPSHOT + 5.1.0 compile com.lishid openinvadapter1_20_R4 - 5.1.0-SNAPSHOT + 5.1.0 compile com.lishid openinvadapter1_20_R3 - 5.1.0-SNAPSHOT + 5.1.0 compile diff --git a/pom.xml b/pom.xml index afed8d80..f129cc1e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.0-SNAPSHOT + 5.1.0 pom From 1967f4ebb117043049b9fe0772d2dfde2cfce544 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Aug 2024 12:54:27 -0400 Subject: [PATCH 214/340] Bump version to 5.1.1-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index cb2aa44a..46c7a13c 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0 + 5.1.1-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 8880f2c1..5b5c80e8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.0 + 5.1.1-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index 9db78dc7..af606d75 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.0 + 5.1.1-SNAPSHOT openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index be64e302..2a0d54f6 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0 + 5.1.1-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 6b38913d..4301e40d 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0 + 5.1.1-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 9a999ce5..724018b5 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.0 + 5.1.1-SNAPSHOT openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 2b01bf3b..979c782f 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.0 + 5.1.1-SNAPSHOT openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.1.0 + 5.1.1-SNAPSHOT compile com.lishid openinvadapter1_20_R4 - 5.1.0 + 5.1.1-SNAPSHOT compile com.lishid openinvadapter1_20_R3 - 5.1.0 + 5.1.1-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index f129cc1e..e12199c3 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.0 + 5.1.1-SNAPSHOT pom From cace6f6726fecf00315a372dbcfc68e2b02d665e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Aug 2024 13:32:42 -0400 Subject: [PATCH 215/340] Update to 1.21.1 Looks like Spigot builds 1.21.1 for 1.20. Mildly annoying. --- internal/v1_21_R1/pom.xml | 2 +- .../main/java/com/lishid/openinv/util/InternalAccessor.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 724018b5..2eff1898 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -32,7 +32,7 @@ 21 21 - 1.21-R0.1-SNAPSHOT + 1.21.1-R0.1-SNAPSHOT diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 1e177d9b..114399ab 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -40,7 +40,8 @@ public class InternalAccessor { public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { try { - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21))) { + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 1)) + || BukkitVersions.MINECRAFT.equals(Version.of(1, 21))) { internal = new com.lishid.openinv.internal.v1_21_R1.InternalAccessor(logger, lang); } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) { internal = new com.lishid.openinv.internal.v1_20_R3.InternalAccessor(logger, lang); From 5d75640cee7823ee2d1054e4866b80314be2a2f7 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Aug 2024 13:33:00 -0400 Subject: [PATCH 216/340] Bump version to 5.1.1 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index 46c7a13c..f93cf529 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1-SNAPSHOT + 5.1.1 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 5b5c80e8..080be7fa 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.1-SNAPSHOT + 5.1.1 openinvapi diff --git a/common/pom.xml b/common/pom.xml index af606d75..fd65dbb3 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.1-SNAPSHOT + 5.1.1 openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 2a0d54f6..18a07a6d 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1-SNAPSHOT + 5.1.1 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index 4301e40d..b5407ce4 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1-SNAPSHOT + 5.1.1 openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 2eff1898..6b04a471 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1-SNAPSHOT + 5.1.1 openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 979c782f..d2195b23 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.1-SNAPSHOT + 5.1.1 openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.1.1-SNAPSHOT + 5.1.1 compile com.lishid openinvadapter1_20_R4 - 5.1.1-SNAPSHOT + 5.1.1 compile com.lishid openinvadapter1_20_R3 - 5.1.1-SNAPSHOT + 5.1.1 compile diff --git a/pom.xml b/pom.xml index e12199c3..357d3943 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.1-SNAPSHOT + 5.1.1 pom From c1a20a4e243defcdaf07e7f86f72967009923456 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 10 Aug 2024 13:33:43 -0400 Subject: [PATCH 217/340] Bump version to 5.1.2-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index f93cf529..ad3c3d15 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1 + 5.1.2-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 080be7fa..a964d079 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.1 + 5.1.2-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index fd65dbb3..7f881012 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.1 + 5.1.2-SNAPSHOT openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index 18a07a6d..d3405710 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1 + 5.1.2-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index b5407ce4..c9a793d0 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1 + 5.1.2-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 6b04a471..e65f6518 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.1 + 5.1.2-SNAPSHOT openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index d2195b23..b1b2b800 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.1 + 5.1.2-SNAPSHOT openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.1.1 + 5.1.2-SNAPSHOT compile com.lishid openinvadapter1_20_R4 - 5.1.1 + 5.1.2-SNAPSHOT compile com.lishid openinvadapter1_20_R3 - 5.1.1 + 5.1.2-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index 357d3943..b3a2aa2f 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.1 + 5.1.2-SNAPSHOT pom From 7b504971b110a59ffa2c6c6dcccb2e21b26c3d24 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 12 Aug 2024 00:22:53 -0400 Subject: [PATCH 218/340] Use Java 21 and Maven 3.9.8 for JitPack Closes #242 --- jitpack.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 jitpack.yml diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000..6fd76428 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,4 @@ +before_install: + - sdk install java 21.0.4-tem + - sdk use java 21.0.4-tem + - mvn wrapper:wrapper -Dmaven=3.9.8 From 8d60cd0726de69fa041200280f9bf05cc5ffc839 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:31:34 +0000 Subject: [PATCH 219/340] Bump com.google.errorprone:error_prone_core from 2.29.2 to 2.31.0 (#244) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b3a2aa2f..3a055db6 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ com.google.errorprone error_prone_core - 2.29.2 + 2.31.0 From f121c113f02e641559abc2d37cf2072836f21f9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:31:41 +0000 Subject: [PATCH 220/340] Bump org.apache.maven.plugins:maven-dependency-plugin (#243) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3a055db6..57caf12e 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ --> maven-dependency-plugin org.apache.maven.plugins - 3.7.1 + 3.8.0 From 847f22f63fe6031936de8f9b32aa6cea76ee010a Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 11 Sep 2024 11:33:30 -0400 Subject: [PATCH 221/340] Fix containers not being notified when owner joins --- .../main/java/com/lishid/openinv/util/InventoryManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index 4210777a..deebb1ec 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -111,7 +111,10 @@ public void unload(@NotNull UUID uuid) { @Keep @EventHandler(priority = EventPriority.LOWEST) private void onPlayerJoin(@NotNull PlayerJoinEvent event) { - consumeLoaded(event.getPlayer().getUniqueId(), inventory -> checkViewerAccess(inventory, true)); + consumeLoaded(event.getPlayer().getUniqueId(), inventory -> { + inventory.setPlayerOnline(event.getPlayer()); + checkViewerAccess(inventory, true); + }); } @Keep From 54f078724f7ce407b1a6dc525c441e8638f9a225 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 11 Sep 2024 11:42:04 -0400 Subject: [PATCH 222/340] also quits Boy, and I thought having to make the first commit was embarrassing, wait till people see that I realized that was missing and didn't think about the counterpart. At least I did think about it though, I wasn't even testing on 1.20 where it would break things. --- .../main/java/com/lishid/openinv/util/InventoryManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index deebb1ec..9cbc82c0 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -120,7 +120,10 @@ private void onPlayerJoin(@NotNull PlayerJoinEvent event) { @Keep @EventHandler(priority = EventPriority.MONITOR) private void onPlayerQuit(@NotNull PlayerQuitEvent event) { - consumeLoaded(event.getPlayer().getUniqueId(), inventory -> checkViewerAccess(inventory, false)); + consumeLoaded(event.getPlayer().getUniqueId(), inventory -> { + inventory.setPlayerOffline(); + checkViewerAccess(inventory, false); + }); } @Keep From c6d36cc9cf0e1d93521580b060281ca9e63d62a8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 11 Sep 2024 11:49:58 -0400 Subject: [PATCH 223/340] Bump version to 5.1.2 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index ad3c3d15..156ff0c0 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2-SNAPSHOT + 5.1.2 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index a964d079..95ac44ab 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.2-SNAPSHOT + 5.1.2 openinvapi diff --git a/common/pom.xml b/common/pom.xml index 7f881012..b4cd1de6 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.2-SNAPSHOT + 5.1.2 openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index d3405710..c8e4edd6 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2-SNAPSHOT + 5.1.2 openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index c9a793d0..f6ae151c 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2-SNAPSHOT + 5.1.2 openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index e65f6518..57d2103a 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2-SNAPSHOT + 5.1.2 openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index b1b2b800..2b7dedc1 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.2-SNAPSHOT + 5.1.2 openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.1.2-SNAPSHOT + 5.1.2 compile com.lishid openinvadapter1_20_R4 - 5.1.2-SNAPSHOT + 5.1.2 compile com.lishid openinvadapter1_20_R3 - 5.1.2-SNAPSHOT + 5.1.2 compile diff --git a/pom.xml b/pom.xml index 57caf12e..01638a34 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.2-SNAPSHOT + 5.1.2 pom From 8229d5f0d9bf102671a08eb893510fa81222998e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 11 Sep 2024 11:50:30 -0400 Subject: [PATCH 224/340] Bump version to 5.1.3-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_20_R3/pom.xml | 2 +- internal/v1_20_R4/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- plugin/pom.xml | 8 ++++---- pom.xml | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index 156ff0c0..56dafff6 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2 + 5.1.3-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 95ac44ab..59cd0dbf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.2 + 5.1.3-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index b4cd1de6..e199a4d2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.2 + 5.1.3-SNAPSHOT openinvcommon diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml index c8e4edd6..54b542c7 100644 --- a/internal/v1_20_R3/pom.xml +++ b/internal/v1_20_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2 + 5.1.3-SNAPSHOT openinvadapter1_20_R3 diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_20_R4/pom.xml index f6ae151c..1741cc09 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_20_R4/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2 + 5.1.3-SNAPSHOT openinvadapter1_20_R4 diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 57d2103a..6374ba0c 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.2 + 5.1.3-SNAPSHOT openinvadapter1_21_R1 diff --git a/plugin/pom.xml b/plugin/pom.xml index 2b7dedc1..472cb848 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.2 + 5.1.3-SNAPSHOT openinvplugin @@ -47,19 +47,19 @@ com.lishid openinvadapter1_21_R1 - 5.1.2 + 5.1.3-SNAPSHOT compile com.lishid openinvadapter1_20_R4 - 5.1.2 + 5.1.3-SNAPSHOT compile com.lishid openinvadapter1_20_R3 - 5.1.2 + 5.1.3-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index 01638a34..95b59ce2 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.2 + 5.1.3-SNAPSHOT pom From 46b8b0c9280a1f104279a4059174f32ba466584c Mon Sep 17 00:00:00 2001 From: Boy0000 Date: Mon, 16 Sep 2024 02:00:49 +0200 Subject: [PATCH 225/340] Fix resolutions for items so mipmap level isnt lowered (#247) * fix resolutions for items so mipmap level isnt lowered * adjust crafting_output texture slightly --- .../openinv/textures/item/crafting_output.png | Bin 191 -> 216 bytes .../assets/openinv/textures/item/not_a_slot.png | Bin 89 -> 99 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/resource-pack/assets/openinv/textures/item/crafting_output.png b/resource-pack/assets/openinv/textures/item/crafting_output.png index 7590bb455342071c41f4fed540d68087cbd6fd60..a53a4142ffa32a1c31aca3b1377cdcbf0e04d892 100644 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!3HFyJAa%3QjEnx?oJHr&dIz4awd7YIEHw1 zzMXQAw?To&)&AIAixNh*1d(kCdJ)|GZ5b29lvSjh`#=1Ck?-F7)Ed+85aQ5TE)N^qfJLr zG278rZSRZ?0QBAL-K3FrtPyl&0BhZu#ql4jNB^wdo7p>SDz7@_T`i<>$?C9C2;rD0 trSxugzV9=loc%&NkrO%5F-j@j0uQ2*X%pggHkJSY002ovPDHLkV1hf9O`8A! diff --git a/resource-pack/assets/openinv/textures/item/not_a_slot.png b/resource-pack/assets/openinv/textures/item/not_a_slot.png index 300e19d2c712143eb9059b8224d6ad69f6bacd40..1d78e5692101da911f79816cc55ce6e6a3298faf 100644 GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|WIbIRLo9le uZOY!aHZj33IoG7XXZkNrwQgjg$$mqelF{r5}E)m Date: Tue, 1 Oct 2024 17:31:07 +0000 Subject: [PATCH 226/340] Bump com.google.errorprone:error_prone_core from 2.31.0 to 2.33.0 (#248) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95b59ce2..4c5258c1 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ com.google.errorprone error_prone_core - 2.31.0 + 2.33.0 From 2c51423882d52f778cad2b06a2df051267affdc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:31:48 +0000 Subject: [PATCH 227/340] Bump org.jetbrains:annotations from 24.1.0 to 25.0.0 (#249) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c5258c1..feb14671 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ annotations org.jetbrains provided - 24.1.0 + 25.0.0 spigot-api From d2ce3a542a7630286ad99e7627d2631f399c4216 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 20:23:16 -0400 Subject: [PATCH 228/340] Bump pascalgn/automerge-action from 0.16.3 to 0.16.4 (#250) Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.16.3 to 0.16.4. - [Release notes](https://github.com/pascalgn/automerge-action/releases) - [Commits](https://github.com/pascalgn/automerge-action/compare/v0.16.3...v0.16.4) --- updated-dependencies: - dependency-name: pascalgn/automerge-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/automerge_dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml index fb0ceb69..5b406454 100644 --- a/.github/workflows/automerge_dependabot.yml +++ b/.github/workflows/automerge_dependabot.yml @@ -47,7 +47,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" pull-request-number: "${{ env.PR_NUMBER }}" - name: Merge - uses: pascalgn/automerge-action@v0.16.3 + uses: pascalgn/automerge-action@v0.16.4 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "dependencies,java" From 21f6b005cdbffb83accff17db3b7c28f94a5a1d6 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 29 Oct 2024 10:51:24 -0400 Subject: [PATCH 229/340] Add support for 1.21.3 (#254) * Add support for 1.21.3 * Drop 1.20.4, 1.20.6, 1.21.0 * 1.20.0 is incompatible with the final changes made to inventories in 1.21.1 --- .../openinv/internal/OpenInventoryView.java | 77 -- internal/v1_20_R3/pom.xml | 77 -- .../internal/v1_20_R3/AnySilentContainer.java | 208 ----- .../internal/v1_20_R3/InternalAccessor.java | 64 -- .../openinv/internal/v1_20_R3/OpenPlayer.java | 184 ---- .../internal/v1_20_R3/PlayerManager.java | 292 ------- .../internal/v1_20_R3/SpecialEnderChest.java | 351 -------- .../v1_20_R3/SpecialPlayerInventory.java | 807 ------------------ .../openinv/internal/v1_20_R4/OpenPlayer.java | 194 ----- .../internal/v1_20_R4/PlayerManager.java | 292 ------- .../internal/v1_20_R4/SpecialEnderChest.java | 352 -------- .../v1_20_R4/SpecialPlayerInventory.java | 768 ----------------- .../container/menu/OpenChestMenu.java | 6 +- .../container/menu/OpenInventoryMenu.java | 2 +- internal/{v1_20_R4 => v1_21_R2}/pom.xml | 6 +- .../internal/v1_21_R2}/InternalAccessor.java | 29 +- .../container}/AnySilentContainer.java | 6 +- .../v1_21_R2/container/OpenEnderChest.java | 196 +++++ .../v1_21_R2/container/OpenInventory.java | 364 ++++++++ .../v1_21_R2/container/Placeholders.java | 183 ++++ .../container/bukkit/OpenDummyInventory.java | 163 ++++ .../container/bukkit/OpenPlayerInventory.java | 221 +++++ .../bukkit/OpenPlayerInventorySelf.java | 26 + .../container/menu/OpenChestMenu.java | 478 +++++++++++ .../container/menu/OpenEnderChestMenu.java | 53 ++ .../container/menu/OpenInventoryMenu.java | 262 ++++++ .../v1_21_R2/container/slot/Content.java | 69 ++ .../container/slot/ContentCrafting.java | 131 +++ .../container/slot/ContentCraftingResult.java | 48 ++ .../container/slot/ContentCursor.java | 117 +++ .../v1_21_R2/container/slot/ContentDrop.java | 88 ++ .../container/slot/ContentEquipment.java | 78 ++ .../v1_21_R2/container/slot/ContentList.java | 58 ++ .../container/slot/ContentOffHand.java | 46 + .../container/slot/ContentViewOnly.java | 56 ++ .../container/slot/SlotPlaceholder.java | 20 + .../v1_21_R2/container/slot/SlotViewOnly.java | 151 ++++ .../internal/v1_21_R2/player/OpenPlayer.java | 178 ++++ .../v1_21_R2/player/PlayerManager.java | 267 ++++++ plugin/pom.xml | 10 +- .../main/java/com/lishid/openinv/OpenInv.java | 7 - .../listener/LegacyInventoryListener.java | 234 ----- .../lishid/openinv/util/InternalAccessor.java | 15 +- pom.xml | 7 +- 44 files changed, 3301 insertions(+), 3940 deletions(-) delete mode 100644 common/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java delete mode 100644 internal/v1_20_R3/pom.xml delete mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java delete mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java delete mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java delete mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java delete mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java delete mode 100644 internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java delete mode 100644 internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java delete mode 100644 internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java delete mode 100644 internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java delete mode 100644 internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java rename internal/{v1_20_R4 => v1_21_R2}/pom.xml (94%) rename internal/{v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4 => v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2}/InternalAccessor.java (62%) rename internal/{v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4 => v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container}/AnySilentContainer.java (96%) create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java delete mode 100644 plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java diff --git a/common/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java b/common/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java deleted file mode 100644 index 618cdc01..00000000 --- a/common/src/main/java/com/lishid/openinv/internal/OpenInventoryView.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011-2022 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal; - -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; - -public class OpenInventoryView extends InventoryView { - - private final @NotNull Player player; - private final @NotNull ISpecialInventory inventory; - private final @NotNull String originalTitle; - private String title; - - public OpenInventoryView( - @NotNull Player player, - @NotNull ISpecialInventory inventory, - @NotNull String originalTitle) { - this.player = player; - this.inventory = inventory; - this.originalTitle = originalTitle; - } - - @Override - public @NotNull Inventory getTopInventory() { - return inventory.getBukkitInventory(); - } - - @Override - public @NotNull Inventory getBottomInventory() { - return getPlayer().getInventory(); - } - - @Override - public @NotNull HumanEntity getPlayer() { - return player; - } - - @Override - public @NotNull InventoryType getType() { - return inventory.getBukkitInventory().getType(); - } - - @Override - public @NotNull String getTitle() { - return title == null ? originalTitle : title; - } - - @Override - public @NotNull String getOriginalTitle() { - return originalTitle; - } - - @Override - public void setTitle(@NotNull String title) { - this.title = title; - } - -} diff --git a/internal/v1_20_R3/pom.xml b/internal/v1_20_R3/pom.xml deleted file mode 100644 index 54b542c7..00000000 --- a/internal/v1_20_R3/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 5.1.3-SNAPSHOT - - - openinvadapter1_20_R3 - OpenInvAdapter1_20_R3 - - - 17 - 17 - 1.20.4-R0.1-SNAPSHOT - - - - - org.spigotmc - spigot-api - ${spigot.version} - - - spigot - org.spigotmc - provided - ${spigot.version} - remapped-mojang - - - openinvapi - com.lishid - - - openinvcommon - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-compiler-plugin - - - net.md-5 - specialsource-maven-plugin - - - - - diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java deleted file mode 100644 index aaddac01..00000000 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R3; - -import com.lishid.openinv.internal.AnySilentContainerBase; -import com.lishid.openinv.util.ReflectionHelper; -import com.lishid.openinv.util.lang.LanguageManager; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.ShulkerBoxBlock; -import net.minecraft.world.level.block.TrappedChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.logging.Logger; - -public class AnySilentContainer extends AnySilentContainerBase { - - private final @NotNull Logger logger; - private final @NotNull LanguageManager lang; - private @Nullable Field serverPlayerGameModeGameType; - - public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { - this.logger = logger; - this.lang = lang; - try { - try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); - this.serverPlayerGameModeGameType.setAccessible(true); - } catch (NoSuchFieldException e) { - logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); - logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); - logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); - // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. - this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); - } - } catch (SecurityException e) { - logger.warning("Unable to directly write player game mode! SilentContainer will fail."); - logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); - } - } - - @Override - public boolean activateContainer( - @NotNull final Player bukkitPlayer, - final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - - final net.minecraft.world.level.Level level = player.level(); - final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final BlockEntity blockEntity = level.getBlockEntity(blockPos); - - if (blockEntity == null) { - return false; - } - - if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock - PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); - enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerManager.getContainers(enderChest.getContainerSize()); - int rows = enderChest.getContainerSize() / 9; - return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable("container.enderchest"))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(blockEntity instanceof MenuProvider menuProvider)) { - return false; - } - - BlockState blockState = level.getBlockState(blockPos); - Block block = blockState.getBlock(); - - if (block instanceof ChestBlock chestBlock) { - - // boolean flag: do not check if chest is blocked - menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); - - if (menuProvider == null) { - lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - if (block instanceof TrappedChestBlock) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof ShulkerBoxBlock) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BarrelBlock) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { - player.openMenu(menuProvider); - return true; - } - - // SilentChest requires access to setting players' game mode directly. - if (this.serverPlayerGameModeGameType == null) { - return false; - } - - if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { - if (lootable.lootTable != null) { - lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - player.openMenu(menuProvider); - this.forceGameType(player, gameType); - return true; - } - - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { - return; - } - - ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - - // Force game mode change without informing plugins or players. - // Regular game mode set calls GameModeChangeEvent and is cancellable. - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - - // ServerPlayer#closeContainer cannot be called without entering an - // infinite loop because this method is called during inventory close. - // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent - player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); - // From ServerPlayer#closeContainer - player.doCloseContainer(); - // Regular inventory close will handle the rest - packet sending, etc. - - // Revert forced game mode. - this.forceGameType(player, gameType); - } - - private void forceGameType(final ServerPlayer player, final GameType gameMode) { - if (this.serverPlayerGameModeGameType == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - this.serverPlayerGameModeGameType.setAccessible(true); - this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); - } - } - -} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java deleted file mode 100644 index 47e199da..00000000 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/InternalAccessor.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.lishid.openinv.internal.v1_20_R3; - -import com.lishid.openinv.internal.Accessor; -import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.internal.ISpecialEnderChest; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.util.lang.LanguageManager; -import net.minecraft.world.Container; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.logging.Logger; - -public class InternalAccessor implements Accessor { - - private final @NotNull PlayerManager manager; - private final @NotNull AnySilentContainer anySilentContainer; - - public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - manager = new PlayerManager(logger, lang); - anySilentContainer = new AnySilentContainer(logger, lang); - } - - @Override - public @NotNull PlayerManager getPlayerManager() { - return manager; - } - - @Override - public @NotNull IAnySilentContainer getAnySilentContainer() { - return anySilentContainer; - } - - @Override - public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { - return new SpecialPlayerInventory(player, player.isOnline()); - } - - @Override - public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { - return new SpecialEnderChest(player, player.isOnline()); - } - - @Override - public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { - if (!(bukkitInventory instanceof CraftInventory craftInventory)) { - return null; - } - Container container = craftInventory.getInventory(); - if (clazz.isInstance(container)) { - return clazz.cast(container); - } - return null; - } - - @Override - public void reload(@NotNull ConfigurationSection config) {} - -} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java deleted file mode 100644 index 52d553af..00000000 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R3; - -import com.lishid.openinv.event.OpenEvents; -import com.mojang.logging.LogUtils; -import net.minecraft.Util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_20_R3.CraftServer; -import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Set; - -public class OpenPlayer extends CraftPlayer { - - private static final Set RESET_TAGS = Set.of( - // net.minecraft.world.Entity#saveWithoutId(CompoundTag) - "CustomName", - "CustomNameVisible", - "Silent", - "NoGravity", - "Glowing", - "TicksFrozen", - "HasVisualFire", - "Tags", - "Passengers", - // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - "warden_spawn_tracker", - "enteredNetherPosition", - "SpawnX", - "SpawnY", - "SpawnZ", - "SpawnForced", - "SpawnAngle", - "SpawnDimension", - // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - "ShoulderEntityLeft", - "ShoulderEntityRight", - "LastDeathLocation", - // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - "ActiveEffects", // Backwards compat: Renamed from 1.19 - "active_effects", - "SleepingX", - "SleepingY", - "SleepingZ", - "Brain" - ); - - private final PlayerManager manager; - - OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { - super(server, entity); - this.manager = manager; - } - - @Override - public void loadData() { - manager.loadData(getHandle()); - } - - @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag oldData = isOnline() ? null : worldNBTStorage.getPlayerData(player.getStringUUID()); - CompoundTag playerData = getWritableTag(oldData); - playerData = player.saveWithoutId(playerData); - setExtraData(playerData); - - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, file); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, file, backupFile); - } catch (Exception e) { - LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); - } - } - - @Contract("null -> new") - private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { - if (oldData == null) { - return new CompoundTag(); - } - - // Copy old data. This is a deep clone, so operating on it should be safe. - oldData = oldData.copy(); - - // Remove vanilla/server data that is not written every time. - oldData.getAllKeys() - .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); - - return oldData; - } - - private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { - // Revert automatic updates to play timestamps. - copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); - } - - private void copyValue( - @NotNull CompoundTag source, - @NotNull CompoundTag target, - @NotNull String container, - @NotNull String key, - @NotNull Class tagType) { - CompoundTag oldContainer = getTag(source, container, CompoundTag.class); - CompoundTag newContainer = getTag(target, container, CompoundTag.class); - - // New container being null means the server implementation doesn't store this data. - if (newContainer == null) { - return; - } - - // If old tag exists, copy it to new location, removing otherwise. - setTag(newContainer, key, getTag(oldContainer, key, tagType)); - } - - private @Nullable T getTag( - @Nullable CompoundTag container, - @NotNull String key, - @NotNull Class dataType) { - if (container == null) { - return null; - } - Tag value = container.get(key); - if (value == null || !dataType.isAssignableFrom(value.getClass())) { - return null; - } - return dataType.cast(value); - } - - private void setTag( - @NotNull CompoundTag container, - @NotNull String key, - @Nullable T data) { - if (data == null) { - container.remove(key); - } else { - container.put(key, data); - } - } - -} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java deleted file mode 100644 index 0df4c729..00000000 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerManager.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R3; - -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.InventoryViewTitle; -import com.lishid.openinv.internal.OpenInventoryView; -import com.lishid.openinv.util.lang.LanguageManager; -import com.mojang.authlib.GameProfile; -import com.mojang.serialization.Dynamic; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ClientInformation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.ChatVisiblity; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.craftbukkit.v1_20_R3.CraftServer; -import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; -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.CraftContainer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.UUID; -import java.util.logging.Logger; - -public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { - - private static boolean paper; - - static { - try { - Class.forName("io.papermc.paper.configuration.Configuration"); - paper = true; - } catch (ClassNotFoundException ignored) { - paper = false; - } - } - - private final @NotNull Logger logger; - private final @NotNull LanguageManager lang; - private @Nullable Field bukkitEntity; - - public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { - this.logger = logger; - this.lang = lang; - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); - logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); - bukkitEntity = null; - } - } - - public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - ServerPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); - } - - return nmsPlayer; - } - - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - ServerLevel worldServer = server.getLevel(Level.OVERWORLD); - - if (worldServer == null) { - return null; - } - - // Create a new ServerPlayer. - ServerPlayer entity = createNewPlayer(server, worldServer, offline); - - // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. - entity.getAdvancements().stopListening(); - - // Try to load the player's data. - if (loadData(entity)) { - // If data is loaded successfully, return the Bukkit entity. - return entity.getBukkitEntity(); - } - - return null; - } - - private @NotNull ServerPlayer createNewPlayer( - @NotNull MinecraftServer server, - @NotNull ServerLevel worldServer, - @NotNull final OfflinePlayer offline) { - // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) - // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - - ClientInformation dummyInfo = new ClientInformation( - "en_us", - 1, // Reduce distance just in case. - ChatVisiblity.HIDDEN, // Don't accept chat. - false, - ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, - ServerPlayer.DEFAULT_MAIN_HAND, - true, - false // Don't list in player list (not that this player is in the list anyway). - ); - - ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - logger.log( - java.util.logging.Level.WARNING, - e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); - } - - return entity; - } - - boolean loadData(@NotNull ServerPlayer player) { - // See CraftPlayer#loadData - CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player); - - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return false; - } - - // Read basic data into the player. - player.load(loadedData); - // Also read "extra" data. - player.readAdditionalSaveData(loadedData); - // Game type settings are also loaded separately. - player.loadGameTypes(loadedData); - - if (paper) { - // Paper: world is not loaded by ServerPlayer#load(CompoundTag). - parseWorld(player, loadedData); - } - - return true; - } - - private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { - // See PlayerList#placeNewPlayer - World bukkitWorld; - if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { - // Modern Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); - } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { - // Legacy Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); - } else { - // Vanilla player data. - DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(logger::warning) - .map(player.server::getLevel) - // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); - return; - } - if (bukkitWorld == null) { - player.spawnIn(null); - return; - } - player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); - } - - private void injectPlayer(ServerPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } - - bukkitEntity.setAccessible(true); - - bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); - } - - @Override - public @NotNull Player inject(@NotNull Player player) { - try { - ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { - return openPlayer; - } - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - logger.log( - java.util.logging.Level.WARNING, - e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); - return player; - } - } - - @Override - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { - - ServerPlayer nmsPlayer = getHandle(player); - - if (nmsPlayer.connection == null) { - return null; - } - - InventoryViewTitle title = InventoryViewTitle.of(inventory); - - if (title == null) { - return player.openInventory(inventory.getBukkitInventory()); - } - - InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(lang, player, inventory)); - AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { - @Override - public MenuType getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - - container.setTitle(Component.literal(view.getOriginalTitle())); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); - - if (container == null) { - return null; - } - - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - Component.literal(container.getBukkitView().getTitle()))); - nmsPlayer.containerMenu = container; - nmsPlayer.initMenu(container); - - return container.getBukkitView(); - - } - - static @NotNull MenuType getContainers(int inventorySize) { - - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; // PLAYER - case 41, 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> MenuType.GENERIC_9x3; // Default 27-slot inventory - }; - } - -} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java deleted file mode 100644 index 2b8b3f38..00000000 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R3; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.ContainerListener; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { - - private final CraftInventory inventory; - private ServerPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChestInventory().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerManager.getHandle(player); - - // Set owner to new player. - this.owner = onlinePlayer; - - // Set player's ender chest contents to our modified contents. - PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); - for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { - onlineEnderChest.setItem(i, this.items.get(i)); - } - - // Set our item array to the new inventory's array. - this.items = onlineEnderChest.items; - - // Add viewers to new inventory. - onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChestInventory().getViewers(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public void setActiveChest(EnderChestBlockEntity enderChest) { - this.owner.getEnderChestInventory().setActiveChest(enderChest); - } - - @Override - public boolean isActiveChest(EnderChestBlockEntity enderChest) { - return this.owner.getEnderChestInventory().isActiveChest(enderChest); - } - - @Override - public int getMaxStackSize() { - return this.owner.getEnderChestInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChestInventory().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChestInventory().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void addListener(ContainerListener listener) { - this.owner.getEnderChestInventory().addListener(listener); - } - - @Override - public void removeListener(ContainerListener listener) { - this.owner.getEnderChestInventory().removeListener(listener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int i, int j) { - ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack addItem(ItemStack itemstack) { - ItemStack localItem = itemstack.copy(); - this.moveItemToOccupiedSlotsWithSameType(localItem); - if (localItem.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.moveItemToEmptySlots(localItem); - return localItem.isEmpty() ? ItemStack.EMPTY : localItem; - } - } - - @Override - public boolean canAddItem(ItemStack itemstack) { - for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || (ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) { - return true; - } - } - - return false; - } - - private void moveItemToEmptySlots(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (localItem.isEmpty()) { - this.setItem(i, itemstack.copy()); - itemstack.setCount(0); - return; - } - } - } - - private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameTags(localItem, itemstack)) { - this.moveItemsBetweenStacks(itemstack, localItem); - if (itemstack.isEmpty()) { - return; - } - } - } - } - - private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { - int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); - int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); - if (j > 0) { - itemstack1.grow(j); - itemstack.shrink(j); - this.setChanged(); - } - } - - @Override - public ItemStack removeItemNoUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.items.set(i, ItemStack.EMPTY); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.setChanged(); - } - - @Override - public int getContainerSize() { - return this.owner.getEnderChestInventory().getContainerSize(); - } - - @Override - public boolean isEmpty() { - return this.items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void startOpen(Player player) { - } - - @Override - public void stopOpen(Player player) { - } - - @Override - public boolean canPlaceItem(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clearContent() { - this.items.clear(); - this.setChanged(); - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountStack(itemstack); - } - - } - - @Override - public List removeAllItems() { - List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clearContent(); - return list; - } - - @Override - public ItemStack removeItemType(Item item, int i) { - ItemStack itemstack = new ItemStack(item, 0); - - for(int j = this.getContainerSize() - 1; j >= 0; --j) { - ItemStack localItem = this.getItem(j); - if (localItem.getItem().equals(item)) { - int k = i - itemstack.getCount(); - ItemStack splitItem = localItem.split(k); - itemstack.grow(splitItem.getCount()); - if (itemstack.getCount() == i) { - break; - } - } - } - - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); - } - - @Override - public void fromTag(ListTag listTag) { - for (int i = 0; i < this.getContainerSize(); ++i) { - this.setItem(i, ItemStack.EMPTY); - } - - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.of(compoundTag)); - } - } - - } - -} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java deleted file mode 100644 index e4c278b2..00000000 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java +++ /dev/null @@ -1,807 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R3; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.DamageTypeTags; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private Player player; - private NonNullList items; - private NonNullList armor; - private NonNullList offhand; - private List> compartments; - - public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.selected = player.getInventory().selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - Player offlinePlayer = this.player; - Player onlinePlayer = PlayerManager.getHandle(player); - onlinePlayer.getInventory().transaction.addAll(this.transaction); - - // Set owner to new player. - this.player = onlinePlayer; - - // Set player's inventory contents to our modified contents. - Inventory onlineInventory = onlinePlayer.getInventory(); - for (int i = 0; i < getContainerSize(); ++i) { - onlineInventory.setItem(i, getRawItem(i)); - } - onlineInventory.selected = this.selected; - - // Set our item arrays to the new inventory's arrays. - this.items = onlineInventory.items; - this.armor = onlineInventory.armor; - this.offhand = onlineInventory.offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - - // Add existing viewers to new viewer list. - Inventory offlineInventory = offlinePlayer.getInventory(); - // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. - offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); - onlineInventory.transaction.addAll(offlineInventory.transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - private @NotNull ItemStack getRawItem(int i) { - if (i < 0) { - return ItemStack.EMPTY; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - return list.get(i); - } - } - - return ItemStack.EMPTY; - } - - private void setRawItem(int i, @NotNull ItemStack itemStack) { - if (i < 0) { - return; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - list.set(i, itemStack); - } - } - } - - private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - - private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { - if (index < items.size()) { - return new IndexedCompartment(items, getReversedItemSlotNum(index)); - } - - index -= items.size(); - - if (index < armor.size()) { - return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); - } - - index -= armor.size(); - - if (index < offhand.size()) { - return new IndexedCompartment(offhand, index); - } - - index -= offhand.size(); - - return new IndexedCompartment(null, index); - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - private boolean contains(Predicate predicate) { - return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.player.getInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.player.getInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.player.getInventory().getViewers(); - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public int getMaxStackSize() { - return this.player.getInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int size) { - this.player.getInventory().setMaxStackSize(size); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public boolean hasCustomName() { - return false; - } - - @Override - public List getContents() { - return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); - } - - @Override - public ItemStack getSelected() { - return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; - } - - private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getRawItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { - remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); - if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { - remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; - } - - @Override - public int getFreeSlot() { - for(int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void setPickedItem(ItemStack itemstack) { - int i = this.findSlotMatchingItem(itemstack); - if (isHotbarSlot(i)) { - this.selected = i; - } else if (i == -1) { - this.selected = this.getSuitableHotbarSlot(); - if (!this.items.get(this.selected).isEmpty()) { - int j = this.getFreeSlot(); - if (j != -1) { - this.items.set(j, this.items.get(this.selected)); - } - } - - this.items.set(this.selected, itemstack); - } else { - this.pickSlot(i); - } - - } - - @Override - public void pickSlot(int i) { - this.selected = this.getSuitableHotbarSlot(); - ItemStack itemstack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int findSlotMatchingItem(ItemStack itemstack) { - for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) { - return i; - } - } - - return -1; - } - - @Override - public int findSlotMatchingUnusedItem(ItemStack itemStack) { - for(int i = 0; i < this.items.size(); ++i) { - ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) { - return i; - } - } - - return -1; - } - - @Override - public int getSuitableHotbarSlot() { - int i; - int j; - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (!this.items.get(i).isEnchanted()) { - return i; - } - } - - return this.selected; - } - - @Override - public void swapPaint(double d0) { - if (d0 > 0.0D) { - d0 = 1.0D; - } - - if (d0 < 0.0D) { - d0 = -1.0D; - } - - this.selected = (int) (this.selected - d0); - - while (this.selected < 0) { - this.selected += 9; - } - - while(this.selected >= 9) { - this.selected -= 9; - } - } - - @Override - public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); - j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); - ItemStack itemstack = this.player.containerMenu.getCarried(); - j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); - if (itemstack.isEmpty()) { - this.player.containerMenu.setCarried(ItemStack.EMPTY); - } - - return j; - } - - private int addResource(ItemStack itemstack) { - int i = this.getSlotWithRemainingSpace(itemstack); - if (i == -1) { - i = this.getFreeSlot(); - } - - return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); - } - - private int addResource(int i, ItemStack itemstack) { - Item item = itemstack.getItem(); - int j = itemstack.getCount(); - ItemStack localItemStack = this.getRawItem(i); - if (localItemStack.isEmpty()) { - localItemStack = new ItemStack(item, 0); - if (itemstack.hasTag()) { - // hasTag ensures tag not null - //noinspection ConstantConditions - localItemStack.setTag(itemstack.getTag().copy()); - } - - this.setRawItem(i, localItemStack); - } - - int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount()); - - if (k > this.getMaxStackSize() - localItemStack.getCount()) { - k = this.getMaxStackSize() - localItemStack.getCount(); - } - - if (k != 0) { - j -= k; - localItemStack.grow(k); - localItemStack.setPopTime(5); - } - - return j; - } - - @Override - public int getSlotWithRemainingSpace(ItemStack itemstack) { - if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { - return this.selected; - } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { - return 40; - } else { - for(int i = 0; i < this.items.size(); ++i) { - if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void tick() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (!compartment.get(i).isEmpty()) { - compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i); - } - } - } - - } - - @Override - public boolean add(ItemStack itemStack) { - return this.add(-1, itemStack); - } - - @Override - public boolean add(int i, ItemStack itemStack) { - if (itemStack.isEmpty()) { - return false; - } else { - try { - if (itemStack.isDamaged()) { - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i >= 0) { - this.items.set(i, itemStack.copy()); - this.items.get(i).setPopTime(5); - itemStack.setCount(0); - return true; - } else if (this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemStack.getCount(); - if (i == -1) { - itemStack.setCount(this.addResource(itemStack)); - } else { - itemStack.setCount(this.addResource(i, itemStack)); - } - } while(!itemStack.isEmpty() && itemStack.getCount() < j); - - if (itemStack.getCount() == j && this.player.getAbilities().instabuild) { - itemStack.setCount(0); - return true; - } else { - return itemStack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); - crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); - crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); - crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); - throw new ReportedException(crashReport); - } - } - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack) { - this.placeItemBackInInventory(itemStack, true); - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { - while(true) { - if (!itemStack.isEmpty()) { - int i = this.getSlotWithRemainingSpace(itemStack); - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); - if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { - ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); - } - continue; - } - - this.player.drop(itemStack, false); - } - - return; - } - } - - @Override - public ItemStack removeItem(int rawIndex, final int j) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null - || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { - return ItemStack.EMPTY; - } - - return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); - } - - @Override - public void removeItem(ItemStack itemStack) { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (compartment.get(i) == itemStack) { - compartment.set(i, ItemStack.EMPTY); - break; - } - } - } - } - - @Override - public ItemStack removeItemNoUpdate(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); - - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - - return removed; - } - - @Override - public void setItem(int rawIndex, final ItemStack itemStack) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - this.player.drop(itemStack, true); - return; - } - - indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); - } - - @Override - public float getDestroySpeed(BlockState blockState) { - return this.items.get(this.selected).getDestroySpeed(blockState); - } - - @Override - public ListTag save(ListTag listTag) { - for (int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.offhand.size(); ++i) { - if (!this.offhand.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(compoundTag); - listTag.add(compoundTag); - } - } - - return listTag; - } - - @Override - public void load(ListTag listTag) { - this.items.clear(); - this.armor.clear(); - this.offhand.clear(); - - for(int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.of(compoundTag); - if (!itemstack.isEmpty()) { - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } - } - } - - } - - @Override - public int getContainerSize() { - return 45; - } - - @Override - public boolean isEmpty() { - return !contains(itemStack -> !itemStack.isEmpty()); - } - - @Override - public ItemStack getItem(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - return indexedCompartment.compartment().get(indexedCompartment.index()); - } - - @Override - public Component getName() { - return this.player.getName(); - } - - @Override - public ItemStack getArmor(int index) { - return this.armor.get(index); - } - - @Override - public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) { - if (damage > 0.0F) { - damage /= 4.0F; - if (damage < 1.0F) { - damage = 1.0F; - } - - for (int index : armorIndices) { - ItemStack itemstack = this.armor.get(index); - if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) { - itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index))); - } - } - } - } - - @Override - public void dropAll() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - ItemStack itemstack = compartment.get(i); - if (!itemstack.isEmpty()) { - this.player.drop(itemstack, true, false); - compartment.set(i, ItemStack.EMPTY); - } - } - } - } - - @Override - public void setChanged() { - super.setChanged(); - } - - @Override - public int getTimesChanged() { - return super.getTimesChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean contains(ItemStack itemstack) { - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(itemstack.getItem())); - } - - @Override - public boolean contains(TagKey tagKey) { - - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); - } - - @Override - public void replaceWith(Inventory inventory) { - Function getter; - - if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { - getter = specialPlayerInventory::getRawItem; - } else { - getter = inventory::getItem; - } - - for(int i = 0; i < this.getContainerSize(); ++i) { - this.setRawItem(i, getter.apply(i)); - } - - this.selected = inventory.selected; - } - - @Override - public void clearContent() { - for (NonNullList compartment : this.compartments) { - compartment.clear(); - } - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountSimpleStack(itemstack); - } - } - - @Override - public ItemStack removeFromSelected(boolean dropWholeStack) { - ItemStack itemstack = this.getSelected(); - return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); - } - -} diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java deleted file mode 100644 index 9223fa19..00000000 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/OpenPlayer.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R4; - -import com.lishid.openinv.event.OpenEvents; -import com.mojang.logging.LogUtils; -import net.minecraft.Util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_20_R4.CraftServer; -import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Set; - -public class OpenPlayer extends CraftPlayer { - - /** - * List of tags to always reset when saving. - * - * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) - * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - */ - private static final Set RESET_TAGS = Set.of( - // Entity#saveWithoutId(CompoundTag) - "CustomName", - "CustomNameVisible", - "Silent", - "NoGravity", - "Glowing", - "TicksFrozen", - "HasVisualFire", - "Tags", - "Passengers", - // ServerPlayer#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - "warden_spawn_tracker", - "enteredNetherPosition", - "SpawnX", - "SpawnY", - "SpawnZ", - "SpawnForced", - "SpawnAngle", - "SpawnDimension", - "raid_omen_position", - // Player#addAdditionalSaveData(CompoundTag) - "ShoulderEntityLeft", - "ShoulderEntityRight", - "LastDeathLocation", - "current_explosion_impact_pos", - // LivingEntity#addAdditionalSaveData(CompoundTag) - "ActiveEffects", // Backwards compat: Renamed from 1.19 - "active_effects", - "SleepingX", - "SleepingY", - "SleepingZ", - "Brain" - ); - - private final PlayerManager manager; - - OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { - super(server, entity); - this.manager = manager; - } - - @Override - public void loadData() { - manager.loadData(getHandle()); - } - - @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); - CompoundTag playerData = getWritableTag(oldData); - playerData = player.saveWithoutId(playerData); - setExtraData(playerData); - - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, tempFile); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, tempFile, backupFile); - } catch (Exception e) { - LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); - } - } - - @Contract("null -> new") - private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { - if (oldData == null) { - return new CompoundTag(); - } - - // Copy old data. This is a deep clone, so operating on it should be safe. - oldData = oldData.copy(); - - // Remove vanilla/server data that is not written every time. - oldData.getAllKeys() - .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); - - return oldData; - } - - private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { - // Revert automatic updates to play timestamps. - copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); - } - - private void copyValue( - @NotNull CompoundTag source, - @NotNull CompoundTag target, - @NotNull String container, - @NotNull String key, - @NotNull Class tagType) { - CompoundTag oldContainer = getTag(source, container, CompoundTag.class); - CompoundTag newContainer = getTag(target, container, CompoundTag.class); - - // New container being null means the server implementation doesn't store this data. - if (newContainer == null) { - return; - } - - // If old tag exists, copy it to new location, removing otherwise. - setTag(newContainer, key, getTag(oldContainer, key, tagType)); - } - - private @Nullable T getTag( - @Nullable CompoundTag container, - @NotNull String key, - @NotNull Class dataType) { - if (container == null) { - return null; - } - Tag value = container.get(key); - if (value == null || !dataType.isAssignableFrom(value.getClass())) { - return null; - } - return dataType.cast(value); - } - - private void setTag( - @NotNull CompoundTag container, - @NotNull String key, - @Nullable T data) { - if (data == null) { - container.remove(key); - } else { - container.put(key, data); - } - } - -} diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java deleted file mode 100644 index 755500db..00000000 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/PlayerManager.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R4; - -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.InventoryViewTitle; -import com.lishid.openinv.internal.OpenInventoryView; -import com.lishid.openinv.util.lang.LanguageManager; -import com.mojang.authlib.GameProfile; -import com.mojang.serialization.Dynamic; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ClientInformation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.ChatVisiblity; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.craftbukkit.v1_20_R4.CraftServer; -import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; -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.CraftContainer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.UUID; -import java.util.logging.Logger; - -public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { - - private static boolean paper; - - static { - try { - Class.forName("io.papermc.paper.configuration.Configuration"); - paper = true; - } catch (ClassNotFoundException ignored) { - paper = false; - } - } - - private final @NotNull Logger logger; - private final @NotNull LanguageManager lang; - private @Nullable Field bukkitEntity; - - public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) { - this.logger = logger; - this.lang = lang; - try { - bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); - } catch (NoSuchFieldException e) { - logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); - logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); - bukkitEntity = null; - } - } - - public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - ServerPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); - } - - return nmsPlayer; - } - - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - ServerLevel worldServer = server.getLevel(Level.OVERWORLD); - - if (worldServer == null) { - return null; - } - - // Create a new ServerPlayer. - ServerPlayer entity = createNewPlayer(server, worldServer, offline); - - // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. - entity.getAdvancements().stopListening(); - - // Try to load the player's data. - if (loadData(entity)) { - // If data is loaded successfully, return the Bukkit entity. - return entity.getBukkitEntity(); - } - - return null; - } - - private @NotNull ServerPlayer createNewPlayer( - @NotNull MinecraftServer server, - @NotNull ServerLevel worldServer, - @NotNull final OfflinePlayer offline) { - // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) - // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) - GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); - - ClientInformation dummyInfo = new ClientInformation( - "en_us", - 1, // Reduce distance just in case. - ChatVisiblity.HIDDEN, // Don't accept chat. - false, - ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, - ServerPlayer.DEFAULT_MAIN_HAND, - true, - false // Don't list in player list (not that this player is in the list anyway). - ); - - ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); - - try { - injectPlayer(entity); - } catch (IllegalAccessException e) { - logger.log( - java.util.logging.Level.WARNING, - e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); - } - - return entity; - } - - boolean loadData(@NotNull ServerPlayer player) { - // See CraftPlayer#loadData - CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); - - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return false; - } - - // Read basic data into the player. - player.load(loadedData); - // Also read "extra" data. - player.readAdditionalSaveData(loadedData); - // Game type settings are also loaded separately. - player.loadGameTypes(loadedData); - - if (paper) { - // Paper: world is not loaded by ServerPlayer#load(CompoundTag). - parseWorld(player, loadedData); - } - - return true; - } - - private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { - // See PlayerList#placeNewPlayer - World bukkitWorld; - if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { - // Modern Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); - } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { - // Legacy Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); - } else { - // Vanilla player data. - DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(logger::warning) - .map(player.server::getLevel) - // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); - return; - } - if (bukkitWorld == null) { - player.spawnIn(null); - return; - } - player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); - } - - private void injectPlayer(ServerPlayer player) throws IllegalAccessException { - if (bukkitEntity == null) { - return; - } - - bukkitEntity.setAccessible(true); - - bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); - } - - @Override - public @NotNull Player inject(@NotNull Player player) { - try { - ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { - return openPlayer; - } - injectPlayer(nmsPlayer); - return nmsPlayer.getBukkitEntity(); - } catch (IllegalAccessException e) { - logger.log( - java.util.logging.Level.WARNING, - e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); - return player; - } - } - - @Override - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { - - ServerPlayer nmsPlayer = getHandle(player); - - if (nmsPlayer.connection == null) { - return null; - } - - InventoryViewTitle title = InventoryViewTitle.of(inventory); - - if (title == null) { - return player.openInventory(inventory.getBukkitInventory()); - } - - InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(lang, player, inventory)); - AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) { - @Override - public MenuType getType() { - return getContainers(inventory.getBukkitInventory().getSize()); - } - }; - - container.setTitle(Component.literal(view.getOriginalTitle())); - container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container); - - if (container == null) { - return null; - } - - nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), - Component.literal(container.getBukkitView().getTitle()))); - nmsPlayer.containerMenu = container; - nmsPlayer.initMenu(container); - - return container.getBukkitView(); - - } - - static @NotNull MenuType getContainers(int inventorySize) { - - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 36 -> MenuType.GENERIC_9x4; // PLAYER - case 41, 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> MenuType.GENERIC_9x3; // Default 27-slot inventory - }; - } - -} diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java deleted file mode 100644 index d38ac428..00000000 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialEnderChest.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R4; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.ContainerListener; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest { - - private final CraftInventory inventory; - private ServerPlayer owner; - private NonNullList items; - private boolean playerOnline; - - public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) { - super(PlayerManager.getHandle(player)); - this.inventory = new CraftInventory(this); - this.owner = PlayerManager.getHandle(player); - this.playerOnline = online; - this.items = this.owner.getEnderChestInventory().items; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - ServerPlayer offlinePlayer = this.owner; - ServerPlayer onlinePlayer = PlayerManager.getHandle(player); - - // Set owner to new player. - this.owner = onlinePlayer; - - // Set player's ender chest contents to our modified contents. - PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory(); - for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) { - onlineEnderChest.setItem(i, this.items.get(i)); - } - - // Set our item array to the new inventory's array. - this.items = onlineEnderChest.items; - - // Add viewers to new inventory. - onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public List getContents() { - return this.items; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.owner.getEnderChestInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.owner.getEnderChestInventory().getViewers(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public void setActiveChest(EnderChestBlockEntity enderChest) { - this.owner.getEnderChestInventory().setActiveChest(enderChest); - } - - @Override - public boolean isActiveChest(EnderChestBlockEntity enderChest) { - return this.owner.getEnderChestInventory().isActiveChest(enderChest); - } - - @Override - public int getMaxStackSize() { - return this.owner.getEnderChestInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int i) { - this.owner.getEnderChestInventory().setMaxStackSize(i); - } - - @Override - public InventoryHolder getOwner() { - return this.owner.getEnderChestInventory().getOwner(); - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void addListener(ContainerListener listener) { - this.owner.getEnderChestInventory().addListener(listener); - } - - @Override - public void removeListener(ContainerListener listener) { - this.owner.getEnderChestInventory().removeListener(listener); - } - - @Override - public ItemStack getItem(int i) { - return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int i, int j) { - ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j); - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack addItem(ItemStack itemstack) { - ItemStack localItem = itemstack.copy(); - this.moveItemToOccupiedSlotsWithSameType(localItem); - if (localItem.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.moveItemToEmptySlots(localItem); - return localItem.isEmpty() ? ItemStack.EMPTY : localItem; - } - } - - @Override - public boolean canAddItem(ItemStack itemstack) { - for (ItemStack itemstack1 : this.items) { - if (itemstack1.isEmpty() || (ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) { - return true; - } - } - - return false; - } - - private void moveItemToEmptySlots(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (localItem.isEmpty()) { - this.setItem(i, itemstack.copy()); - itemstack.setCount(0); - return; - } - } - } - - private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) { - for(int i = 0; i < this.getContainerSize(); ++i) { - ItemStack localItem = this.getItem(i); - if (ItemStack.isSameItemSameComponents(localItem, itemstack)) { - this.moveItemsBetweenStacks(itemstack, localItem); - if (itemstack.isEmpty()) { - return; - } - } - } - } - - private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) { - int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize()); - int j = Math.min(itemstack.getCount(), i - itemstack1.getCount()); - if (j > 0) { - itemstack1.grow(j); - itemstack.shrink(j); - this.setChanged(); - } - } - - @Override - public ItemStack removeItemNoUpdate(int i) { - ItemStack itemstack = this.items.get(i); - if (itemstack.isEmpty()) { - return ItemStack.EMPTY; - } else { - this.items.set(i, ItemStack.EMPTY); - return itemstack; - } - } - - @Override - public void setItem(int i, ItemStack itemstack) { - this.items.set(i, itemstack); - if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { - itemstack.setCount(this.getMaxStackSize()); - } - - this.setChanged(); - } - - @Override - public int getContainerSize() { - return this.owner.getEnderChestInventory().getContainerSize(); - } - - @Override - public boolean isEmpty() { - return this.items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public void startOpen(Player player) { - } - - @Override - public void stopOpen(Player player) { - } - - @Override - public boolean canPlaceItem(int i, ItemStack itemstack) { - return true; - } - - @Override - public void clearContent() { - this.items.clear(); - this.setChanged(); - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountStack(itemstack); - } - - } - - @Override - public List removeAllItems() { - List list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList()); - this.clearContent(); - return list; - } - - @Override - public ItemStack removeItemType(Item item, int i) { - ItemStack itemstack = new ItemStack(item, 0); - - for(int j = this.getContainerSize() - 1; j >= 0; --j) { - ItemStack localItem = this.getItem(j); - if (localItem.getItem().equals(item)) { - int k = i - itemstack.getCount(); - ItemStack splitItem = localItem.split(k); - itemstack.grow(splitItem.getCount()); - if (itemstack.getCount() == i) { - break; - } - } - } - - if (!itemstack.isEmpty()) { - this.setChanged(); - } - - return itemstack; - } - - @Override - public String toString() { - return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString(); - } - - @Override - public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) { - for (int i = 0; i < this.getContainerSize(); ++i) { - this.setItem(i, ItemStack.EMPTY); - } - - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j < this.getContainerSize()) { - this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag)); - } - } - - } - -} diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java deleted file mode 100644 index 155f4224..00000000 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/SpecialPlayerInventory.java +++ /dev/null @@ -1,768 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_20_R4; - -import com.google.common.collect.ImmutableList; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.core.NonNullList; -import net.minecraft.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.InventoryHolder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory { - - private final CraftInventory inventory; - private boolean playerOnline; - private Player player; - private NonNullList items; - private NonNullList armor; - private NonNullList offhand; - private List> compartments; - - public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) { - super(PlayerManager.getHandle(bukkitPlayer)); - this.inventory = new CraftInventory(this); - this.playerOnline = online; - this.player = super.player; - this.selected = player.getInventory().selected; - this.items = this.player.getInventory().items; - this.armor = this.player.getInventory().armor; - this.offhand = this.player.getInventory().offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - if (this.playerOnline) { - return; - } - - Player offlinePlayer = this.player; - Player onlinePlayer = PlayerManager.getHandle(player); - onlinePlayer.getInventory().transaction.addAll(this.transaction); - - // Set owner to new player. - this.player = onlinePlayer; - - // Set player's inventory contents to our modified contents. - Inventory onlineInventory = onlinePlayer.getInventory(); - for (int i = 0; i < getContainerSize(); ++i) { - onlineInventory.setItem(i, getRawItem(i)); - } - onlineInventory.selected = this.selected; - - // Set our item arrays to the new inventory's arrays. - this.items = onlineInventory.items; - this.armor = onlineInventory.armor; - this.offhand = onlineInventory.offhand; - this.compartments = ImmutableList.of(this.items, this.armor, this.offhand); - - // Add existing viewers to new viewer list. - Inventory offlineInventory = offlinePlayer.getInventory(); - // Remove self from listing - player is always a viewer of their own inventory, prevent duplicates. - offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity()); - onlineInventory.transaction.addAll(offlineInventory.transaction); - - this.playerOnline = true; - } - - @Override - public @NotNull CraftInventory getBukkitInventory() { - return this.inventory; - } - - @Override - public void setPlayerOffline() { - this.playerOnline = false; - } - - @Override - public @NotNull HumanEntity getPlayer() { - return this.player.getBukkitEntity(); - } - - private @NotNull ItemStack getRawItem(int i) { - if (i < 0) { - return ItemStack.EMPTY; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - return list.get(i); - } - } - - return ItemStack.EMPTY; - } - - private void setRawItem(int i, @NotNull ItemStack itemStack) { - if (i < 0) { - return; - } - - NonNullList list; - for (Iterator> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) { - list = iterator.next(); - if (i < list.size()) { - list.set(i, itemStack); - } - } - } - - private record IndexedCompartment(@Nullable NonNullList compartment, int index) {} - - private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) { - if (index < items.size()) { - return new IndexedCompartment(items, getReversedItemSlotNum(index)); - } - - index -= items.size(); - - if (index < armor.size()) { - return new IndexedCompartment(armor, getReversedArmorSlotNum(index)); - } - - index -= armor.size(); - - if (index < offhand.size()) { - return new IndexedCompartment(offhand, index); - } - - index -= offhand.size(); - - return new IndexedCompartment(null, index); - } - - private int getReversedArmorSlotNum(final int i) { - if (i == 0) { - return 3; - } - if (i == 1) { - return 2; - } - if (i == 2) { - return 1; - } - if (i == 3) { - return 0; - } - return i; - } - - private int getReversedItemSlotNum(final int i) { - if (i >= 27) { - return i - 27; - } - return i + 9; - } - - @Override - public boolean contains(Predicate predicate) { - return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate); - } - - @Override - public List getArmorContents() { - return this.armor; - } - - @Override - public void onOpen(CraftHumanEntity who) { - this.player.getInventory().onOpen(who); - } - - @Override - public void onClose(CraftHumanEntity who) { - this.player.getInventory().onClose(who); - } - - @Override - public List getViewers() { - return this.player.getInventory().getViewers(); - } - - @Override - public InventoryHolder getOwner() { - return this.player.getBukkitEntity(); - } - - @Override - public int getMaxStackSize() { - return this.player.getInventory().getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int size) { - this.player.getInventory().setMaxStackSize(size); - } - - @Override - public Location getLocation() { - return this.player.getBukkitEntity().getLocation(); - } - - @Override - public boolean hasCustomName() { - return false; - } - - @Override - public List getContents() { - return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList()); - } - - @Override - public ItemStack getSelected() { - return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY; - } - - private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) { - return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); - } - - @Override - public int canHold(ItemStack itemstack) { - int remains = itemstack.getCount(); - - for (int i = 0; i < this.items.size(); ++i) { - ItemStack itemstack1 = this.getRawItem(i); - if (itemstack1.isEmpty()) { - return itemstack.getCount(); - } - - if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) { - remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); - } - - if (remains <= 0) { - return itemstack.getCount(); - } - } - - ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size()); - if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) { - remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount(); - } - - return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains; - } - - @Override - public int getFreeSlot() { - for(int i = 0; i < this.items.size(); ++i) { - if (this.items.get(i).isEmpty()) { - return i; - } - } - - return -1; - } - - @Override - public void setPickedItem(ItemStack itemstack) { - int i = this.findSlotMatchingItem(itemstack); - if (isHotbarSlot(i)) { - this.selected = i; - } else if (i == -1) { - this.selected = this.getSuitableHotbarSlot(); - if (!this.items.get(this.selected).isEmpty()) { - int j = this.getFreeSlot(); - if (j != -1) { - this.items.set(j, this.items.get(this.selected)); - } - } - - this.items.set(this.selected, itemstack); - } else { - this.pickSlot(i); - } - - } - - @Override - public void pickSlot(int i) { - this.selected = this.getSuitableHotbarSlot(); - ItemStack itemstack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(i)); - this.items.set(i, itemstack); - } - - @Override - public int findSlotMatchingItem(ItemStack itemstack) { - for(int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) { - return i; - } - } - - return -1; - } - - @Override - public int findSlotMatchingUnusedItem(ItemStack itemStack) { - for(int i = 0; i < this.items.size(); ++i) { - ItemStack localItem = this.items.get(i); - if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) { - return i; - } - } - - return -1; - } - - @Override - public int getSuitableHotbarSlot() { - int i; - int j; - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (this.items.get(i).isEmpty()) { - return i; - } - } - - for(j = 0; j < 9; ++j) { - i = (this.selected + j) % 9; - if (!this.items.get(i).isEnchanted()) { - return i; - } - } - - return this.selected; - } - - @Override - public void swapPaint(double d0) { - int i = (int) Math.signum(d0); - - this.selected -= i; - - while (this.selected < 0) { - this.selected += 9; - } - - while(this.selected >= 9) { - this.selected -= 9; - } - } - - @Override - public int clearOrCountMatchingItems(Predicate predicate, int i, Container container) { - byte b0 = 0; - boolean flag = i == 0; - int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag); - j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag); - ItemStack itemstack = this.player.containerMenu.getCarried(); - j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag); - if (itemstack.isEmpty()) { - this.player.containerMenu.setCarried(ItemStack.EMPTY); - } - - return j; - } - - private int addResource(ItemStack itemstack) { - int i = this.getSlotWithRemainingSpace(itemstack); - if (i == -1) { - i = this.getFreeSlot(); - } - - return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack); - } - - private int addResource(int i, ItemStack itemstack) { - int j = itemstack.getCount(); - ItemStack localItemStack = this.getRawItem(i); - if (localItemStack.isEmpty()) { - localItemStack = itemstack.copyWithCount(0); - this.setRawItem(i, localItemStack); - } - - int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount()); - - if (k != 0) { - j -= k; - localItemStack.grow(k); - localItemStack.setPopTime(5); - } - - return j; - } - - @Override - public int getSlotWithRemainingSpace(ItemStack itemstack) { - if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) { - return this.selected; - } else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) { - return 40; - } else { - for(int i = 0; i < this.items.size(); ++i) { - if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) { - return i; - } - } - - return -1; - } - } - - @Override - public void tick() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (!compartment.get(i).isEmpty()) { - compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i); - } - } - } - - } - - @Override - public boolean add(ItemStack itemStack) { - return this.add(-1, itemStack); - } - - @Override - public boolean add(int i, ItemStack itemStack) { - if (itemStack.isEmpty()) { - return false; - } else { - try { - if (itemStack.isDamaged()) { - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i >= 0) { - this.items.set(i, itemStack.copy()); - this.items.get(i).setPopTime(5); - itemStack.setCount(0); - return true; - } else if (this.player.hasInfiniteMaterials()) { - itemStack.setCount(0); - return true; - } else { - return false; - } - } else { - int j; - do { - j = itemStack.getCount(); - if (i == -1) { - itemStack.setCount(this.addResource(itemStack)); - } else { - itemStack.setCount(this.addResource(i, itemStack)); - } - } while(!itemStack.isEmpty() && itemStack.getCount() < j); - - if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) { - itemStack.setCount(0); - return true; - } else { - return itemStack.getCount() < j; - } - } - } catch (Throwable var6) { - CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added"); - crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem())); - crashReportCategory.setDetail("Item data", itemStack.getDamageValue()); - crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString()); - throw new ReportedException(crashReport); - } - } - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack) { - this.placeItemBackInInventory(itemStack, true); - } - - @Override - public void placeItemBackInInventory(ItemStack itemStack, boolean flag) { - while(true) { - if (!itemStack.isEmpty()) { - int i = this.getSlotWithRemainingSpace(itemStack); - if (i == -1) { - i = this.getFreeSlot(); - } - - if (i != -1) { - int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount(); - if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) { - ((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i))); - } - continue; - } - - this.player.drop(itemStack, false); - } - - return; - } - } - - @Override - public ItemStack removeItem(int rawIndex, final int j) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null - || indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) { - return ItemStack.EMPTY; - } - - return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j); - } - - @Override - public void removeItem(ItemStack itemStack) { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - if (compartment.get(i) == itemStack) { - compartment.set(i, ItemStack.EMPTY); - break; - } - } - } - } - - @Override - public ItemStack removeItemNoUpdate(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY); - - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - - return removed; - } - - @Override - public void setItem(int rawIndex, final ItemStack itemStack) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - this.player.drop(itemStack, true); - return; - } - - indexedCompartment.compartment().set(indexedCompartment.index(), itemStack); - } - - @Override - public float getDestroySpeed(BlockState blockState) { - return this.items.get(this.selected).getDestroySpeed(blockState); - } - - @Override - public ListTag save(ListTag listTag) { - for (int i = 0; i < this.items.size(); ++i) { - if (!this.items.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)i); - this.items.get(i).save(this.player.registryAccess(), compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.armor.size(); ++i) { - if (!this.armor.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 100)); - this.armor.get(i).save(this.player.registryAccess(), compoundTag); - listTag.add(compoundTag); - } - } - - for (int i = 0; i < this.offhand.size(); ++i) { - if (!this.offhand.get(i).isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte)(i + 150)); - this.offhand.get(i).save(this.player.registryAccess(), compoundTag); - listTag.add(compoundTag); - } - } - - return listTag; - } - - @Override - public void load(ListTag listTag) { - this.items.clear(); - this.armor.clear(); - this.offhand.clear(); - - for(int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY); - if (j < this.items.size()) { - this.items.set(j, itemstack); - } else if (j >= 100 && j < this.armor.size() + 100) { - this.armor.set(j - 100, itemstack); - } else if (j >= 150 && j < this.offhand.size() + 150) { - this.offhand.set(j - 150, itemstack); - } - } - - } - - @Override - public int getContainerSize() { - return 45; - } - - @Override - public boolean isEmpty() { - return !contains(itemStack -> !itemStack.isEmpty()); - } - - @Override - public ItemStack getItem(int rawIndex) { - IndexedCompartment indexedCompartment = getIndexedContent(rawIndex); - - if (indexedCompartment.compartment() == null) { - return ItemStack.EMPTY; - } - - return indexedCompartment.compartment().get(indexedCompartment.index()); - } - - @Override - public Component getName() { - return this.player.getName(); - } - - @Override - public ItemStack getArmor(int index) { - return this.armor.get(index); - } - - @Override - public void dropAll() { - for (NonNullList compartment : this.compartments) { - for (int i = 0; i < compartment.size(); ++i) { - ItemStack itemstack = compartment.get(i); - if (!itemstack.isEmpty()) { - this.player.drop(itemstack, true, false); - compartment.set(i, ItemStack.EMPTY); - } - } - } - } - - @Override - public void setChanged() { - super.setChanged(); - } - - @Override - public int getTimesChanged() { - return super.getTimesChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public boolean contains(ItemStack itemstack) { - return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack)); - } - - @Override - public boolean contains(TagKey tagKey) { - return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey)); - } - - @Override - public void replaceWith(Inventory inventory) { - Function getter; - - if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) { - getter = specialPlayerInventory::getRawItem; - } else { - getter = inventory::getItem; - } - - for(int i = 0; i < this.getContainerSize(); ++i) { - this.setRawItem(i, getter.apply(i)); - } - - this.selected = inventory.selected; - } - - @Override - public void clearContent() { - for (NonNullList compartment : this.compartments) { - compartment.clear(); - } - } - - @Override - public void fillStackedContents(StackedContents stackedContents) { - for (ItemStack itemstack : this.items) { - stackedContents.accountSimpleStack(itemstack); - } - } - - @Override - public ItemStack removeFromSelected(boolean dropWholeStack) { - ItemStack itemstack = this.getSelected(); - return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1); - } - -} diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java index cf95822c..512ab769 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java @@ -45,7 +45,7 @@ public abstract class OpenChestMenu> bukkitEntity; + private CraftInventoryView, Inventory> bukkitEntity; // Syncher fields private @Nullable ContainerSynchronizer synchronizer; private final List dataSlots = new ArrayList<>(); @@ -132,7 +132,7 @@ protected void preSlotSetup() {} @Override - public final @NotNull CraftInventoryView> getBukkitView() { + public final @NotNull CraftInventoryView, Inventory> getBukkitView() { if (bukkitEntity == null) { bukkitEntity = createBukkitEntity(); } @@ -140,7 +140,7 @@ protected void preSlotSetup() {} return bukkitEntity; } - protected @NotNull CraftInventoryView> createBukkitEntity() { + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { Inventory top; if (viewOnly) { top = new OpenDummyInventory(container); diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java index d5567f62..f0c75a6f 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java @@ -99,7 +99,7 @@ protected void preSlotSetup() { } @Override - protected @NotNull CraftInventoryView> createBukkitEntity() { + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { org.bukkit.inventory.Inventory bukkitInventory; if (viewOnly) { bukkitInventory = new OpenDummyInventory(container); diff --git a/internal/v1_20_R4/pom.xml b/internal/v1_21_R2/pom.xml similarity index 94% rename from internal/v1_20_R4/pom.xml rename to internal/v1_21_R2/pom.xml index 1741cc09..024f2ab8 100644 --- a/internal/v1_20_R4/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -26,13 +26,13 @@ 5.1.3-SNAPSHOT - openinvadapter1_20_R4 - OpenInvAdapter1_20_R4 + openinvadapter1_21_R2 + OpenInvAdapter1_21_R2 21 21 - 1.20.6-R0.1-SNAPSHOT + 1.21.3-R0.1-SNAPSHOT diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java similarity index 62% rename from internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java rename to internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java index ff599644..3b873935 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/InternalAccessor.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java @@ -1,28 +1,36 @@ -package com.lishid.openinv.internal.v1_20_R4; +package com.lishid.openinv.internal.v1_21_R2; import com.lishid.openinv.internal.Accessor; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.v1_21_R2.container.AnySilentContainer; +import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.logging.Level; import java.util.logging.Logger; public class InternalAccessor implements Accessor { + private final @NotNull Logger logger; private final @NotNull PlayerManager manager; private final @NotNull AnySilentContainer anySilentContainer; public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - manager = new PlayerManager(logger, lang); + this.logger = logger; + manager = new PlayerManager(logger); anySilentContainer = new AnySilentContainer(logger, lang); } @@ -38,12 +46,12 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { @Override public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { - return new SpecialPlayerInventory(player, player.isOnline()); + return new OpenInventory(player); } @Override public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { - return new SpecialEnderChest(player, player.isOnline()); + return new OpenEnderChest(player); } @Override @@ -59,6 +67,15 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } @Override - public void reload(@NotNull ConfigurationSection config) {} + public void reload(@NotNull ConfigurationSection config) { + ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); + if (placeholders != null) { + try { + Placeholders.load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + } } diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/AnySilentContainer.java similarity index 96% rename from internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java rename to internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/AnySilentContainer.java index 9ea9065f..39b52b67 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/AnySilentContainer.java @@ -14,9 +14,11 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_20_R4; +package com.lishid.openinv.internal.v1_21_R2.container; import com.lishid.openinv.internal.AnySilentContainerBase; +import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenChestMenu; +import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -102,7 +104,7 @@ public boolean activateContainer( PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = PlayerManager.getContainers(enderChest.getContainerSize()); + MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); }, Component.translatable("container.enderchest"))); diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java new file mode 100644 index 00000000..2f0b3d97 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java @@ -0,0 +1,196 @@ +package com.lishid.openinv.internal.v1_21_R2.container; + +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenEnderChestMenu; +import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedItemContents; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.StackedContentsCompatible; +import net.minecraft.world.item.ItemStack; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned, + ISpecialEnderChest { + + private CraftInventory inventory; + private @NotNull ServerPlayer owner; + private NonNullList items; + private int maxStack = 64; + private final List transaction = new ArrayList<>(); + + public OpenEnderChest(@NotNull org.bukkit.entity.Player player) { + this.owner = PlayerManager.getHandle(player); + this.items = owner.getEnderChestInventory().items; + } + + @Override + public @NotNull ServerPlayer getOwnerHandle() { + return owner; + } + + @Override + public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { + if (inventory == null) { + inventory = new CraftInventory(this) { + @Override + public @NotNull InventoryType getType() { + return InventoryType.ENDER_CHEST; + } + }; + } + return inventory; + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + owner = PlayerManager.getHandle(player); + NonNullList activeItems = owner.getEnderChestInventory().items; + + // Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally. + int max = Math.min(items.size(), activeItems.size()); + for (int index = 0; index < max; ++index) { + activeItems.set(index, items.get(index)); + } + + items = activeItems; + } + + @Override + public void setPlayerOffline() {} + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return owner.getBukkitEntity(); + } + + @Override + public int getContainerSize() { + return items.size(); + } + + @Override + public boolean isEmpty() { + return items.stream().allMatch(ItemStack::isEmpty); + } + + @Override + public ItemStack getItem(int index) { + return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int index, int amount) { + ItemStack itemstack = ContainerHelper.removeItem(items, index, amount); + + if (!itemstack.isEmpty()) { + setChanged(); + } + + return itemstack; + } + + @Override + public ItemStack removeItemNoUpdate(int index) { + return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY; + } + + @Override + public void setItem(int index, ItemStack itemStack) { + if (index >= 0 && index < items.size()) { + items.set(index, itemStack); + } + } + + @Override + public int getMaxStackSize() { + return maxStack; + } + + @Override + public void setChanged() { + this.owner.getEnderChestInventory().setChanged(); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public List getContents() { + return items; + } + + @Override + public void onOpen(CraftHumanEntity craftHumanEntity) { + transaction.add(craftHumanEntity); + } + + @Override + public void onClose(CraftHumanEntity craftHumanEntity) { + transaction.remove(craftHumanEntity); + } + + @Override + public List getViewers() { + return transaction; + } + + @Override + public org.bukkit.entity.Player getOwner() { + return getPlayer(); + } + + @Override + public void setMaxStackSize(int size) { + maxStack = size; + } + + @Override + public @Nullable Location getLocation() { + return null; + } + + @Override + public void clearContent() { + items.clear(); + setChanged(); + } + + @Override + public void fillStackedContents(StackedItemContents stackedContents) { + for (ItemStack itemstack : items) { + stackedContents.accountStack(itemstack); + } + } + + public Component getTitle() { + return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName()) + .append(Component.translatable("container.enderchest")) + .append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName())); + } + + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly); + } + return null; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java new file mode 100644 index 00000000..ef3113ff --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java @@ -0,0 +1,364 @@ +package com.lishid.openinv.internal.v1_21_R2.container; + +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventory; +import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenInventoryMenu; +import com.lishid.openinv.internal.v1_21_R2.container.slot.Content; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCrafting; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCraftingResult; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCursor; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentList; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentOffHand; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; +import net.minecraft.ChatFormatting; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class OpenInventory implements Container, InternalOwned, ISpecialPlayerInventory { + + private final List slots; + private final int size; + private ServerPlayer owner; + private int maxStackSize = 99; + private CraftInventory bukkitEntity; + public List transaction = new ArrayList<>(); + + public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { + owner = PlayerManager.getHandle(bukkitPlayer); + + // Get total size, rounding up to nearest 9 for client compatibility. + int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; + size = ((int) Math.ceil(rawSize / 9.0)) * 9; + + slots = NonNullList.withSize(size, new ContentViewOnly(owner)); + setupSlots(); + } + + private void setupSlots() { + // Top of inventory: Regular contents. + int nextIndex = addMainInventory(); + + // If inventory is expected size, we can arrange slots to be pretty. + Inventory ownerInv = owner.getInventory(); + if (ownerInv.items.size() == 36 + && ownerInv.armor.size() == 4 + && ownerInv.offhand.size() == 1 + && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) { + // Armor slots: Bottom left. + addArmor(36); + // Off-hand: Below chestplate. + addOffHand(46); + // Drop slot: Bottom right. + slots.set(53, new ContentDrop(owner)); + // Cursor slot: Above drop. + slots.set(44, new ContentCursor(owner)); + + // Crafting is displayed in the bottom right corner. + // As we're using the pretty view, this is a 3x2. + addCrafting(41, true); + return; + } + + // Otherwise we'll just add elements linearly. + nextIndex = addArmor(nextIndex); + nextIndex = addOffHand(nextIndex); + nextIndex = addCrafting(nextIndex, false); + slots.set(nextIndex, new ContentCursor(owner)); + // Drop slot last. + slots.set(slots.size() - 1, new ContentDrop(owner)); + } + + private int addMainInventory() { + int listSize = owner.getInventory().items.size(); + // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, + // so everything else needs to move up a row. + int hotbarDiff = listSize - 9; + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + InventoryType.SlotType type; + int invIndex; + if (localIndex < hotbarDiff) { + invIndex = localIndex + 9; + type = InventoryType.SlotType.CONTAINER; + } else { + type = InventoryType.SlotType.QUICKBAR; + invIndex = localIndex - hotbarDiff; + } + + slots.set(localIndex, new ContentList(owner, invIndex, type) { + @Override + public void setHolder(@NotNull ServerPlayer holder) { + items = holder.getInventory().items; + } + }); + } + return listSize; + } + + private int addArmor(int startIndex) { + int listSize = owner.getInventory().armor.size(); + + for (int i = 0; i < listSize; ++i) { + // Armor slots go bottom to top; boots are slot 0, helmet is slot 3. + // Since we have to display horizontally due to space restrictions, + // making the left side the "top" is more user-friendly. + int armorIndex; + EquipmentSlot slot; + switch (i) { + case 3 -> { + armorIndex = 0; + slot = EquipmentSlot.FEET; + } + case 2 -> { + armorIndex = 1; + slot = EquipmentSlot.LEGS; + } + case 1 -> { + armorIndex = 2; + slot = EquipmentSlot.CHEST; + } + case 0 -> { + armorIndex = 3; + slot = EquipmentSlot.HEAD; + } + default -> { + // In the event that new armor slots are added, they can be placed at the end. + armorIndex = i; + slot = EquipmentSlot.MAINHAND; + } + } + + slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot)); + } + + return startIndex + listSize; + } + + private int addOffHand(int startIndex) { + int listSize = owner.getInventory().offhand.size(); + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex)); + } + return startIndex + listSize; + } + + private int addCrafting(int startIndex, boolean pretty) { + int listSize = owner.inventoryMenu.getCraftSlots().getContents().size(); + pretty &= listSize == 4; + + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + // Pretty display is a 2x2 rather than linear. + // If index is in top row, grid is not 2x2, or pretty is disabled, just use current index. + // Otherwise, subtract 2 and add 9 to start in the same position on the next row. + int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); + + slots.set(modIndex, new ContentCrafting(owner, localIndex)); + } + + if (pretty) { + slots.set(startIndex + 2, new ContentViewOnly(owner) { + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + return Placeholders.craftingOutput; + } + }; + } + }); + slots.set(startIndex + 11, new ContentCraftingResult(owner)); + } + + return startIndex + listSize; + } + + public Slot getMenuSlot(int index, int x, int y) { + return slots.get(index).asSlot(this, index, x, y); + } + + public InventoryType.SlotType getSlotType(int index) { + return slots.get(index).getSlotType(); + } + + public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { + MutableComponent component = Component.empty(); + // Prefix for use with custom bitmap image fonts. + if (owner.equals(viewer)) { + component.append( + Component.translatableWithFallback("openinv.container.inventory.self", "") + .withStyle(style -> style + .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withColor(ChatFormatting.WHITE))); + } else { + component.append( + Component.translatableWithFallback("openinv.container.inventory.other", "") + .withStyle(style -> style + .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withColor(ChatFormatting.WHITE))); + } + // Normal title: "Inventory - OwnerName" + component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) + .append(Component.translatable("container.inventory")) + .append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName())); + return component; + } + + @Override + public ServerPlayer getOwnerHandle() { + return owner; + } + + @Override + public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { + if (bukkitEntity == null) { + bukkitEntity = new OpenPlayerInventory(this); + } + return bukkitEntity; + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + ServerPlayer newOwner = PlayerManager.getHandle(player); + // Only transfer regular inventory - crafting and cursor slots are transient. + newOwner.getInventory().replaceWith(owner.getInventory()); + owner = newOwner; + // Update slots to point to new inventory. + slots.forEach(slot -> slot.setHolder(newOwner)); + } + + @Override + public void setPlayerOffline() {} + + @Override + public boolean isInUse() { + return !transaction.isEmpty(); + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return getOwner(); + } + + @Override + public int getContainerSize() { + return size; + } + + @Override + public boolean isEmpty() { + return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty); + } + + @Override + public ItemStack getItem(int index) { + return slots.get(index).get(); + } + + @Override + public ItemStack removeItem(int index, int amount) { + return slots.get(index).removePartial(amount); + } + + @Override + public ItemStack removeItemNoUpdate(int index) { + return slots.get(index).remove(); + } + + @Override + public void setItem(int index, ItemStack itemStack) { + slots.get(index).set(itemStack); + } + + @Override + public int getMaxStackSize() { + return maxStackSize; + } + + @Override + public void setMaxStackSize(int maxStackSize) { + this.maxStackSize = maxStackSize; + } + + @Override + public void setChanged() {} + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public List getContents() { + NonNullList contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); + for (int i = 0; i < getContainerSize(); ++i) { + contents.set(i, getItem(i)); + } + return contents; + } + + @Override + public void onOpen(CraftHumanEntity viewer) { + transaction.add(viewer); + } + + @Override + public void onClose(CraftHumanEntity viewer) { + transaction.remove(viewer); + } + + @Override + public List getViewers() { + return transaction; + } + + @Override + public org.bukkit.entity.Player getOwner() { + return owner.getBukkitEntity(); + } + + @Override + public Location getLocation() { + return owner.getBukkitEntity().getLocation(); + } + + @Override + public void clearContent() { + owner.getInventory().clearContent(); + owner.inventoryMenu.getCraftSlots().clearContent(); + owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots()); + owner.containerMenu.setCarried(ItemStack.EMPTY); + } + + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); + } + return null; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java new file mode 100644 index 00000000..ec9422f5 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java @@ -0,0 +1,183 @@ +package com.lishid.openinv.internal.v1_21_R2.container; + +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Unit; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.CustomModelData; +import net.minecraft.world.item.component.DyedItemColor; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.entity.BannerPattern; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.entity.BannerPatterns; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_21_R2.CraftRegistry; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.List; +import java.util.Optional; + +public final class Placeholders { + + private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); + public static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); + public static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); + public static @NotNull ItemStack cursor = defaultCursor(); + public static @NotNull ItemStack drop = defaultDrop(); + public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); + public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); + public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); + public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); + public static @NotNull ItemStack emptyOffHand = getEmptyShield(); + public static @NotNull ItemStack notSlot = defaultNotSlot(); + public static @NotNull ItemStack blockedOffline = defaultBlockedOffline(); + + static { + for (GameType type : GameType.values()) { + // Barrier: "Not available - Creative" etc. + ItemStack typeItem = new ItemStack(Items.BARRIER); + typeItem.set( + DataComponents.ITEM_NAME, + Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName())); + BLOCKED_GAME_TYPE.put(type, typeItem); + } + } + + public static void load(@NotNull ConfigurationSection section) throws Exception { + craftingOutput = parse(section, "crafting-output", craftingOutput); + cursor = parse(section, "cursor", cursor); + drop = parse(section, "drop", drop); + emptyHelmet = parse(section, "empty-helmet", emptyHelmet); + emptyChestplate = parse(section, "empty-chestplate", emptyChestplate); + emptyLeggings = parse(section, "empty-leggings", emptyLeggings); + emptyBoots = parse(section, "empty-boots", emptyBoots); + emptyOffHand = parse(section, "empty-off-hand", emptyOffHand); + notSlot = parse(section, "not-a-slot", notSlot); + blockedOffline = parse(section, "blocked.offline", blockedOffline); + BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE))); + BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); + } + + private static @NotNull ItemStack parse( + @NotNull ConfigurationSection section, + @NotNull String path, + @NotNull ItemStack defaultStack) throws Exception { + String itemText = section.getString(path); + + if (itemText == null) { + return defaultStack; + } + + CompoundTag compoundTag = TagParser.parseTag(itemText); + Optional parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag); + return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); + } + + public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { + if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { + return blockedOffline; + } + + return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY); + } + + private static ItemStack defaultCraftingOutput() { + // Crafting table: "Crafting" + ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE); + itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting")); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack defaultCursor() { + // Cursor-like banner with no tooltip + ItemStack itemStack = new ItemStack(Items.WHITE_BANNER); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value(); + BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value(); + BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value(); + itemStack.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + return itemStack; + } + + private static ItemStack defaultDrop() { + // Dropper: "Drop Selected Item" + ItemStack itemStack = new ItemStack(Items.DROPPER); + // Note: translatable component, not keybind component! We want the text identifying the keybind, not the key. + itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop")); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack getEmptyArmor(ItemLike item) { + // Inventory-background-grey-ish leather armor with no tooltip + ItemStack itemStack = new ItemStack(item); + DyedItemColor color = new DyedItemColor(0xC8C8C8, false); + itemStack.set(DataComponents.DYED_COLOR, color); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack getEmptyShield() { + ItemStack itemStack = new ItemStack(Items.SHIELD); + itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value(); + BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value(); + BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value(); + BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value(); + BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value(); + itemStack.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack defaultNotSlot() { + // White pane with no tooltip + ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + return itemStack; + } + + private static ItemStack defaultBlockedOffline() { + // Barrier: "Not available - Offline" + ItemStack itemStack = new ItemStack(Items.BARRIER); + itemStack.set(DataComponents.ITEM_NAME, + Component.translatable("options.narrator.notavailable") + .append(Component.literal(" - ")) + .append(Component.translatable("gui.socialInteractions.status_offline"))); + return itemStack; + } + + private Placeholders() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java new file mode 100644 index 00000000..61b89810 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java @@ -0,0 +1,163 @@ +package com.lishid.openinv.internal.v1_21_R2.container.bukkit; + +import com.lishid.openinv.internal.ViewOnly; +import net.minecraft.world.Container; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.ListIterator; + +/** + * A locked down "empty" inventory that rejects plugin interaction. + */ +public class OpenDummyInventory extends CraftInventory implements ViewOnly { + + public OpenDummyInventory(Container inventory) { + super(inventory); + } + + @Override + public @Nullable ItemStack getItem(int index) { + return null; + } + + @Override + public void setItem(int index, @Nullable ItemStack item) { + + } + + @SuppressWarnings("NonApiType") + @Override + public @NotNull HashMap addItem(@NotNull ItemStack... items) throws IllegalArgumentException { + return arrayToHashMap(items); + } + + @SuppressWarnings("NonApiType") + @Override + public @NotNull HashMap removeItem(@NotNull ItemStack... items) throws IllegalArgumentException { + return arrayToHashMap(items); + } + + @SuppressWarnings("NonApiType") + private static @NotNull HashMap arrayToHashMap(@NotNull ItemStack[] items) { + HashMap ignored = new HashMap<>(); + for (int index = 0; index < items.length; ++index) { + ignored.put(index, items[index]); + } + return ignored; + } + + @Override + public ItemStack[] getContents() { + return new ItemStack[getSize()]; + } + + @Override + public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException { + + } + + @Override + public @NotNull ItemStack[] getStorageContents() { + return new ItemStack[getSize()]; + } + + @Override + public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException { + + } + + @Override + public boolean contains(@NotNull Material material) throws IllegalArgumentException { + return false; + } + + @Override + public boolean contains(@Nullable ItemStack item) { + return false; + } + + @Override + public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException { + return false; + } + + @Override + public boolean contains(@Nullable ItemStack item, int amount) { + return false; + } + + @Override + public boolean containsAtLeast(@Nullable ItemStack item, int amount) { + return false; + } + + @SuppressWarnings("NonApiType") + @Override + public @NotNull HashMap all( + @NotNull Material material) throws IllegalArgumentException { + return new HashMap<>(); + } + + @SuppressWarnings("NonApiType") + @Override + public @NotNull HashMap all(@Nullable ItemStack item) { + return new HashMap<>(); + } + + @Override + public int first(@NotNull Material material) throws IllegalArgumentException { + return -1; + } + + @Override + public int first(@NotNull ItemStack item) { + return -1; + } + + @Override + public int firstEmpty() { + return -1; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public void remove(@NotNull Material material) throws IllegalArgumentException { + + } + + @Override + public void remove(@NotNull ItemStack item) { + + } + + @Override + public void clear(int index) { + + } + + @Override + public void clear() { + + } + + @Override + public @NotNull ListIterator iterator() { + return Collections.emptyListIterator(); + } + + @Override + public @NotNull ListIterator iterator(int index) { + return Collections.emptyListIterator(); + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java new file mode 100644 index 00000000..01be5ba1 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java @@ -0,0 +1,221 @@ +package com.lishid.openinv.internal.v1_21_R2.container.bukkit; + +import com.google.common.base.Preconditions; +import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { + + public OpenPlayerInventory(@NotNull OpenInventory inventory) { + super(inventory); + } + + @Override + public @NotNull OpenInventory getInventory() { + return (OpenInventory) super.getInventory(); + } + + @Override + public ItemStack[] getContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents()); + } + + @Override + public void setContents(ItemStack[] items) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + int size = internal.getContainerSize(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + + for (int index = 0; index < size; ++index) { + if (index < items.length) { + internal.setItem(index, CraftItemStack.asNMSCopy(items[index])); + } else { + internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY); + } + } + } + + @Override + public ItemStack[] getStorageContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().items); + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + NonNullList list = getInventory().getOwnerHandle().getInventory().items; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull InventoryType getType() { + return InventoryType.PLAYER; + } + + @Override + public @NotNull Player getHolder() { + return getInventory().getOwner(); + } + + @Override + public @NotNull ItemStack[] getArmorContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor); + } + + @Override + public void setArmorContents(@Nullable ItemStack[] items) { + NonNullList list = getInventory().getOwnerHandle().getInventory().armor; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull ItemStack[] getExtraContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand); + } + + @Override + public void setExtraContents(@Nullable ItemStack[] items) { + NonNullList list = getInventory().getOwnerHandle().getInventory().offhand; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @Nullable ItemStack getHelmet() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.HEAD.getIndex())); + } + + @Override + public void setHelmet(@Nullable ItemStack helmet) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet)); + } + + @Override + public @Nullable ItemStack getChestplate() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.HEAD.getIndex())); + } + + @Override + public void setChestplate(@Nullable ItemStack chestplate) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate)); + } + + @Override + public @Nullable ItemStack getLeggings() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.LEGS.getIndex())); + } + + @Override + public void setLeggings(@Nullable ItemStack leggings) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings)); + } + + @Override + public @Nullable ItemStack getBoots() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.FEET.getIndex())); + } + + @Override + public void setBoots(@Nullable ItemStack boots) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots)); + } + + @Override + public @NotNull ItemStack getItemInMainHand() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return CraftItemStack.asCraftMirror(internal.getItem(internal.selected)); + } + + @Override + public void setItemInMainHand(@Nullable ItemStack item) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item)); + } + + @Override + public @NotNull ItemStack getItemInOffHand() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst()); + } + + @Override + public void setItemInOffHand(@Nullable ItemStack item) { + getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); + } + + @Override + public @NotNull ItemStack getItemInHand() { + return getItemInMainHand(); + } + + @Override + public void setItemInHand(@Nullable ItemStack stack) { + setItemInMainHand(stack); + } + + @Override + public int getHeldItemSlot() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return internal.items.size() - 9 + internal.selected; + } + + @Override + public void setHeldItemSlot(int slot) { + slot %= 9; + getInventory().getOwnerHandle().getInventory().selected = slot; + } + + @Override + public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { + return switch (slot) { + case HAND -> getItemInMainHand(); + case OFF_HAND -> getItemInOffHand(); + case FEET -> getBoots(); + case LEGS -> getLeggings(); + case CHEST -> getChestplate(); + case HEAD -> getHelmet(); + default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); + }; + } + + @Override + public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) { + switch (slot) { + case HAND -> setItemInMainHand(item); + case OFF_HAND -> setItemInOffHand(item); + case FEET -> setBoots(item); + case LEGS -> setLeggings(item); + case CHEST -> setChestplate(item); + case HEAD -> setHelmet(item); + default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); + } + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java new file mode 100644 index 00000000..e44eb4c9 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java @@ -0,0 +1,26 @@ +package com.lishid.openinv.internal.v1_21_R2.container.bukkit; + +import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class OpenPlayerInventorySelf extends OpenPlayerInventory { + + private final int offset; + + public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) { + super(inventory); + this.offset = offset; + } + + @Override + public ItemStack getItem(int index) { + return super.getItem(offset + index); + } + + @Override + public void setItem(int index, ItemStack item) { + super.setItem(offset + index, item); + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java new file mode 100644 index 00000000..d12c373a --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java @@ -0,0 +1,478 @@ +package com.lishid.openinv.internal.v1_21_R2.container.menu; + +import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotPlaceholder; +import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.ContainerListener; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.DataSlot; +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.inventory.CraftInventoryView; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. + */ +public abstract class OpenChestMenu> + extends AbstractContainerMenu { + + protected static final int BOTTOM_INVENTORY_SIZE = 36; + + protected final T container; + protected final ServerPlayer viewer; + protected final boolean viewOnly; + protected final boolean ownContainer; + protected final int topSize; + private CraftInventoryView, Inventory> bukkitEntity; + // Syncher fields + private @Nullable ContainerSynchronizer synchronizer; + private final List dataSlots = new ArrayList<>(); + private final IntList remoteDataSlots = new IntArrayList(); + private final List containerListeners = new ArrayList<>(); + private ItemStack remoteCarried = ItemStack.EMPTY; + private boolean suppressRemoteUpdates; + + protected OpenChestMenu( + @NotNull MenuType type, + int containerCounter, + @NotNull T container, + @NotNull ServerPlayer viewer, + boolean viewOnly) { + super(type, containerCounter); + this.container = container; + this.viewer = viewer; + this.viewOnly = viewOnly; + ownContainer = container.getOwnerHandle().equals(viewer); + topSize = getTopSize(viewer); + + preSlotSetup(); + + int upperRows = topSize / 9; + // View's upper inventory - our container + for (int row = 0; row < upperRows; ++row) { + for (int col = 0; col < 9; ++col) { + // x and y for client purposes, but hey, we're thorough here. + // Adapted from net.minecraft.world.inventory.ChestMenu + int x = 8 + col * 18; + int y = 18 + row * 18; + int index = row * 9 + col; + + // Guard against weird inventory sizes. + if (index >= container.getContainerSize()) { + addSlot(new SlotViewOnly(container, index, x, y)); + continue; + } + + Slot slot = getUpperSlot(index, x, y); + + addSlot(slot); + } + } + + // View's lower inventory - viewer inventory + int playerInvPad = (upperRows - 4) * 18; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + row * 18 + 103; + addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); + } + } + // Hotbar + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + 161; + addSlot(new Slot(viewer.getInventory(), col, x, y)); + } + } + + public static @NotNull MenuType getChestMenuType(int inventorySize) { + inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9; + return switch (inventorySize) { + case 9 -> MenuType.GENERIC_9x1; + case 18 -> MenuType.GENERIC_9x2; + case 27 -> MenuType.GENERIC_9x3; + case 36 -> MenuType.GENERIC_9x4; + case 45 -> MenuType.GENERIC_9x5; + case 54 -> MenuType.GENERIC_9x6; + default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize); + }; + } + + protected void preSlotSetup() {} + + protected @NotNull Slot getUpperSlot(int index, int x, int y) { + if (viewOnly) { + return new SlotViewOnly(container, index, x, y); + } + return new Slot(container, index, x, y); + } + + + @Override + public final @NotNull CraftInventoryView, Inventory> getBukkitView() { + if (bukkitEntity == null) { + bukkitEntity = createBukkitEntity(); + } + + return bukkitEntity; + } + + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { + Inventory top; + if (viewOnly) { + top = new OpenDummyInventory(container); + } else { + top = container.getBukkitInventory(); + } + return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) { + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } + return super.getInventory(rawSlot); + } + + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + return super.convertSlot(rawSlot); + } + + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly) { + return InventoryType.SlotType.OUTSIDE; + } + return super.getSlotType(slot); + } + }; + } + + private int getTopSize(ServerPlayer viewer) { + MenuType menuType = getType(); + if (menuType == null) { + throw new IllegalStateException("MenuType cannot be null!"); + } else if (menuType == MenuType.GENERIC_9x1) { + return 9; + } else if (menuType == MenuType.GENERIC_9x2) { + return 18; + } else if (menuType == MenuType.GENERIC_9x3) { + return 27; + } else if (menuType == MenuType.GENERIC_9x4) { + return 36; + } else if (menuType == MenuType.GENERIC_9x5) { + return 45; + } else if (menuType == MenuType.GENERIC_9x6) { + return 54; + } + // This is a bit gross, but allows us a safe fallthrough. + return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE; + } + + /** + * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake + * slots and respects {@link Slot#hasItem()}. + * + * @param itemStack the stack to quick-move + * @param rangeLow the start of the range of slots that can be moved to, inclusive + * @param rangeHigh the end of the range of slots that can be moved to, exclusive + * @param topDown whether to start at the top of the range or bottom + * @return whether the stack was modified as a result of being quick-moved + */ + @Override + protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { + boolean modified = false; + boolean stackable = itemStack.isStackable(); + Slot firstEmpty = null; + + for (int index = topDown ? rangeHigh - 1 : rangeLow; + !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); + index += topDown ? -1 : 1 + ) { + Slot slot = slots.get(index); + // If the slot cannot be added to, check the next slot. + if (slot.isFake() || !slot.mayPlace(itemStack)) { + continue; + } + + if (slot.hasItem()) { + // If the item isn't stackable, check the next slot. + if (!stackable) { + continue; + } + // Otherwise, add as many as we can from our stack to the slot. + modified |= addToExistingStack(itemStack, slot); + } else { + // If this is the first empty slot, keep track of it for later use. + if (firstEmpty == null) { + firstEmpty = slot; + } + // If the item isn't stackable, we've located the slot we're adding it to, so we're done. + if (!stackable) { + break; + } + } + } + + // If the item hasn't been fully added yet, add as many as we can to the first open slot. + if (!itemStack.isEmpty() && firstEmpty != null) { + firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); + firstEmpty.setChanged(); + modified = true; + } + + return modified; + } + + private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { + ItemStack existing = slot.getItem(); + + // If the items aren't the same, we can't add our item. + if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { + return false; + } + + int max = slot.getMaxStackSize(existing); + int existingCount = existing.getCount(); + + // If the stack is already full, we can't add more. + if (existingCount >= max) { + return false; + } + + int total = existingCount + itemStack.getCount(); + + // If the existing item can accept the entirety of our item, we're done! + if (total <= max) { + itemStack.setCount(0); + existing.setCount(total); + slot.setChanged(); + return true; + } + + // Otherwise, add as many as we can. + itemStack.shrink(max - existingCount); + existing.setCount(max); + slot.setChanged(); + return true; + } + + @Override + public void clicked(int i, int j, ClickType clickType, Player player) { + if (viewOnly) { + if (clickType == ClickType.QUICK_CRAFT) { + sendAllDataToRemote(); + } + return; + } + super.clicked(i, j, clickType, player); + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + // Overrides from here on are purely to modify the sync process to send placeholder items. + @Override + protected Slot addSlot(Slot slot) { + slot.index = this.slots.size(); + this.slots.add(slot); + this.lastSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(ItemStack.EMPTY); + return slot; + } + + @Override + protected DataSlot addDataSlot(DataSlot dataSlot) { + this.dataSlots.add(dataSlot); + this.remoteDataSlots.add(0); + return dataSlot; + } + + @Override + protected void addDataSlots(ContainerData containerData) { + for (int i = 0; i < containerData.getCount(); i++) { + this.addDataSlot(DataSlot.forContainer(containerData, i)); + } + } + + @Override + public void addSlotListener(ContainerListener containerListener) { + if (!this.containerListeners.contains(containerListener)) { + this.containerListeners.add(containerListener); + this.broadcastChanges(); + } + } + + @Override + public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { + this.synchronizer = containerSynchronizer; + this.sendAllDataToRemote(); + } + + @Override + public void sendAllDataToRemote() { + for (int index = 0; index < slots.size(); ++index) { + Slot slot = slots.get(index); + this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); + } + + remoteCarried = getCarried().copy(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); + } + + if (this.synchronizer != null) { + this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); + } + } + + @Override + public void broadcastCarriedItem() { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + + @Override + public void removeSlotListener(ContainerListener containerListener) { + this.containerListeners.remove(containerListener); + } + + @Override + public void broadcastChanges() { + for (int index = 0; index < this.slots.size(); ++index) { + Slot slot = this.slots.get(index); + ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + Supplier supplier = Suppliers.memoize(itemstack::copy); + this.triggerSlotListeners(index, itemstack, supplier); + this.synchronizeSlotToRemote(index, itemstack, supplier); + } + + this.synchronizeCarriedToRemote(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot dataSlot = this.dataSlots.get(index); + int j = dataSlot.get(); + if (dataSlot.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, j); + } + + this.synchronizeDataSlotToRemote(index, j); + } + } + + @Override + public void broadcastFullState() { + for (int index = 0; index < this.slots.size(); ++index) { + ItemStack itemstack = this.slots.get(index).getItem(); + this.triggerSlotListeners(index, itemstack, itemstack::copy); + } + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot containerproperty = this.dataSlots.get(index); + if (containerproperty.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, containerproperty.get()); + } + } + + this.sendAllDataToRemote(); + } + + private void updateDataSlotListeners(int i, int j) { + for (ContainerListener containerListener : this.containerListeners) { + containerListener.dataChanged(this, i, j); + } + } + + private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + ItemStack itemStack1 = this.lastSlots.get(index); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemStack2 = supplier.get(); + this.lastSlots.set(index, itemStack2); + + for (ContainerListener containerListener : this.containerListeners) { + containerListener.slotChanged(this, index, itemStack2); + } + } + } + + private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { + if (!this.suppressRemoteUpdates) { + ItemStack itemStack1 = this.remoteSlots.get(i); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemstack2 = supplier.get(); + this.remoteSlots.set(i, itemstack2); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, i, itemstack2); + } + } + } + } + + private void synchronizeDataSlotToRemote(int index, int value) { + if (!this.suppressRemoteUpdates) { + int existing = this.remoteDataSlots.getInt(index); + if (existing != value) { + this.remoteDataSlots.set(index, value); + if (this.synchronizer != null) { + this.synchronizer.sendDataChange(this, index, value); + } + } + } + } + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + } + + @Override + public void setRemoteCarried(ItemStack itemstack) { + this.remoteCarried = itemstack.copy(); + } + + @Override + public void suppressRemoteUpdates() { + this.suppressRemoteUpdates = true; + } + + @Override + public void resumeRemoteUpdates() { + this.suppressRemoteUpdates = false; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java new file mode 100644 index 00000000..832efa73 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java @@ -0,0 +1,53 @@ +package com.lishid.openinv.internal.v1_21_R2.container.menu; + +import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class OpenEnderChestMenu extends OpenChestMenu { + + public OpenEnderChestMenu( + @NotNull OpenEnderChest enderChest, + @NotNull ServerPlayer viewer, + int containerId, + boolean viewOnly) { + super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + if (viewOnly) { + return ItemStack.EMPTY; + } + + // See ChestMenu + Slot slot = this.slots.get(index); + + if (slot.isFake() || !slot.hasItem()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack original = itemStack.copy(); + + if (index < topSize) { + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) { + return ItemStack.EMPTY; + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return original; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java new file mode 100644 index 00000000..0d0f2a1e --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java @@ -0,0 +1,262 @@ +package com.lishid.openinv.internal.v1_21_R2.container.menu; + +import com.google.common.base.Preconditions; +import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventorySelf; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly; +import com.lishid.openinv.util.Permissions; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.ChestMenu; +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.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenInventoryMenu extends OpenChestMenu { + + private int offset; + + public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) { + super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly); + } + + private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + int size = inventory.getContainerSize(); + // Disallow duplicate access to own main inventory contents. + if (inventory.getOwnerHandle().equals(viewer)) { + size -= viewer.getInventory().items.size(); + size = ((int) Math.ceil(size / 9.0)) * 9; + } + + return getChestMenuType(size); + } + + @Override + protected void preSlotSetup() { + offset = ownContainer ? viewer.getInventory().items.size() : 0; + } + + @Override + protected @NotNull Slot getUpperSlot(int index, int x, int y) { + index += offset; + Slot slot = container.getMenuSlot(index, x, y); + + // If the slot cannot be interacted with there's nothing to configure. + if (slot.getClass().equals(SlotViewOnly.class)) { + return slot; + } + + // Remove drop slot if viewer is not allowed to use it. + if (slot instanceof ContentDrop.SlotDrop + && (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) { + return new SlotViewOnly(container, index, x, y); + } + + if (slot instanceof ContentEquipment.SlotEquipment equipment) { + if (viewOnly) { + return SlotViewOnly.wrap(slot); + } + + Permissions perm = switch (equipment.getEquipmentSlot()) { + case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY; + case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY; + case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY; + case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY; + // Off-hand can hold anything, not just equipment. + default -> null; + }; + + // If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot. + if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) { + equipment.onlyEquipmentFor(container.getOwnerHandle()); + } + + // Equipment slots are a core part of the inventory, so they will always be shown. + return slot; + } + + // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). + if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) { + return new SlotViewOnly(container, index, x, y); + } + + if (viewOnly) { + return SlotViewOnly.wrap(slot); + } + + return slot; + } + + @Override + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { + org.bukkit.inventory.Inventory bukkitInventory; + if (viewOnly) { + bukkitInventory = new OpenDummyInventory(container); + } else if (ownContainer) { + bukkitInventory = new OpenPlayerInventorySelf(container, offset); + } else { + bukkitInventory = container.getBukkitInventory(); + } + + return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) { + @Override + public org.bukkit.inventory.ItemStack getItem(int index) { + if (viewOnly || index < 0) { + return null; + } + + Slot slot = slots.get(index); + return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY); + } + + @Override + public boolean isInTop(int rawSlot) { + return rawSlot < topSize; + } + + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } + if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) { + return null; + } + Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, + "Slot %s outside of inventory", rawSlot); + if (rawSlot > topSize) { + return getBottomInventory(); + } + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return null; + } + return getTopInventory(); + } + + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + if (rawSlot < 0) { + return rawSlot; + } + if (rawSlot < topSize) { + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return InventoryView.OUTSIDE; + } + return rawSlot; + } + + int slot = rawSlot - topSize; + + if (slot >= 27) { + slot -= 27; + } else { + slot += 9; + } + + return slot; + } + + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly || slot < 0) { + return InventoryType.SlotType.OUTSIDE; + } + if (slot >= topSize) { + slot -= topSize; + if (slot >= 27) { + return InventoryType.SlotType.QUICKBAR; + } + return InventoryType.SlotType.CONTAINER; + } + return OpenInventoryMenu.this.container.getSlotType(offset + slot); + } + + @Override + public int countSlots() { + return topSize + BOTTOM_INVENTORY_SIZE; + } + }; + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + if (viewOnly) { + return ItemStack.EMPTY; + } + + // See ChestMenu and InventoryMenu + Slot slot = this.slots.get(index); + + if (!slot.hasItem() || slot.isFake()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack originalStack = itemStack.copy(); + + if (index < topSize) { + // If we're moving top to bottom, do a normal transfer. + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else { + EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack); + boolean movedGear = switch (equipmentSlot) { + // If this is gear, try to move it to the correct slot first. + case OFFHAND, FEET, LEGS, CHEST, HEAD -> { + // Locate the correct slot in the contents following the main inventory. + for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { + Slot extraSlot = getSlot(extra); + if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot + && equipSlot.getEquipmentSlot() == equipmentSlot) { + // If we've found a matching slot, try to move to it. + // If this succeeds, even partially, we will not attempt to move to other slots. + // Otherwise, armor is already occupied, so we'll fall through to main inventory. + yield this.moveItemStackTo(itemStack, extra, extra + 1, false); + } + } + yield false; + } + // Non-gear gets no special treatment. + default -> false; + }; + + // If main inventory is not available, there's nowhere else to move. + if (offset != 0) { + if (!movedGear) { + return ItemStack.EMPTY; + } + } else { + // If we didn't move to a gear slot, try to move to a main inventory slot. + if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) { + return ItemStack.EMPTY; + } + } + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return originalStack; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java new file mode 100644 index 00000000..15c04fef --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java @@ -0,0 +1,69 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering. + */ +public interface Content { + + /** + * Update internal holder. + * + * @param holder the new holder + */ + void setHolder(@NotNull ServerPlayer holder); + + /** + * Get the current item. + * + * @return the current item + */ + ItemStack get(); + + /** + * Remove the current item. + * + * @return the current item + */ + ItemStack remove(); + + /** + * Remove some of the current item. + * + * @return the current item + */ + ItemStack removePartial(int amount); + + /** + * Set the current item. If slot is currently not usable, will drop item instead. + * + * @param itemStack the item to set + */ + void set(ItemStack itemStack); + + /** + * Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will + * impose any specific restrictions to insertion or removal. + * + * @param container the backing container + * @param slot the slot of the backing container represented + * @param x clientside x dimension from top left of inventory, not used + * @param y clientside y dimension from top left of inventory, not used + * @return a menu slot + */ + Slot asSlot(Container container, int slot, int x, int y); + + /** + * Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather + * than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}. + * + * @return the closes Bukkit slot type + */ + org.bukkit.event.inventory.InventoryType.SlotType getSlotType(); + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java new file mode 100644 index 00000000..fb71cf40 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java @@ -0,0 +1,131 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * A slot in a survival crafting inventory. Unavailable when not online in a survival mode. + */ +public class ContentCrafting implements Content { + + private final int index; + private ServerPlayer holder; + private List items; + + public ContentCrafting(@NotNull ServerPlayer holder, int index) { + setHolder(holder); + this.index = index; + } + + private boolean isAvailable() { + return isAvailable(holder); + } + + static boolean isAvailable(@NotNull ServerPlayer holder) { + // Player must be online and not in creative - since the creative client is (semi-)authoritative, + // it ignores changes without extra help, and will delete the item as a result. + // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. + return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + // Note: CraftingContainer#getItems is immutable! Be careful with updates. + this.items = holder.inventoryMenu.getCraftSlots().getContents(); + } + + @Override + public ItemStack get() { + return isAvailable() ? items.get(index) : ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + if (!this.isAvailable()) { + return ItemStack.EMPTY; + } + ItemStack removed = items.remove(index); + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); + return removed; + } + + @Override + public ItemStack removePartial(int amount) { + if (!this.isAvailable()) { + return ItemStack.EMPTY; + } + ItemStack removed = ContainerHelper.removeItem(items, index, amount); + if (removed.isEmpty()) { + return ItemStack.EMPTY; + } + holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); + return removed; + } + + @Override + public void set(ItemStack itemStack) { + if (isAvailable()) { + items.set(index, itemStack); + holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); + } else { + holder.drop(itemStack, false); + } + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotCrafting(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE; + } + + public class SlotCrafting extends SlotPlaceholder { + + private SlotCrafting(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder); + } + + @Override + public boolean mayPickup(Player player) { + return isAvailable(); + } + + @Override + public boolean mayPlace(ItemStack itemStack) { + return isAvailable(); + } + + @Override + public boolean hasItem() { + return isAvailable() && super.hasItem(); + } + + @Override + public boolean isFake() { + return !isAvailable(); + } + + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java new file mode 100644 index 00000000..62e9a62b --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java @@ -0,0 +1,48 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot allowing viewing of the crafting result. + * + *

Unmodifiable because I said so. Use your own crafting grid.

+ */ +public class ContentCraftingResult extends ContentViewOnly { + + public ContentCraftingResult(@NotNull ServerPlayer holder) { + super(holder); + } + + @Override + public ItemStack get() { + InventoryMenu inventoryMenu = holder.inventoryMenu; + return inventoryMenu.getResultSlot().getItem(); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + if (!ContentCrafting.isAvailable(holder)) { + return Placeholders.survivalOnly(holder); + } + InventoryMenu inventoryMenu = holder.inventoryMenu; + return inventoryMenu.getResultSlot().getItem(); + } + }; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.RESULT; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java new file mode 100644 index 00000000..41cbb3f7 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java @@ -0,0 +1,117 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode. + */ +public class ContentCursor implements Content { + + private @NotNull ServerPlayer holder; + + public ContentCursor(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + ItemStack carried = holder.containerMenu.getCarried(); + holder.containerMenu.setCarried(ItemStack.EMPTY); + return carried; + } + + @Override + public ItemStack removePartial(int amount) { + ItemStack carried = holder.containerMenu.getCarried(); + if (!carried.isEmpty() && carried.getCount() >= amount) { + ItemStack value = carried.split(amount); + if (carried.isEmpty()) { + holder.containerMenu.setCarried(ItemStack.EMPTY); + } + return value; + } + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + if (isAvailable()) { + holder.containerMenu.setCarried(itemStack); + } else { + holder.drop(itemStack, false); + } + } + + private boolean isAvailable() { + // Player must be online and not in creative - since the creative client is (semi-)authoritative, + // it ignores changes without extra help, and will delete the item as a result. + // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. + return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotCursor(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + // As close as possible to "not real" + return InventoryType.SlotType.OUTSIDE; + } + + public class SlotCursor extends SlotPlaceholder { + + private SlotCursor(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + if (!isAvailable()) { + return Placeholders.survivalOnly(holder); + } + ItemStack carried = holder.containerMenu.getCarried(); + return carried.isEmpty() ? Placeholders.cursor : carried; + } + + @Override + public boolean mayPickup(Player player) { + return isAvailable(); + } + + @Override + public boolean mayPlace(ItemStack itemStack) { + return isAvailable(); + } + + @Override + public boolean hasItem() { + return isAvailable() && super.hasItem(); + } + + @Override + public boolean isFake() { + return true; + } + + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java new file mode 100644 index 00000000..ebe52541 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java @@ -0,0 +1,88 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A fake slot used to drop items. Unavailable offline. + */ +public class ContentDrop implements Content { + + private ServerPlayer holder; + + public ContentDrop(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack removePartial(int amount) { + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + holder.drop(itemStack, true); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotDrop(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + // Behaves like dropping an item outside the screen, just by the target player. + return InventoryType.SlotType.OUTSIDE; + } + + public class SlotDrop extends SlotPlaceholder { + + private SlotDrop(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + return holder.connection != null && !holder.connection.isDisconnected() + ? Placeholders.drop + : Placeholders.blockedOffline; + } + + @Override + public boolean mayPlace(ItemStack itemStack) { + return holder.connection != null && !holder.connection.isDisconnected(); + } + + @Override + public boolean hasItem() { + return false; + } + + @Override + public boolean isFake() { + return true; + } + + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java new file mode 100644 index 00000000..def3448b --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java @@ -0,0 +1,78 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot for equipment that displays placeholders if empty. + */ +public class ContentEquipment extends ContentList { + + private final ItemStack placeholder; + private final EquipmentSlot equipmentSlot; + + public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { + super(holder, index, InventoryType.SlotType.ARMOR); + placeholder = switch (equipmentSlot) { + case HEAD -> Placeholders.emptyHelmet; + case CHEST -> Placeholders.emptyChestplate; + case LEGS -> Placeholders.emptyLeggings; + case FEET -> Placeholders.emptyBoots; + default -> Placeholders.emptyOffHand; + }; + this.equipmentSlot = equipmentSlot; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.items = holder.getInventory().armor; + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y); + } + + public class SlotEquipment extends SlotPlaceholder { + + private ServerPlayer viewer; + + SlotEquipment(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + ItemStack itemStack = getItem(); + if (!itemStack.isEmpty()) { + return itemStack; + } + return placeholder; + } + + public EquipmentSlot getEquipmentSlot() { + return equipmentSlot; + } + + public void onlyEquipmentFor(ServerPlayer viewer) { + this.viewer = viewer; + } + + @Override + public boolean mayPlace(ItemStack var0) { + if (viewer == null) { + return true; + } + + return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot; + } + + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java new file mode 100644 index 00000000..27490a50 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java @@ -0,0 +1,58 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; + +import java.util.List; + +/** + * A normal slot backed by an item list. + */ +public abstract class ContentList implements Content { + + private final int index; + private final InventoryType.SlotType slotType; + protected List items; + + public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) { + this.index = index; + this.slotType = slotType; + setHolder(holder); + } + + @Override + public ItemStack get() { + return items.get(index); + } + + @Override + public ItemStack remove() { + ItemStack removed = items.remove(index); + return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed; + } + + @Override + public ItemStack removePartial(int amount) { + return ContainerHelper.removeItem(items, index, amount); + } + + @Override + public void set(ItemStack itemStack) { + items.set(index, itemStack); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new Slot(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return slotType; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java new file mode 100644 index 00000000..b015bb8e --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java @@ -0,0 +1,46 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.Slot; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot for equipment that updates held items if necessary. + */ +public class ContentOffHand extends ContentEquipment { + + private ServerPlayer holder; + + public ContentOffHand(ServerPlayer holder, int localIndex) { + super(holder, localIndex, EquipmentSlot.OFFHAND); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.items = holder.getInventory().offhand; + this.holder = holder; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.QUICKBAR; + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y) { + @Override + public void setChanged() { + if (holder.connection != null + && !holder.connection.isDisconnected() + && holder.containerMenu != holder.inventoryMenu) { + holder.resendItemInHands(); + } + } + }; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java new file mode 100644 index 00000000..37213db0 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java @@ -0,0 +1,56 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A view-only slot that can't be interacted with. + */ +public class ContentViewOnly implements Content { + + @NotNull ServerPlayer holder; + + public ContentViewOnly(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.holder = holder; + } + + @Override + public ItemStack get() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack remove() { + return ItemStack.EMPTY; + } + + @Override + public ItemStack removePartial(int amount) { + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + this.holder.drop(itemStack, false); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.OUTSIDE; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java new file mode 100644 index 00000000..ef0bf7a0 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java @@ -0,0 +1,20 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +/** + * An implementation of a slot as used by a menu that may have fake placeholder items. + * + *

Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.

+ */ +public abstract class SlotPlaceholder extends Slot { + + public SlotPlaceholder(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + public abstract ItemStack getOrDefault(); + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java new file mode 100644 index 00000000..7727775d --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java @@ -0,0 +1,151 @@ +package com.lishid.openinv.internal.v1_21_R2.container.slot; + +import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content. + */ +public class SlotViewOnly extends SlotPlaceholder { + + public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) { + SlotViewOnly wrapper; + if (wrapped instanceof SlotPlaceholder placeholder) { + wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) { + @Override + public ItemStack getOrDefault() { + return placeholder.getOrDefault(); + } + }; + } else { + wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) { + @Override + public ItemStack getOrDefault() { + return wrapped.getItem(); + } + }; + } + wrapper.index = wrapped.index; + return wrapper; + } + + public SlotViewOnly(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + return Placeholders.notSlot; + } + + @Override + public void onQuickCraft(ItemStack var0, ItemStack var1) { + } + + @Override + public void onTake(Player var0, ItemStack var1) { + } + + @Override + public boolean mayPlace(ItemStack var0) { + return false; + } + + @Override + public ItemStack getItem() { + return ItemStack.EMPTY; + } + + @Override + public boolean hasItem() { + return false; + } + + @Override + public void setByPlayer(ItemStack newStack) { + } + + @Override + public void setByPlayer(ItemStack newStack, ItemStack oldStack) { + } + + @Override + public void set(ItemStack var0) { + } + + @Override + public void setChanged() { + } + + @Override + public int getMaxStackSize() { + return 0; + } + + @Override + public int getMaxStackSize(ItemStack itemStack) { + return 0; + } + + @Override + public ItemStack remove(int amount) { + return ItemStack.EMPTY; + } + + @Override + public boolean mayPickup(Player var0) { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public Optional tryRemove(int var0, int var1, Player var2) { + return Optional.empty(); + } + + @Override + public ItemStack safeTake(int var0, int var1, Player var2) { + return ItemStack.EMPTY; + } + + @Override + public ItemStack safeInsert(ItemStack itemStack) { + return itemStack; + } + + @Override + public ItemStack safeInsert(ItemStack itemStack, int amount) { + return itemStack; + } + + @Override + public boolean allowModification(Player var0) { + return false; + } + + @Override + public int getContainerSlot() { + return this.slot; + } + + @Override + public boolean isHighlightable() { + return false; + } + + @Override + public boolean isFake() { + return true; + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java new file mode 100644 index 00000000..4005359a --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java @@ -0,0 +1,178 @@ +package com.lishid.openinv.internal.v1_21_R2.player; + +import com.lishid.openinv.event.OpenEvents; +import com.mojang.logging.LogUtils; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.bukkit.craftbukkit.v1_21_R2.CraftServer; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +public class OpenPlayer extends CraftPlayer { + + /** + * List of tags to always reset when saving. + * + * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) + * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + */ + private static final Set RESET_TAGS = Set.of( + // Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + "raid_omen_position", + "ender_pearls", + // Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + "current_explosion_impact_pos", + // LivingEntity#addAdditionalSaveData(CompoundTag) + "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + + private final PlayerManager manager; + + OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { + super(server, entity); + this.manager = manager; + } + + @Override + public void loadData() { + manager.loadData(getHandle()); + } + + @Override + public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); + setExtraData(playerData); + + if (oldData != null) { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + } + + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, tempFile, backupFile); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); + } + } + + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + if (oldData == null) { + return new CompoundTag(); + } + + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @SuppressWarnings("SameParameterValue") @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @Nullable CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + if (container == null) { + return null; + } + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java new file mode 100644 index 00000000..2bc534ec --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java @@ -0,0 +1,267 @@ +package com.lishid.openinv.internal.v1_21_R2.player; + +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import com.mojang.authlib.GameProfile; +import com.mojang.serialization.Dynamic; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ParticleStatus; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.ChatVisiblity; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R2.CraftServer; +import org.bukkit.craftbukkit.v1_21_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.UUID; +import java.util.logging.Logger; + +public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { + + private static boolean paper; + + static { + try { + Class.forName("io.papermc.paper.configuration.Configuration"); + paper = true; + } catch (ClassNotFoundException ignored) { + paper = false; + } + } + + private final @NotNull Logger logger; + private @Nullable Field bukkitEntity; + + public PlayerManager(@NotNull Logger logger) { + this.logger = logger; + try { + bukkitEntity = Entity.class.getDeclaredField("bukkitEntity"); + } catch (NoSuchFieldException e) { + logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!"); + logger.log(java.util.logging.Level.WARNING, e.getMessage(), e); + bukkitEntity = null; + } + } + + public static @NotNull ServerPlayer getHandle(final Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + ServerPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName()); + } + + return nmsPlayer; + } + + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + ServerLevel worldServer = server.getLevel(Level.OVERWORLD); + + if (worldServer == null) { + return null; + } + + // Create a new ServerPlayer. + ServerPlayer entity = createNewPlayer(server, worldServer, offline); + + // Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak. + entity.getAdvancements().stopListening(); + + // Try to load the player's data. + if (loadData(entity)) { + // If data is loaded successfully, return the Bukkit entity. + return entity.getBukkitEntity(); + } + + return null; + } + + private @NotNull ServerPlayer createNewPlayer( + @NotNull MinecraftServer server, + @NotNull ServerLevel worldServer, + @NotNull final OfflinePlayer offline) { + // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) + // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + + ClientInformation dummyInfo = new ClientInformation( + "en_us", + 1, // Reduce distance just in case. + ChatVisiblity.HIDDEN, // Don't accept chat. + false, + ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, + ServerPlayer.DEFAULT_MAIN_HAND, + true, + false, // Don't list in player list (not that this player is in the list anyway). + ParticleStatus.MINIMAL + ); + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + } + + return entity; + } + + boolean loadData(@NotNull ServerPlayer player) { + // See CraftPlayer#loadData + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); + + if (loadedData == null) { + // Exceptions with loading are logged by Mojang. + return false; + } + + // Read basic data into the player. + player.load(loadedData); + // Also read "extra" data. + player.readAdditionalSaveData(loadedData); + // Game type settings are also loaded separately. + player.loadGameTypes(loadedData); + + if (paper) { + // Paper: world is not loaded by ServerPlayer#load(CompoundTag). + parseWorld(player, loadedData); + } + + return true; + } + + private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + // Modern Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(logger::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + return; + } + if (bukkitWorld == null) { + player.spawnIn(null); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); + } + + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); + } + + @Override + public @NotNull Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } + injectPlayer(nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + return player; + } + } + + @Override + public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) { + ServerPlayer player = getHandle(bukkitPlayer); + + if (player.connection == null) { + return null; + } + + // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) + AbstractContainerMenu menu; + Component title; + if (inventory instanceof OpenInventory playerInv) { + menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly); + title = playerInv.getTitle(player); + } else if (inventory instanceof OpenEnderChest enderChest) { + menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly); + title = enderChest.getTitle(); + } else { + return null; + } + + // Should never happen, player is a ServerPlayer with an active connection. + if (menu == null) { + return null; + } + + // Set up title. Title can only be set once for a menu, and is set during the open process. + // Further title changes are a hack where the client is sent a "new" inventory with the same ID, + // resulting in a title change but no other state modifications (like cursor position). + menu.setTitle(title); + + menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); + + // Menu is null if event is cancelled. + if (menu == null) { + return null; + } + + player.containerMenu = menu; + player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); + player.initMenu(menu); + + return menu.getBukkitView(); + } + +} diff --git a/plugin/pom.xml b/plugin/pom.xml index 472cb848..43e33471 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -46,19 +46,13 @@
com.lishid - openinvadapter1_21_R1 + openinvadapter1_21_R2 5.1.3-SNAPSHOT compile com.lishid - openinvadapter1_20_R4 - 5.1.3-SNAPSHOT - compile - - - com.lishid - openinvadapter1_20_R3 + openinvadapter1_21_R1 5.1.3-SNAPSHOT compile diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 45a2129f..3c5e59e1 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -16,8 +16,6 @@ package com.lishid.openinv; -import com.github.jikoo.planarwrappers.util.version.BukkitVersions; -import com.github.jikoo.planarwrappers.util.version.Version; import com.lishid.openinv.command.ContainerSettingCommand; import com.lishid.openinv.command.OpenInvCommand; import com.lishid.openinv.command.SearchContainerCommand; @@ -28,7 +26,6 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.listener.ContainerListener; -import com.lishid.openinv.listener.LegacyInventoryListener; import com.lishid.openinv.listener.ToggleListener; import com.lishid.openinv.util.AccessEqualMode; import com.lishid.openinv.util.InternalAccessor; @@ -144,10 +141,6 @@ public void onEnable() { private void registerEvents() { PluginManager pluginManager = this.getServer().getPluginManager(); - // Legacy: extra listener for permission handling and self-view issue prevention. - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { - pluginManager.registerEvents(new LegacyInventoryListener(this, config), this); - } pluginManager.registerEvents(playerLoader, this); pluginManager.registerEvents(inventoryManager, this); pluginManager.registerEvents(new ContainerListener(accessor, languageManager), this); diff --git a/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java b/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java deleted file mode 100644 index 38728dad..00000000 --- a/plugin/src/main/java/com/lishid/openinv/listener/LegacyInventoryListener.java +++ /dev/null @@ -1,234 +0,0 @@ -package com.lishid.openinv.listener; - -import com.google.errorprone.annotations.Keep; -import com.lishid.openinv.internal.ISpecialEnderChest; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.util.AccessEqualMode; -import com.lishid.openinv.util.InventoryAccess; -import com.lishid.openinv.util.Permissions; -import com.lishid.openinv.util.config.Config; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryInteractEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * A listener used to enable functionality and prevent issues on versions < 1.21. - */ -public class LegacyInventoryListener implements Listener { - - private final @NotNull Plugin plugin; - private final @NotNull Config config; - - public LegacyInventoryListener(@NotNull Plugin plugin, @NotNull Config config) { - this.plugin = plugin; - this.config = config; - } - - @Keep - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - private void onInventoryClick(@NotNull final InventoryClickEvent event) { - if (handleInventoryInteract(event)) { - return; - } - - // Safe cast - has to be a player to be the holder of a special player inventory. - Player player = (Player) event.getWhoClicked(); - - if (event.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY) { - return; - } - - // Extra handling for MOVE_TO_OTHER_INVENTORY - apparently Mojang no longer removes the item from the target - // inventory prior to adding it to existing stacks. - ItemStack currentItem = event.getCurrentItem(); - if (currentItem == null) { - // Other plugin doing some sort of handling (would be NOTHING for null item otherwise), ignore. - return; - } - - ItemStack clone = currentItem.clone(); - event.setCurrentItem(null); - - // Complete add action in same tick after event completion. - this.plugin.getServer().getScheduler().runTask(this.plugin, () -> player.getInventory().addItem(clone)); - } - - @Keep - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - private void onInventoryDrag(@NotNull final InventoryDragEvent event) { - if (handleInventoryInteract(event)) { - return; - } - - InventoryView view = event.getView(); - - if (view.getCursor() == null) { - return; - } - - int topSize = view.getTopInventory().getSize(); - - // Get bottom inventory active slots as player inventory slots. - Set slots = event.getRawSlots().stream() - .filter(slot -> slot >= topSize) - .map(slot -> convertToPlayerSlot(view, slot)).collect(Collectors.toSet()); - - int overlapLosses = 0; - - // Count overlapping slots. - for (Map.Entry newItem : event.getNewItems().entrySet()) { - int rawSlot = newItem.getKey(); - - // Skip bottom inventory slots. - if (rawSlot >= topSize) { - continue; - } - - int convertedSlot = convertToPlayerSlot(view, rawSlot); - - if (slots.contains(convertedSlot)) { - overlapLosses += getCountDiff(view.getItem(rawSlot), newItem.getValue()); - } - } - - // Allow no overlap to proceed as usual. - if (overlapLosses < 1) { - return; - } - - final ItemStack lost = view.getCursor().clone(); - lost.setAmount(overlapLosses); - - // Re-add the lost items in the same tick after the event has completed. - plugin.getServer().getScheduler().runTask(plugin, () -> { - InventoryView currentOpen = event.getWhoClicked().getOpenInventory(); - - if (!currentOpen.equals(view)) { - event.getWhoClicked().getWorld().dropItem(event.getWhoClicked().getLocation(), lost).setPickupDelay(0); - return; - } - - ItemStack cursor = currentOpen.getCursor(); - - if (cursor == null) { - currentOpen.setCursor(lost); - } else if (lost.isSimilar(cursor)) { - cursor.setAmount(cursor.getAmount() + lost.getAmount()); - currentOpen.setCursor(cursor); - } else { - event.getWhoClicked().getWorld().dropItem(event.getWhoClicked().getLocation(), lost).setPickupDelay(0); - } - }); - } - - private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result) { - if (original == null || original.getType() != result.getType()) { - return result.getAmount(); - } - - return result.getAmount() - original.getAmount(); - } - - /** - * Handle common InventoryInteractEvent functions. - * - * @param event the InventoryInteractEvent - * @return true unless the top inventory is the holder's own inventory - */ - private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) { - HumanEntity viewer = event.getWhoClicked(); - - Inventory inventory = event.getView().getTopInventory(); - ISpecialInventory backing = InventoryAccess.getInventory(inventory); - - if (backing == null) { - return true; - } - - Permissions edit; - HumanEntity target = backing.getPlayer(); - boolean ownContainer = viewer.equals(target); - if (backing instanceof ISpecialPlayerInventory) { - edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; - } else if (backing instanceof ISpecialEnderChest) { - edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER; - } else { - // Unknown implementation. - return true; - } - - if (!edit.hasPermission(viewer)) { - event.setCancelled(true); - return true; - } - - // If access ties aren't view-only mode, don't bother with permission checks. - if (config.getAccessEqualMode() != AccessEqualMode.VIEW) { - return !ownContainer || !(backing instanceof ISpecialPlayerInventory); - } - - for (int level = 4; level > 0; --level) { - String permission = "openinv.access.level." + level; - // If the target doesn't have this access level... - if (!target.hasPermission(permission)) { - // If the viewer does have the access level, all good. - if (viewer.hasPermission(permission)) { - break; - } - // Otherwise check next access level. - continue; - } - - // Either the viewer lacks access (which shouldn't be possible) or this is a tie. View-only. - event.setCancelled(true); - return true; - } - - return !ownContainer || !(backing instanceof ISpecialPlayerInventory); - } - - private static int convertToPlayerSlot(InventoryView view, int rawSlot) { - int topSize = view.getTopInventory().getSize(); - if (topSize <= rawSlot) { - // Slot is not inside special inventory, use Bukkit logic. - return view.convertSlot(rawSlot); - } - - // Main inventory, slots 0-26 -> 9-35 - if (rawSlot < 27) { - return rawSlot + 9; - } - // Hotbar, slots 27-35 -> 0-8 - if (rawSlot < 36) { - return rawSlot - 27; - } - // Armor, slots 36-39 -> 39-36 - if (rawSlot < 40) { - return 36 + (39 - rawSlot); - } - // Off-hand - if (rawSlot == 40) { - return 40; - } - // Drop slots, "out of inventory" - return -1; - } - -} diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 114399ab..4214eb7b 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -40,13 +40,10 @@ public class InternalAccessor { public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { try { - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 1)) - || BukkitVersions.MINECRAFT.equals(Version.of(1, 21))) { + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 3))) { + internal = new com.lishid.openinv.internal.v1_21_R2.InternalAccessor(logger, lang); + } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 1))) { internal = new com.lishid.openinv.internal.v1_21_R1.InternalAccessor(logger, lang); - } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) { - internal = new com.lishid.openinv.internal.v1_20_R3.InternalAccessor(logger, lang); - } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 6))) { - internal = new com.lishid.openinv.internal.v1_20_R4.InternalAccessor(logger, lang); } if (internal != null) { InventoryAccess.setProvider(internal::get); @@ -121,6 +118,12 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3 return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 + return "Unsupported; upgrade to 1.20.6: https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21 + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; + } return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/pom.xml b/pom.xml index feb14671..7964b278 100644 --- a/pom.xml +++ b/pom.xml @@ -43,9 +43,8 @@ api addon/togglepersist common + internal/v1_21_R2 internal/v1_21_R1 - internal/v1_20_R4 - internal/v1_20_R3 plugin
@@ -78,13 +77,13 @@ annotations org.jetbrains provided - 25.0.0 + 26.0.1
spigot-api org.spigotmc provided - 1.20.4-R0.1-SNAPSHOT + 1.21.1-R0.1-SNAPSHOT openinvapi From df913e33c313974b464a9a80fd35b4db01ec9bb3 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 29 Oct 2024 10:52:51 -0400 Subject: [PATCH 230/340] Bump version to 5.1.3 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index 56dafff6..f9d528b6 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.3-SNAPSHOT + 5.1.3 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 59cd0dbf..7d67debf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.3-SNAPSHOT + 5.1.3 openinvapi diff --git a/common/pom.xml b/common/pom.xml index e199a4d2..eba299a3 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.3-SNAPSHOT + 5.1.3 openinvcommon diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index 6374ba0c..f4b98f12 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.3-SNAPSHOT + 5.1.3 openinvadapter1_21_R1 diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index 024f2ab8..0c21b211 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.3-SNAPSHOT + 5.1.3 openinvadapter1_21_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index 43e33471..b7f71e91 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.3-SNAPSHOT + 5.1.3 openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R2 - 5.1.3-SNAPSHOT + 5.1.3 compile com.lishid openinvadapter1_21_R1 - 5.1.3-SNAPSHOT + 5.1.3 compile diff --git a/pom.xml b/pom.xml index 7964b278..e8e3b81e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.3-SNAPSHOT + 5.1.3 pom From 6df4de1dd38b332196dd4ff529d64825fca6d82e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 29 Oct 2024 10:53:32 -0400 Subject: [PATCH 231/340] Bump version to 5.1.4-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R1/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index f9d528b6..b8b403a7 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.3 + 5.1.4-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 7d67debf..d9eb974a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.3 + 5.1.4-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index eba299a3..ad5b590c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.3 + 5.1.4-SNAPSHOT openinvcommon diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R1/pom.xml index f4b98f12..8fcd01be 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R1/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.3 + 5.1.4-SNAPSHOT openinvadapter1_21_R1 diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index 0c21b211..0192fc36 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.3 + 5.1.4-SNAPSHOT openinvadapter1_21_R2 diff --git a/plugin/pom.xml b/plugin/pom.xml index b7f71e91..44b31b17 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.3 + 5.1.4-SNAPSHOT openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R2 - 5.1.3 + 5.1.4-SNAPSHOT compile com.lishid openinvadapter1_21_R1 - 5.1.3 + 5.1.4-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index e8e3b81e..99fcc749 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.3 + 5.1.4-SNAPSHOT pom From 532dffc068bc5f61a49bc479b3f8e9a44575f45e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:17:31 +0000 Subject: [PATCH 232/340] Bump org.apache.maven.plugins:maven-dependency-plugin (#255) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 99fcc749..1c885b86 100644 --- a/pom.xml +++ b/pom.xml @@ -116,7 +116,7 @@ --> maven-dependency-plugin org.apache.maven.plugins - 3.8.0 + 3.8.1
From cd7219f4720681987cca1514f0eaaaff4134668b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:18:22 +0000 Subject: [PATCH 233/340] Bump com.google.errorprone:error_prone_core from 2.33.0 to 2.35.1 (#256) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1c885b86..c84ad598 100644 --- a/pom.xml +++ b/pom.xml @@ -151,7 +151,7 @@ com.google.errorprone error_prone_core - 2.33.0 + 2.35.1 From 08d4a0ea27c76519019dc95fdfcb7b44823bd3a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 18:38:06 -0400 Subject: [PATCH 234/340] Bump softprops/action-gh-release from 2.0.8 to 2.0.9 (#257) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.8 to 2.0.9. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.8...v2.0.9) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 3dca4b01..85e98d58 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -34,7 +34,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.0.8 + uses: softprops/action-gh-release@v2.0.9 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 03ea798ee071b5222b51b445910422ba9a41ef99 Mon Sep 17 00:00:00 2001 From: Christoffer Tibell Date: Fri, 1 Nov 2024 23:39:01 +0100 Subject: [PATCH 235/340] Add support for 1.21.3 to legibility pack (#258) --- resource-pack/pack.mcmeta | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resource-pack/pack.mcmeta b/resource-pack/pack.mcmeta index 51a24480..1f4db36b 100644 --- a/resource-pack/pack.mcmeta +++ b/resource-pack/pack.mcmeta @@ -1,6 +1,7 @@ { "pack": { "description": "Improve OpenInv's legibility", - "pack_format": 34 + "pack_format": 42, + "supported_formats": [34, 42] } } From 8d897664effb819deea4bd43b1d8b05804878194 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Dec 2024 12:27:53 -0500 Subject: [PATCH 236/340] Bump softprops/action-gh-release from 2.0.9 to 2.1.0 (#259) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.9 to 2.1.0. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.9...v2.1.0) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 85e98d58..c50ddd64 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -34,7 +34,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.0.9 + uses: softprops/action-gh-release@v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 73a9c743a624b98bc5f5787417fa916242ac61f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 05:10:31 +0000 Subject: [PATCH 237/340] Bump com.google.errorprone:error_prone_core from 2.35.1 to 2.36.0 (#260) * Bump com.google.errorprone:error_prone_core from 2.35.1 to 2.36.0 Bumps [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) from 2.35.1 to 2.36.0. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.35.1...v2.36.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Add new required arg --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jikoo --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c84ad598..0fbe402f 100644 --- a/pom.xml +++ b/pom.xml @@ -151,11 +151,12 @@ com.google.errorprone error_prone_core - 2.35.1 + 2.36.0 -XDcompilePolicy=simple + --should-stop=ifError=FLOW -Xplugin:ErrorProne -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED From 4e89a04b36e43a4edddd8bb8d15b877fc6e7e78d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 2 Dec 2024 00:26:07 -0500 Subject: [PATCH 238/340] Correct header --- .../com/lishid/openinv/util/StringMetric.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java b/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java index 8a2dae37..b1afaed2 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java +++ b/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java @@ -1,17 +1,22 @@ /* - * Copyright (C) 2011-2021 lishid. All rights reserved. + * This file is an amalgamation of code from the Simmetrics authors. + * The originals may be found here: + * https://github.com/Simmetrics/simmetrics/blob/master/simmetrics-core/src/main/java/org/simmetrics/metrics/JaroWinkler.java + * https://github.com/Simmetrics/simmetrics/blob/master/simmetrics-core/src/main/java/org/simmetrics/metrics/Jaro.java * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. + * Copyright (C) 2014 - 2016 Simmetrics Authors * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.lishid.openinv.util; From 5ac09bc5a3916402c4b124d02d94c3a4fa2941f5 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 8 Dec 2024 20:07:34 -0500 Subject: [PATCH 239/340] Add support for 1.21.4 (#262) * Add 1.21.4 adapter * Update resource pack to 1.21.4 --- internal/{v1_21_R1 => v1_21_R3}/pom.xml | 6 +- .../internal/v1_21_R3}/InternalAccessor.java | 14 +- .../container/AnySilentContainer.java | 6 +- .../v1_21_R3}/container/OpenEnderChest.java | 14 +- .../v1_21_R3}/container/OpenInventory.java | 32 ++-- .../v1_21_R3}/container/Placeholders.java | 26 +-- .../container/bukkit/OpenDummyInventory.java | 4 +- .../container/bukkit/OpenPlayerInventory.java | 8 +- .../bukkit/OpenPlayerInventorySelf.java | 4 +- .../container/menu/OpenChestMenu.java | 10 +- .../container/menu/OpenEnderChestMenu.java | 4 +- .../container/menu/OpenInventoryMenu.java | 18 +- .../v1_21_R3}/container/slot/Content.java | 2 +- .../container/slot/ContentCrafting.java | 4 +- .../container/slot/ContentCraftingResult.java | 8 +- .../container/slot/ContentCursor.java | 4 +- .../v1_21_R3}/container/slot/ContentDrop.java | 4 +- .../container/slot/ContentEquipment.java | 4 +- .../v1_21_R3}/container/slot/ContentList.java | 2 +- .../container/slot/ContentOffHand.java | 2 +- .../container/slot/ContentViewOnly.java | 2 +- .../container/slot/SlotPlaceholder.java | 2 +- .../container/slot/SlotViewOnly.java | 4 +- .../internal/v1_21_R3}/player/OpenPlayer.java | 7 +- .../v1_21_R3}/player/PlayerManager.java | 18 +- plugin/pom.xml | 4 +- .../lishid/openinv/util/InternalAccessor.java | 9 +- pom.xml | 2 +- .../minecraft/items/crafting_table.json | 19 ++ .../assets/minecraft/items/dropper.json | 19 ++ .../assets/minecraft/items/leather_boots.json | 174 ++++++++++++++++++ .../minecraft/items/leather_chestplate.json | 174 ++++++++++++++++++ .../minecraft/items/leather_helmet.json | 174 ++++++++++++++++++ .../minecraft/items/leather_leggings.json | 174 ++++++++++++++++++ .../assets/minecraft/items/shield.json | 33 ++++ .../assets/minecraft/items/white_banner.json | 23 +++ .../items/white_stained_glass_pane.json | 19 ++ .../minecraft/models/item/crafting_table.json | 11 -- .../assets/minecraft/models/item/dropper.json | 11 -- .../minecraft/models/item/leather_boots.json | 75 -------- .../models/item/leather_chestplate.json | 75 -------- .../minecraft/models/item/leather_helmet.json | 75 -------- .../models/item/leather_leggings.json | 75 -------- .../assets/minecraft/models/item/shield.json | 58 ------ .../minecraft/models/item/white_banner.json | 11 -- .../models/item/white_stained_glass_pane.json | 14 -- .../openinv/models/item/crafting_output.json | 3 +- .../openinv/models/item/empty_boots.json | 2 +- .../openinv/models/item/empty_chestplate.json | 2 +- .../openinv/models/item/empty_helmet.json | 2 +- .../openinv/models/item/empty_leggings.json | 2 +- .../openinv/models/item/empty_shield.json | 2 +- .../openinv/textures/item/empty_boots.png | Bin 0 -> 111 bytes .../textures/item/empty_chestplate.png | Bin 0 -> 112 bytes .../openinv/textures/item/empty_helmet.png | Bin 0 -> 110 bytes .../openinv/textures/item/empty_leggings.png | Bin 0 -> 102 bytes .../openinv/textures/item/empty_shield.png | Bin 0 -> 169 bytes resource-pack/pack.mcmeta | 4 +- 58 files changed, 933 insertions(+), 522 deletions(-) rename internal/{v1_21_R1 => v1_21_R3}/pom.xml (94%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/InternalAccessor.java (84%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/AnySilentContainer.java (98%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/OpenEnderChest.java (91%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/OpenInventory.java (91%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/Placeholders.java (92%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/bukkit/OpenDummyInventory.java (96%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/bukkit/OpenPlayerInventory.java (96%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/bukkit/OpenPlayerInventorySelf.java (80%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/menu/OpenChestMenu.java (97%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/menu/OpenEnderChestMenu.java (91%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/menu/OpenInventoryMenu.java (93%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/Content.java (96%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentCrafting.java (96%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentCraftingResult.java (80%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentCursor.java (96%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentDrop.java (93%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentEquipment.java (94%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentList.java (95%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentOffHand.java (95%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/ContentViewOnly.java (95%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/SlotPlaceholder.java (89%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/container/slot/SlotViewOnly.java (95%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/player/OpenPlayer.java (96%) rename internal/{v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1 => v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3}/player/PlayerManager.java (93%) create mode 100644 resource-pack/assets/minecraft/items/crafting_table.json create mode 100644 resource-pack/assets/minecraft/items/dropper.json create mode 100644 resource-pack/assets/minecraft/items/leather_boots.json create mode 100644 resource-pack/assets/minecraft/items/leather_chestplate.json create mode 100644 resource-pack/assets/minecraft/items/leather_helmet.json create mode 100644 resource-pack/assets/minecraft/items/leather_leggings.json create mode 100644 resource-pack/assets/minecraft/items/shield.json create mode 100644 resource-pack/assets/minecraft/items/white_banner.json create mode 100644 resource-pack/assets/minecraft/items/white_stained_glass_pane.json delete mode 100644 resource-pack/assets/minecraft/models/item/crafting_table.json delete mode 100644 resource-pack/assets/minecraft/models/item/dropper.json delete mode 100644 resource-pack/assets/minecraft/models/item/leather_boots.json delete mode 100644 resource-pack/assets/minecraft/models/item/leather_chestplate.json delete mode 100644 resource-pack/assets/minecraft/models/item/leather_helmet.json delete mode 100644 resource-pack/assets/minecraft/models/item/leather_leggings.json delete mode 100644 resource-pack/assets/minecraft/models/item/shield.json delete mode 100644 resource-pack/assets/minecraft/models/item/white_banner.json delete mode 100644 resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json create mode 100644 resource-pack/assets/openinv/textures/item/empty_boots.png create mode 100644 resource-pack/assets/openinv/textures/item/empty_chestplate.png create mode 100644 resource-pack/assets/openinv/textures/item/empty_helmet.png create mode 100644 resource-pack/assets/openinv/textures/item/empty_leggings.png create mode 100644 resource-pack/assets/openinv/textures/item/empty_shield.png diff --git a/internal/v1_21_R1/pom.xml b/internal/v1_21_R3/pom.xml similarity index 94% rename from internal/v1_21_R1/pom.xml rename to internal/v1_21_R3/pom.xml index 8fcd01be..0a79bfb2 100644 --- a/internal/v1_21_R1/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -26,13 +26,13 @@ 5.1.4-SNAPSHOT - openinvadapter1_21_R1 - OpenInvAdapter1_21_R1 + openinvadapter1_21_R3 + OpenInvAdapter1_21_R3 21 21 - 1.21.1-R0.1-SNAPSHOT + 1.21.4-R0.1-SNAPSHOT diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/InternalAccessor.java similarity index 84% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/InternalAccessor.java index 94588f9d..4b22c13c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/InternalAccessor.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/InternalAccessor.java @@ -1,19 +1,19 @@ -package com.lishid.openinv.internal.v1_21_R1; +package com.lishid.openinv.internal.v1_21_R3; import com.lishid.openinv.internal.Accessor; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.v1_21_R1.container.AnySilentContainer; -import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; -import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; +import com.lishid.openinv.internal.v1_21_R3.container.AnySilentContainer; +import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java similarity index 98% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java index f23f1edc..c2724bad 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java @@ -14,11 +14,11 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R3.container; import com.lishid.openinv.internal.AnySilentContainerBase; -import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenChestMenu; -import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; +import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenChestMenu; +import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenEnderChest.java similarity index 91% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenEnderChest.java index 3d495735..eee8f317 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenEnderChest.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenEnderChest.java @@ -1,22 +1,22 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R3.container; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenEnderChestMenu; -import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; +import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenEnderChestMenu; +import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.entity.player.StackedItemContents; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.StackedContentsCompatible; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -174,7 +174,7 @@ public void clearContent() { } @Override - public void fillStackedContents(StackedContents stackedContents) { + public void fillStackedContents(StackedItemContents stackedContents) { for (ItemStack itemstack : items) { stackedContents.accountStack(itemstack); } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenInventory.java similarity index 91% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenInventory.java index 748038a6..8f227240 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/OpenInventory.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenInventory.java @@ -1,20 +1,20 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R3.container; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventory; -import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenInventoryMenu; -import com.lishid.openinv.internal.v1_21_R1.container.slot.Content; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCrafting; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCraftingResult; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCursor; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentList; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentOffHand; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentViewOnly; -import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; -import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; +import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventory; +import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenInventoryMenu; +import com.lishid.openinv.internal.v1_21_R3.container.slot.Content; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCrafting; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCraftingResult; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCursor; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentList; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentOffHand; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -29,8 +29,8 @@ import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/Placeholders.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/Placeholders.java similarity index 92% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/Placeholders.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/Placeholders.java index c5f35d2d..7a95351c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/Placeholders.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/Placeholders.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.container; +package com.lishid.openinv.internal.v1_21_R3.container; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; @@ -20,7 +20,7 @@ import net.minecraft.world.level.block.entity.BannerPatternLayers; import net.minecraft.world.level.block.entity.BannerPatterns; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry; +import org.bukkit.craftbukkit.v1_21_R3.CraftRegistry; import org.jetbrains.annotations.NotNull; import java.util.EnumMap; @@ -29,7 +29,7 @@ public final class Placeholders { - private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); + private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of()); public static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); public static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); public static @NotNull ItemStack cursor = defaultCursor(); @@ -103,10 +103,10 @@ private static ItemStack defaultCursor() { // Cursor-like banner with no tooltip ItemStack itemStack = new ItemStack(Items.WHITE_BANNER); RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); - Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); - BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT); - BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT); - BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER); + Registry bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value(); + BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value(); + BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value(); itemStack.set(DataComponents.BANNER_PATTERNS, new BannerPatternLayers(List.of( new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), @@ -140,12 +140,12 @@ private static ItemStack getEmptyShield() { ItemStack itemStack = new ItemStack(Items.SHIELD); itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); - Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); - BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL); - BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT); - BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT); - BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT); - BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT); + Registry bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value(); + BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value(); + BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value(); + BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value(); + BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value(); itemStack.set(DataComponents.BANNER_PATTERNS, new BannerPatternLayers(List.of( new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenDummyInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenDummyInventory.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java index c0eb1a38..0a140ae0 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenDummyInventory.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java @@ -1,9 +1,9 @@ -package com.lishid.openinv.internal.v1_21_R1.container.bukkit; +package com.lishid.openinv.internal.v1_21_R3.container.bukkit; import com.lishid.openinv.internal.ViewOnly; import net.minecraft.world.Container; import org.bukkit.Material; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventory.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java index de323f5a..7d612d77 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventory.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java @@ -1,12 +1,12 @@ -package com.lishid.openinv.internal.v1_21_R1.container.bukkit; +package com.lishid.openinv.internal.v1_21_R3.container.bukkit; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventorySelf.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java similarity index 80% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventorySelf.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java index f9826ed1..a26e75c8 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/bukkit/OpenPlayerInventorySelf.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.bukkit; +package com.lishid.openinv.internal.v1_21_R3.container.bukkit; -import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java similarity index 97% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java index 512ab769..f6557c59 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java @@ -1,11 +1,11 @@ -package com.lishid.openinv.internal.v1_21_R1.container.menu; +package com.lishid.openinv.internal.v1_21_R3.container.menu; import com.google.common.base.Suppliers; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenDummyInventory; -import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotPlaceholder; -import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotPlaceholder; +import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.server.level.ServerPlayer; @@ -21,7 +21,7 @@ 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.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenEnderChestMenu.java similarity index 91% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenEnderChestMenu.java index 172f42ea..075c0849 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenEnderChestMenu.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenEnderChestMenu.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.menu; +package com.lishid.openinv.internal.v1_21_R3.container.menu; -import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java index f0c75a6f..91732433 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java @@ -1,12 +1,12 @@ -package com.lishid.openinv.internal.v1_21_R1.container.menu; +package com.lishid.openinv.internal.v1_21_R3.container.menu; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenDummyInventory; -import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventorySelf; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventorySelf; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop; +import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment; +import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly; import com.lishid.openinv.util.Permissions; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EquipmentSlot; @@ -15,8 +15,8 @@ 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.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/Content.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/Content.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java index d63536f0..fb290de4 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/Content.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCrafting.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCrafting.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java index 3f2bdb98..0479ea8c 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCrafting.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCraftingResult.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java similarity index 80% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCraftingResult.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java index 54fb0cdc..1fceaca0 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCraftingResult.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.InventoryMenu; @@ -23,7 +23,7 @@ public ContentCraftingResult(@NotNull ServerPlayer holder) { @Override public ItemStack get() { InventoryMenu inventoryMenu = holder.inventoryMenu; - return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); + return inventoryMenu.getResultSlot().getItem(); } @Override @@ -35,7 +35,7 @@ public ItemStack getOrDefault() { return Placeholders.survivalOnly(holder); } InventoryMenu inventoryMenu = holder.inventoryMenu; - return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); + return inventoryMenu.getResultSlot().getItem(); } }; } diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCursor.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCursor.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java index 5401daca..46ea737e 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentCursor.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentDrop.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentDrop.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java index 9a083b33..5e1c5168 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentDrop.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentEquipment.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentEquipment.java similarity index 94% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentEquipment.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentEquipment.java index 662d4d67..9fe03914 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentEquipment.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentEquipment.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentList.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentList.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentList.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentList.java index 48e14ced..e9483b95 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentList.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentList.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentOffHand.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentOffHand.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java index 7b319041..df66fec8 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentOffHand.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentViewOnly.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentViewOnly.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentViewOnly.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentViewOnly.java index 5dec5b32..6d78e6e0 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/ContentViewOnly.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentViewOnly.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotPlaceholder.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java similarity index 89% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotPlaceholder.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java index c7a53a3a..7ec22a84 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotPlaceholder.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotViewOnly.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java similarity index 95% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotViewOnly.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java index 93975d51..76981cff 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/slot/SlotViewOnly.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R1.container.slot; +package com.lishid.openinv.internal.v1_21_R3.container.slot; -import com.lishid.openinv.internal.v1_21_R1.container.Placeholders; +import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java similarity index 96% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java index c90d4e1e..4fd1cb72 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/OpenPlayer.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R1.player; +package com.lishid.openinv.internal.v1_21_R3.player; import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; @@ -9,8 +9,8 @@ import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_21_R1.CraftServer; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R3.CraftServer; +import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,6 +51,7 @@ public class OpenPlayer extends CraftPlayer { "SpawnAngle", "SpawnDimension", "raid_omen_position", + "ender_pearls", // Player#addAdditionalSaveData(CompoundTag) "ShoulderEntityLeft", "ShoulderEntityRight", diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/PlayerManager.java similarity index 93% rename from internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java rename to internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/PlayerManager.java index 1eae5f04..ef44fbd1 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/player/PlayerManager.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/PlayerManager.java @@ -1,8 +1,8 @@ -package com.lishid.openinv.internal.v1_21_R1.player; +package com.lishid.openinv.internal.v1_21_R3.player; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory; +import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest; +import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -11,6 +11,7 @@ import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ParticleStatus; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -22,10 +23,10 @@ import org.bukkit.OfflinePlayer; import org.bukkit.Server; import org.bukkit.World; -import org.bukkit.craftbukkit.v1_21_R1.CraftServer; -import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R3.CraftServer; +import org.bukkit.craftbukkit.v1_21_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R3.event.CraftEventFactory; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -123,7 +124,8 @@ public PlayerManager(@NotNull Logger logger) { ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, ServerPlayer.DEFAULT_MAIN_HAND, true, - false // Don't list in player list (not that this player is in the list anyway). + false, // Don't list in player list (not that this player is in the list anyway). + ParticleStatus.MINIMAL ); ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); diff --git a/plugin/pom.xml b/plugin/pom.xml index 44b31b17..e5a3ce62 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -46,13 +46,13 @@ com.lishid - openinvadapter1_21_R2 + openinvadapter1_21_R3 5.1.4-SNAPSHOT compile com.lishid - openinvadapter1_21_R1 + openinvadapter1_21_R2 5.1.4-SNAPSHOT compile diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 4214eb7b..a29b0f13 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -40,10 +40,10 @@ public class InternalAccessor { public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { try { - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 3))) { + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) { + internal = new com.lishid.openinv.internal.v1_21_R3.InternalAccessor(logger, lang); + } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 3))) { internal = new com.lishid.openinv.internal.v1_21_R2.InternalAccessor(logger, lang); - } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 1))) { - internal = new com.lishid.openinv.internal.v1_21_R1.InternalAccessor(logger, lang); } if (internal != null) { InventoryAccess.setProvider(internal::get); @@ -124,6 +124,9 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21 return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3"; + } return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/pom.xml b/pom.xml index 0fbe402f..2869f088 100644 --- a/pom.xml +++ b/pom.xml @@ -43,8 +43,8 @@ api addon/togglepersist common + internal/v1_21_R3 internal/v1_21_R2 - internal/v1_21_R1 plugin diff --git a/resource-pack/assets/minecraft/items/crafting_table.json b/resource-pack/assets/minecraft/items/crafting_table.json new file mode 100644 index 00000000..c77347e5 --- /dev/null +++ b/resource-pack/assets/minecraft/items/crafting_table.json @@ -0,0 +1,19 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/crafting_output" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:model", + "model": "minecraft:block/crafting_table" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/dropper.json b/resource-pack/assets/minecraft/items/dropper.json new file mode 100644 index 00000000..8493cfa9 --- /dev/null +++ b/resource-pack/assets/minecraft/items/dropper.json @@ -0,0 +1,19 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/drop" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:model", + "model": "minecraft:block/dropper" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/leather_boots.json b/resource-pack/assets/minecraft/items/leather_boots.json new file mode 100644 index 00000000..c6859113 --- /dev/null +++ b/resource-pack/assets/minecraft/items/leather_boots.json @@ -0,0 +1,174 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/empty_boots" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_quartz_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:quartz" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_iron_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:iron" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_netherite_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:netherite" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_redstone_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:redstone" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_copper_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:copper" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_gold_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:gold" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_emerald_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:emerald" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_diamond_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:diamond" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_lapis_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:lapis" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_amethyst_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:amethyst" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots_resin_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:resin" + } + ], + "fallback": { + "type": "minecraft:model", + "model": "minecraft:item/leather_boots", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "property": "minecraft:trim_material" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/leather_chestplate.json b/resource-pack/assets/minecraft/items/leather_chestplate.json new file mode 100644 index 00000000..10e51d04 --- /dev/null +++ b/resource-pack/assets/minecraft/items/leather_chestplate.json @@ -0,0 +1,174 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/empty_chestplate" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_quartz_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:quartz" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_iron_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:iron" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_netherite_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:netherite" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_redstone_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:redstone" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_copper_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:copper" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_gold_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:gold" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_emerald_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:emerald" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_diamond_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:diamond" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_lapis_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:lapis" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_amethyst_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:amethyst" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate_resin_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:resin" + } + ], + "fallback": { + "type": "minecraft:model", + "model": "minecraft:item/leather_chestplate", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "property": "minecraft:trim_material" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/leather_helmet.json b/resource-pack/assets/minecraft/items/leather_helmet.json new file mode 100644 index 00000000..27a56391 --- /dev/null +++ b/resource-pack/assets/minecraft/items/leather_helmet.json @@ -0,0 +1,174 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/empty_helmet" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_quartz_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:quartz" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_iron_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:iron" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_netherite_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:netherite" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_redstone_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:redstone" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_copper_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:copper" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_gold_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:gold" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_emerald_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:emerald" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_diamond_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:diamond" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_lapis_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:lapis" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_amethyst_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:amethyst" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet_resin_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:resin" + } + ], + "fallback": { + "type": "minecraft:model", + "model": "minecraft:item/leather_helmet", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "property": "minecraft:trim_material" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/leather_leggings.json b/resource-pack/assets/minecraft/items/leather_leggings.json new file mode 100644 index 00000000..4686e060 --- /dev/null +++ b/resource-pack/assets/minecraft/items/leather_leggings.json @@ -0,0 +1,174 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/empty_leggings" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_quartz_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:quartz" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_iron_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:iron" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_netherite_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:netherite" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_redstone_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:redstone" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_copper_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:copper" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_gold_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:gold" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_emerald_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:emerald" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_diamond_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:diamond" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_lapis_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:lapis" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_amethyst_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:amethyst" + }, + { + "model": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings_resin_trim", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "when": "minecraft:resin" + } + ], + "fallback": { + "type": "minecraft:model", + "model": "minecraft:item/leather_leggings", + "tints": [ + { + "type": "minecraft:dye", + "default": -6265536 + } + ] + }, + "property": "minecraft:trim_material" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/shield.json b/resource-pack/assets/minecraft/items/shield.json new file mode 100644 index 00000000..cfac9c50 --- /dev/null +++ b/resource-pack/assets/minecraft/items/shield.json @@ -0,0 +1,33 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/empty_shield" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:condition", + "on_false": { + "type": "minecraft:special", + "base": "minecraft:item/shield", + "model": { + "type": "minecraft:shield" + } + }, + "on_true": { + "type": "minecraft:special", + "base": "minecraft:item/shield_blocking", + "model": { + "type": "minecraft:shield" + } + }, + "property": "minecraft:using_item" + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/white_banner.json b/resource-pack/assets/minecraft/items/white_banner.json new file mode 100644 index 00000000..9f3d690e --- /dev/null +++ b/resource-pack/assets/minecraft/items/white_banner.json @@ -0,0 +1,23 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "minecraft:model", + "model": "openinv:item/cursor" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "minecraft:special", + "base": "minecraft:item/template_banner", + "model": { + "type": "minecraft:banner", + "color": "white" + } + }, + "property": "minecraft:custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/items/white_stained_glass_pane.json b/resource-pack/assets/minecraft/items/white_stained_glass_pane.json new file mode 100644 index 00000000..69561da0 --- /dev/null +++ b/resource-pack/assets/minecraft/items/white_stained_glass_pane.json @@ -0,0 +1,19 @@ +{ + "model": { + "type": "minecraft:select", + "cases": [ + { + "model": { + "type": "model", + "model": "openinv:item/not_a_slot" + }, + "when": "openinv:custom" + } + ], + "fallback": { + "type": "model", + "model": "item/white_stained_glass_pane" + }, + "property": "custom_model_data" + } +} diff --git a/resource-pack/assets/minecraft/models/item/crafting_table.json b/resource-pack/assets/minecraft/models/item/crafting_table.json deleted file mode 100644 index 230469c5..00000000 --- a/resource-pack/assets/minecraft/models/item/crafting_table.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "parent": "minecraft:block/crafting_table", - "overrides": [ - { - "model": "openinv:item/crafting_output", - "predicate": { - "custom_model_data": 9999 - } - } - ] -} diff --git a/resource-pack/assets/minecraft/models/item/dropper.json b/resource-pack/assets/minecraft/models/item/dropper.json deleted file mode 100644 index 0c8bb744..00000000 --- a/resource-pack/assets/minecraft/models/item/dropper.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "parent": "minecraft:block/dropper", - "overrides": [ - { - "model": "openinv:item/drop", - "predicate": { - "custom_model_data": 9999 - } - } - ] -} diff --git a/resource-pack/assets/minecraft/models/item/leather_boots.json b/resource-pack/assets/minecraft/models/item/leather_boots.json deleted file mode 100644 index f9cd4073..00000000 --- a/resource-pack/assets/minecraft/models/item/leather_boots.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "overrides": [ - { - "model": "openinv:item/empty_boots", - "predicate": { - "custom_model_data": 9999 - } - }, - { - "model": "minecraft:item/leather_boots_quartz_trim", - "predicate": { - "trim_type": 0.1 - } - }, - { - "model": "minecraft:item/leather_boots_iron_trim", - "predicate": { - "trim_type": 0.2 - } - }, - { - "model": "minecraft:item/leather_boots_netherite_trim", - "predicate": { - "trim_type": 0.3 - } - }, - { - "model": "minecraft:item/leather_boots_redstone_trim", - "predicate": { - "trim_type": 0.4 - } - }, - { - "model": "minecraft:item/leather_boots_copper_trim", - "predicate": { - "trim_type": 0.5 - } - }, - { - "model": "minecraft:item/leather_boots_gold_trim", - "predicate": { - "trim_type": 0.6 - } - }, - { - "model": "minecraft:item/leather_boots_emerald_trim", - "predicate": { - "trim_type": 0.7 - } - }, - { - "model": "minecraft:item/leather_boots_diamond_trim", - "predicate": { - "trim_type": 0.8 - } - }, - { - "model": "minecraft:item/leather_boots_lapis_trim", - "predicate": { - "trim_type": 0.9 - } - }, - { - "model": "minecraft:item/leather_boots_amethyst_trim", - "predicate": { - "trim_type": 1.0 - } - } - ], - "textures": { - "layer0": "minecraft:item/leather_boots", - "layer1": "minecraft:item/leather_boots_overlay" - } -} diff --git a/resource-pack/assets/minecraft/models/item/leather_chestplate.json b/resource-pack/assets/minecraft/models/item/leather_chestplate.json deleted file mode 100644 index d6dc8c5f..00000000 --- a/resource-pack/assets/minecraft/models/item/leather_chestplate.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "overrides": [ - { - "model": "openinv:item/empty_chestplate", - "predicate": { - "custom_model_data": 9999 - } - }, - { - "model": "minecraft:item/leather_chestplate_quartz_trim", - "predicate": { - "trim_type": 0.1 - } - }, - { - "model": "minecraft:item/leather_chestplate_iron_trim", - "predicate": { - "trim_type": 0.2 - } - }, - { - "model": "minecraft:item/leather_chestplate_netherite_trim", - "predicate": { - "trim_type": 0.3 - } - }, - { - "model": "minecraft:item/leather_chestplate_redstone_trim", - "predicate": { - "trim_type": 0.4 - } - }, - { - "model": "minecraft:item/leather_chestplate_copper_trim", - "predicate": { - "trim_type": 0.5 - } - }, - { - "model": "minecraft:item/leather_chestplate_gold_trim", - "predicate": { - "trim_type": 0.6 - } - }, - { - "model": "minecraft:item/leather_chestplate_emerald_trim", - "predicate": { - "trim_type": 0.7 - } - }, - { - "model": "minecraft:item/leather_chestplate_diamond_trim", - "predicate": { - "trim_type": 0.8 - } - }, - { - "model": "minecraft:item/leather_chestplate_lapis_trim", - "predicate": { - "trim_type": 0.9 - } - }, - { - "model": "minecraft:item/leather_chestplate_amethyst_trim", - "predicate": { - "trim_type": 1.0 - } - } - ], - "textures": { - "layer0": "minecraft:item/leather_chestplate", - "layer1": "minecraft:item/leather_chestplate_overlay" - } -} diff --git a/resource-pack/assets/minecraft/models/item/leather_helmet.json b/resource-pack/assets/minecraft/models/item/leather_helmet.json deleted file mode 100644 index 236ae610..00000000 --- a/resource-pack/assets/minecraft/models/item/leather_helmet.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "overrides": [ - { - "model": "openinv:item/empty_helmet", - "predicate": { - "custom_model_data": 9999 - } - }, - { - "model": "minecraft:item/leather_helmet_quartz_trim", - "predicate": { - "trim_type": 0.1 - } - }, - { - "model": "minecraft:item/leather_helmet_iron_trim", - "predicate": { - "trim_type": 0.2 - } - }, - { - "model": "minecraft:item/leather_helmet_netherite_trim", - "predicate": { - "trim_type": 0.3 - } - }, - { - "model": "minecraft:item/leather_helmet_redstone_trim", - "predicate": { - "trim_type": 0.4 - } - }, - { - "model": "minecraft:item/leather_helmet_copper_trim", - "predicate": { - "trim_type": 0.5 - } - }, - { - "model": "minecraft:item/leather_helmet_gold_trim", - "predicate": { - "trim_type": 0.6 - } - }, - { - "model": "minecraft:item/leather_helmet_emerald_trim", - "predicate": { - "trim_type": 0.7 - } - }, - { - "model": "minecraft:item/leather_helmet_diamond_trim", - "predicate": { - "trim_type": 0.8 - } - }, - { - "model": "minecraft:item/leather_helmet_lapis_trim", - "predicate": { - "trim_type": 0.9 - } - }, - { - "model": "minecraft:item/leather_helmet_amethyst_trim", - "predicate": { - "trim_type": 1.0 - } - } - ], - "textures": { - "layer0": "minecraft:item/leather_helmet", - "layer1": "minecraft:item/leather_helmet_overlay" - } -} diff --git a/resource-pack/assets/minecraft/models/item/leather_leggings.json b/resource-pack/assets/minecraft/models/item/leather_leggings.json deleted file mode 100644 index eb9ddc89..00000000 --- a/resource-pack/assets/minecraft/models/item/leather_leggings.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "overrides": [ - { - "model": "openinv:item/empty_leggings", - "predicate": { - "custom_model_data": 9999 - } - }, - { - "model": "minecraft:item/leather_leggings_quartz_trim", - "predicate": { - "trim_type": 0.1 - } - }, - { - "model": "minecraft:item/leather_leggings_iron_trim", - "predicate": { - "trim_type": 0.2 - } - }, - { - "model": "minecraft:item/leather_leggings_netherite_trim", - "predicate": { - "trim_type": 0.3 - } - }, - { - "model": "minecraft:item/leather_leggings_redstone_trim", - "predicate": { - "trim_type": 0.4 - } - }, - { - "model": "minecraft:item/leather_leggings_copper_trim", - "predicate": { - "trim_type": 0.5 - } - }, - { - "model": "minecraft:item/leather_leggings_gold_trim", - "predicate": { - "trim_type": 0.6 - } - }, - { - "model": "minecraft:item/leather_leggings_emerald_trim", - "predicate": { - "trim_type": 0.7 - } - }, - { - "model": "minecraft:item/leather_leggings_diamond_trim", - "predicate": { - "trim_type": 0.8 - } - }, - { - "model": "minecraft:item/leather_leggings_lapis_trim", - "predicate": { - "trim_type": 0.9 - } - }, - { - "model": "minecraft:item/leather_leggings_amethyst_trim", - "predicate": { - "trim_type": 1.0 - } - } - ], - "textures": { - "layer0": "minecraft:item/leather_leggings", - "layer1": "minecraft:item/leather_leggings_overlay" - } -} diff --git a/resource-pack/assets/minecraft/models/item/shield.json b/resource-pack/assets/minecraft/models/item/shield.json deleted file mode 100644 index 5ea7eddd..00000000 --- a/resource-pack/assets/minecraft/models/item/shield.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "parent": "builtin/entity", - "gui_light": "front", - "textures": { - "particle": "block/dark_oak_planks" - }, - "display": { - "thirdperson_righthand": { - "rotation": [ 0, 90, 0 ], - "translation": [ 10, 6, -4 ], - "scale": [ 1, 1, 1 ] - }, - "thirdperson_lefthand": { - "rotation": [ 0, 90, 0 ], - "translation": [ 10, 6, 12 ], - "scale": [ 1, 1, 1 ] - }, - "firstperson_righthand": { - "rotation": [ 0, 180, 5 ], - "translation": [ -10, 2, -10 ], - "scale": [ 1.25, 1.25, 1.25 ] - }, - "firstperson_lefthand": { - "rotation": [ 0, 180, 5 ], - "translation": [ 10, 0, -10 ], - "scale": [ 1.25, 1.25, 1.25 ] - }, - "gui": { - "rotation": [ 15, -25, -5 ], - "translation": [ 2, 3, 0 ], - "scale": [ 0.65, 0.65, 0.65 ] - }, - "fixed": { - "rotation": [ 0, 180, 0 ], - "translation": [ -4.5, 4.5, -5], - "scale":[ 0.55, 0.55, 0.55] - }, - "ground": { - "rotation": [ 0, 0, 0 ], - "translation": [ 2, 4, 2], - "scale":[ 0.25, 0.25, 0.25] - } - }, - "overrides": [ - { - "model": "openinv:item/empty_shield", - "predicate": { - "custom_model_data": 9999 - } - }, - { - "predicate": { - "blocking": 1 - }, - "model": "item/shield_blocking" - } - ] -} diff --git a/resource-pack/assets/minecraft/models/item/white_banner.json b/resource-pack/assets/minecraft/models/item/white_banner.json deleted file mode 100644 index bc6fadb8..00000000 --- a/resource-pack/assets/minecraft/models/item/white_banner.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "parent": "minecraft:item/template_banner", - "overrides": [ - { - "model": "openinv:item/cursor", - "predicate": { - "custom_model_data": 9999 - } - } - ] -} diff --git a/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json b/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json deleted file mode 100644 index e4edacdb..00000000 --- a/resource-pack/assets/minecraft/models/item/white_stained_glass_pane.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "minecraft:block/white_stained_glass" - }, - "overrides": [ - { - "model": "openinv:item/not_a_slot", - "predicate": { - "custom_model_data": 9999 - } - } - ] -} diff --git a/resource-pack/assets/openinv/models/item/crafting_output.json b/resource-pack/assets/openinv/models/item/crafting_output.json index 6c167cdc..482b3af9 100644 --- a/resource-pack/assets/openinv/models/item/crafting_output.json +++ b/resource-pack/assets/openinv/models/item/crafting_output.json @@ -1,7 +1,8 @@ { "texture_size": [ 16, 32 ], "textures": { - "layer0": "openinv:item/crafting_output" + "layer0": "openinv:item/crafting_output", + "particle": "minecraft:block/crafting_table_front" }, "elements": [ { diff --git a/resource-pack/assets/openinv/models/item/empty_boots.json b/resource-pack/assets/openinv/models/item/empty_boots.json index 5b3ddf27..bdb545f8 100644 --- a/resource-pack/assets/openinv/models/item/empty_boots.json +++ b/resource-pack/assets/openinv/models/item/empty_boots.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "minecraft:item/empty_armor_slot_boots" + "layer0": "openinv:item/empty_boots" } } diff --git a/resource-pack/assets/openinv/models/item/empty_chestplate.json b/resource-pack/assets/openinv/models/item/empty_chestplate.json index 4003d627..b407d98c 100644 --- a/resource-pack/assets/openinv/models/item/empty_chestplate.json +++ b/resource-pack/assets/openinv/models/item/empty_chestplate.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "minecraft:item/empty_armor_slot_chestplate" + "layer0": "openinv:item/empty_chestplate" } } diff --git a/resource-pack/assets/openinv/models/item/empty_helmet.json b/resource-pack/assets/openinv/models/item/empty_helmet.json index dda818a6..f7cc30f6 100644 --- a/resource-pack/assets/openinv/models/item/empty_helmet.json +++ b/resource-pack/assets/openinv/models/item/empty_helmet.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "minecraft:item/empty_armor_slot_helmet" + "layer0": "openinv:item/empty_helmet" } } diff --git a/resource-pack/assets/openinv/models/item/empty_leggings.json b/resource-pack/assets/openinv/models/item/empty_leggings.json index c74e7e2a..0467df35 100644 --- a/resource-pack/assets/openinv/models/item/empty_leggings.json +++ b/resource-pack/assets/openinv/models/item/empty_leggings.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "minecraft:item/empty_armor_slot_leggings" + "layer0": "openinv:item/empty_leggings" } } diff --git a/resource-pack/assets/openinv/models/item/empty_shield.json b/resource-pack/assets/openinv/models/item/empty_shield.json index 6e6b21c1..0cf9047a 100644 --- a/resource-pack/assets/openinv/models/item/empty_shield.json +++ b/resource-pack/assets/openinv/models/item/empty_shield.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "minecraft:item/empty_armor_slot_shield" + "layer0": "openinv:item/empty_shield" } } diff --git a/resource-pack/assets/openinv/textures/item/empty_boots.png b/resource-pack/assets/openinv/textures/item/empty_boots.png new file mode 100644 index 0000000000000000000000000000000000000000..356e615bf2926cf53f006a274a4ad22501724172 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`W}YsNAr_~T6C~;sOiaGm^E?(l zymF?9m5;@F*Id66;xm{p$%9N0~)~K>FVdQ I&MBb@0763{vj6}9 literal 0 HcmV?d00001 diff --git a/resource-pack/assets/openinv/textures/item/empty_leggings.png b/resource-pack/assets/openinv/textures/item/empty_leggings.png new file mode 100644 index 0000000000000000000000000000000000000000..bea579e0b423401a42adce9f0d608760174259c7 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`x}GkMAr_~T6C_F$OiaGm+xY|- z8;bELGH>`fJA=6+v0us{gZIF+cb=#CmoPC*;>n)6%=gkEphgByS3j3^P6mdKI;Vst05Nts6aWAK literal 0 HcmV?d00001 diff --git a/resource-pack/pack.mcmeta b/resource-pack/pack.mcmeta index 1f4db36b..786f9616 100644 --- a/resource-pack/pack.mcmeta +++ b/resource-pack/pack.mcmeta @@ -1,7 +1,7 @@ { "pack": { "description": "Improve OpenInv's legibility", - "pack_format": 42, - "supported_formats": [34, 42] + "pack_format": 46, + "supported_formats": [ 46 ] } } From 9b54e4e22e82e4cf01e048b813bf27afc25ad240 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 8 Dec 2024 20:08:52 -0500 Subject: [PATCH 240/340] Bump version to 5.1.4 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- internal/v1_21_R3/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index b8b403a7..9f0d0048 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.4-SNAPSHOT + 5.1.4 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index d9eb974a..402d100a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.4-SNAPSHOT + 5.1.4 openinvapi diff --git a/common/pom.xml b/common/pom.xml index ad5b590c..83cb6744 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.4-SNAPSHOT + 5.1.4 openinvcommon diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index 0192fc36..804fa09f 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.4-SNAPSHOT + 5.1.4 openinvadapter1_21_R2 diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml index 0a79bfb2..cd29e43d 100644 --- a/internal/v1_21_R3/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.4-SNAPSHOT + 5.1.4 openinvadapter1_21_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index e5a3ce62..ca59d7a1 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.4-SNAPSHOT + 5.1.4 openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R3 - 5.1.4-SNAPSHOT + 5.1.4 compile com.lishid openinvadapter1_21_R2 - 5.1.4-SNAPSHOT + 5.1.4 compile diff --git a/pom.xml b/pom.xml index 2869f088..a84a489e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.4-SNAPSHOT + 5.1.4 pom From b0c7c8421e116478d36e9ad66424f280623d1194 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 8 Dec 2024 20:09:37 -0500 Subject: [PATCH 241/340] Bump version to 5.1.5-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- internal/v1_21_R3/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index 9f0d0048..13edc837 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.4 + 5.1.5-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 402d100a..4b19242a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.4 + 5.1.5-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index 83cb6744..6a9ec796 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.4 + 5.1.5-SNAPSHOT openinvcommon diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index 804fa09f..d2061a28 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.4 + 5.1.5-SNAPSHOT openinvadapter1_21_R2 diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml index cd29e43d..f6885be2 100644 --- a/internal/v1_21_R3/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.4 + 5.1.5-SNAPSHOT openinvadapter1_21_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index ca59d7a1..022642b4 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.4 + 5.1.5-SNAPSHOT openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R3 - 5.1.4 + 5.1.5-SNAPSHOT compile com.lishid openinvadapter1_21_R2 - 5.1.4 + 5.1.5-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index a84a489e..0047f5bc 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.4 + 5.1.5-SNAPSHOT pom From 0ac7575bad8482696d2563e622879eba1cd7c383 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 14 Dec 2024 13:18:44 -0500 Subject: [PATCH 242/340] Re-add rpack support for 1.21.3 (#264) * Re-add support for 1.21.3 to resource pack * Add pack icon --- .../minecraft/models/item/crafting_table.json | 11 +++ .../assets/minecraft/models/item/dropper.json | 11 +++ .../minecraft/models/item/leather_boots.json | 75 ++++++++++++++++++ .../models/item/leather_chestplate.json | 75 ++++++++++++++++++ .../minecraft/models/item/leather_helmet.json | 75 ++++++++++++++++++ .../models/item/leather_leggings.json | 75 ++++++++++++++++++ .../assets/minecraft/models/item/shield.json | 58 ++++++++++++++ .../minecraft/models/item/white_banner.json | 11 +++ .../models/item/white_stained_glass_pane.json | 14 ++++ .../minecraft/items/crafting_table.json | 0 .../assets/minecraft/items/dropper.json | 0 .../assets/minecraft/items/leather_boots.json | 0 .../minecraft/items/leather_chestplate.json | 0 .../minecraft/items/leather_helmet.json | 0 .../minecraft/items/leather_leggings.json | 0 .../assets/minecraft/items/shield.json | 0 .../assets/minecraft/items/white_banner.json | 0 .../items/white_stained_glass_pane.json | 0 resource-pack/pack.mcmeta | 14 +++- resource-pack/pack.png | Bin 0 -> 2358 bytes 20 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/crafting_table.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/dropper.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/leather_boots.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/shield.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/white_banner.json create mode 100644 resource-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json rename resource-pack/{ => openinv_44}/assets/minecraft/items/crafting_table.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/dropper.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/leather_boots.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/leather_chestplate.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/leather_helmet.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/leather_leggings.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/shield.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/white_banner.json (100%) rename resource-pack/{ => openinv_44}/assets/minecraft/items/white_stained_glass_pane.json (100%) create mode 100644 resource-pack/pack.png diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/crafting_table.json b/resource-pack/openinv_34/assets/minecraft/models/item/crafting_table.json new file mode 100644 index 00000000..230469c5 --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/crafting_table.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:block/crafting_table", + "overrides": [ + { + "model": "openinv:item/crafting_output", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/dropper.json b/resource-pack/openinv_34/assets/minecraft/models/item/dropper.json new file mode 100644 index 00000000..0c8bb744 --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/dropper.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:block/dropper", + "overrides": [ + { + "model": "openinv:item/drop", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_boots.json b/resource-pack/openinv_34/assets/minecraft/models/item/leather_boots.json new file mode 100644 index 00000000..f9cd4073 --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/leather_boots.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_boots", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_boots_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_boots_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_boots_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_boots_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_boots_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_boots_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_boots_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_boots_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_boots_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_boots_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_boots", + "layer1": "minecraft:item/leather_boots_overlay" + } +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json b/resource-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json new file mode 100644 index 00000000..d6dc8c5f --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_chestplate", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_chestplate_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_chestplate_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_chestplate_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_chestplate_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_chestplate_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_chestplate_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_chestplate_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_chestplate_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_chestplate_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_chestplate_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_chestplate", + "layer1": "minecraft:item/leather_chestplate_overlay" + } +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json b/resource-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json new file mode 100644 index 00000000..236ae610 --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_helmet", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_helmet_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_helmet_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_helmet_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_helmet_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_helmet_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_helmet_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_helmet_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_helmet_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_helmet_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_helmet_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_helmet", + "layer1": "minecraft:item/leather_helmet_overlay" + } +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json b/resource-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json new file mode 100644 index 00000000..eb9ddc89 --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:item/generated", + "overrides": [ + { + "model": "openinv:item/empty_leggings", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "model": "minecraft:item/leather_leggings_quartz_trim", + "predicate": { + "trim_type": 0.1 + } + }, + { + "model": "minecraft:item/leather_leggings_iron_trim", + "predicate": { + "trim_type": 0.2 + } + }, + { + "model": "minecraft:item/leather_leggings_netherite_trim", + "predicate": { + "trim_type": 0.3 + } + }, + { + "model": "minecraft:item/leather_leggings_redstone_trim", + "predicate": { + "trim_type": 0.4 + } + }, + { + "model": "minecraft:item/leather_leggings_copper_trim", + "predicate": { + "trim_type": 0.5 + } + }, + { + "model": "minecraft:item/leather_leggings_gold_trim", + "predicate": { + "trim_type": 0.6 + } + }, + { + "model": "minecraft:item/leather_leggings_emerald_trim", + "predicate": { + "trim_type": 0.7 + } + }, + { + "model": "minecraft:item/leather_leggings_diamond_trim", + "predicate": { + "trim_type": 0.8 + } + }, + { + "model": "minecraft:item/leather_leggings_lapis_trim", + "predicate": { + "trim_type": 0.9 + } + }, + { + "model": "minecraft:item/leather_leggings_amethyst_trim", + "predicate": { + "trim_type": 1.0 + } + } + ], + "textures": { + "layer0": "minecraft:item/leather_leggings", + "layer1": "minecraft:item/leather_leggings_overlay" + } +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/shield.json b/resource-pack/openinv_34/assets/minecraft/models/item/shield.json new file mode 100644 index 00000000..5ea7eddd --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/shield.json @@ -0,0 +1,58 @@ +{ + "parent": "builtin/entity", + "gui_light": "front", + "textures": { + "particle": "block/dark_oak_planks" + }, + "display": { + "thirdperson_righthand": { + "rotation": [ 0, 90, 0 ], + "translation": [ 10, 6, -4 ], + "scale": [ 1, 1, 1 ] + }, + "thirdperson_lefthand": { + "rotation": [ 0, 90, 0 ], + "translation": [ 10, 6, 12 ], + "scale": [ 1, 1, 1 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 180, 5 ], + "translation": [ -10, 2, -10 ], + "scale": [ 1.25, 1.25, 1.25 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 180, 5 ], + "translation": [ 10, 0, -10 ], + "scale": [ 1.25, 1.25, 1.25 ] + }, + "gui": { + "rotation": [ 15, -25, -5 ], + "translation": [ 2, 3, 0 ], + "scale": [ 0.65, 0.65, 0.65 ] + }, + "fixed": { + "rotation": [ 0, 180, 0 ], + "translation": [ -4.5, 4.5, -5], + "scale":[ 0.55, 0.55, 0.55] + }, + "ground": { + "rotation": [ 0, 0, 0 ], + "translation": [ 2, 4, 2], + "scale":[ 0.25, 0.25, 0.25] + } + }, + "overrides": [ + { + "model": "openinv:item/empty_shield", + "predicate": { + "custom_model_data": 9999 + } + }, + { + "predicate": { + "blocking": 1 + }, + "model": "item/shield_blocking" + } + ] +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/white_banner.json b/resource-pack/openinv_34/assets/minecraft/models/item/white_banner.json new file mode 100644 index 00000000..bc6fadb8 --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/white_banner.json @@ -0,0 +1,11 @@ +{ + "parent": "minecraft:item/template_banner", + "overrides": [ + { + "model": "openinv:item/cursor", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json b/resource-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json new file mode 100644 index 00000000..e4edacdb --- /dev/null +++ b/resource-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json @@ -0,0 +1,14 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:block/white_stained_glass" + }, + "overrides": [ + { + "model": "openinv:item/not_a_slot", + "predicate": { + "custom_model_data": 9999 + } + } + ] +} diff --git a/resource-pack/assets/minecraft/items/crafting_table.json b/resource-pack/openinv_44/assets/minecraft/items/crafting_table.json similarity index 100% rename from resource-pack/assets/minecraft/items/crafting_table.json rename to resource-pack/openinv_44/assets/minecraft/items/crafting_table.json diff --git a/resource-pack/assets/minecraft/items/dropper.json b/resource-pack/openinv_44/assets/minecraft/items/dropper.json similarity index 100% rename from resource-pack/assets/minecraft/items/dropper.json rename to resource-pack/openinv_44/assets/minecraft/items/dropper.json diff --git a/resource-pack/assets/minecraft/items/leather_boots.json b/resource-pack/openinv_44/assets/minecraft/items/leather_boots.json similarity index 100% rename from resource-pack/assets/minecraft/items/leather_boots.json rename to resource-pack/openinv_44/assets/minecraft/items/leather_boots.json diff --git a/resource-pack/assets/minecraft/items/leather_chestplate.json b/resource-pack/openinv_44/assets/minecraft/items/leather_chestplate.json similarity index 100% rename from resource-pack/assets/minecraft/items/leather_chestplate.json rename to resource-pack/openinv_44/assets/minecraft/items/leather_chestplate.json diff --git a/resource-pack/assets/minecraft/items/leather_helmet.json b/resource-pack/openinv_44/assets/minecraft/items/leather_helmet.json similarity index 100% rename from resource-pack/assets/minecraft/items/leather_helmet.json rename to resource-pack/openinv_44/assets/minecraft/items/leather_helmet.json diff --git a/resource-pack/assets/minecraft/items/leather_leggings.json b/resource-pack/openinv_44/assets/minecraft/items/leather_leggings.json similarity index 100% rename from resource-pack/assets/minecraft/items/leather_leggings.json rename to resource-pack/openinv_44/assets/minecraft/items/leather_leggings.json diff --git a/resource-pack/assets/minecraft/items/shield.json b/resource-pack/openinv_44/assets/minecraft/items/shield.json similarity index 100% rename from resource-pack/assets/minecraft/items/shield.json rename to resource-pack/openinv_44/assets/minecraft/items/shield.json diff --git a/resource-pack/assets/minecraft/items/white_banner.json b/resource-pack/openinv_44/assets/minecraft/items/white_banner.json similarity index 100% rename from resource-pack/assets/minecraft/items/white_banner.json rename to resource-pack/openinv_44/assets/minecraft/items/white_banner.json diff --git a/resource-pack/assets/minecraft/items/white_stained_glass_pane.json b/resource-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json similarity index 100% rename from resource-pack/assets/minecraft/items/white_stained_glass_pane.json rename to resource-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json diff --git a/resource-pack/pack.mcmeta b/resource-pack/pack.mcmeta index 786f9616..89488360 100644 --- a/resource-pack/pack.mcmeta +++ b/resource-pack/pack.mcmeta @@ -2,6 +2,18 @@ "pack": { "description": "Improve OpenInv's legibility", "pack_format": 46, - "supported_formats": [ 46 ] + "supported_formats": [ 34, 46 ] + }, + "overlays": { + "entries": [ + { + "formats": [ 44, 46 ], + "directory": "openinv_44" + }, + { + "formats": [ 34, 43 ], + "directory": "openinv_34" + } + ] } } diff --git a/resource-pack/pack.png b/resource-pack/pack.png new file mode 100644 index 0000000000000000000000000000000000000000..536a28f670bfd864cc28e5895c3892cf03fbc057 GIT binary patch literal 2358 zcmV-63CZ?}P)VvEmJ~oNYgQ=?MRfQOB=hf;#Ao3-^kV;85Vl}uHK5kKxMi)U!(rN!NqWKf4cS=4X&6-M{yGmj=)ByQ5D16EBn4N~ zg_pAJi))x zQHEYfdav_zDiXQ3vN(W{$#+E0Rpe?-Bocu{qC_|Y$wHHI=2unKQbtp*W}CQB@UptiMT&k*8+#e7zIgC_W2-&k|PfZLdR(>i9fuf2Du**BbTW2ot-F=5G zk)qKki9`Y!w^FEegG4p_xJAKzU;Pb$y?Z+8bZfkS`pt)8A7JYNoo(c7?XE1Nd2 z=h}a70I;d`5uQ^w)9Jt?RtiqMpu4-fF1tgwuc3)eo7Xdzxnm6KirBPyJx#uKZ0iWH z%R9ozKTfWiC07zEcFVK-tuq%4i;K{?Z7XA=x0#t;bKFa7Q`A6^=JU_-%53CcR9 zfj_+cF)sHye%AX$LGa8S@WUBxlrK(yQg`O;+`E=Z(rmY?+?vQ1_5(|c4FCO8?qqWW zn-%_bB~6RJ(U{m8@H`ZUBUuRwz{X&JTN7C(Cg+)&o@XRAMPq{-n4`tt$l}r>)3bM1 zJ;tr(RGJ_~CAg=i=ZzO*6Vm|9%`X^Zi%W|D_>FWh^c&(&$x;L^wG; zw_*#67adM6UXJO4O3yl&-T#}>)rA*tg!Jd4lGr=vt`;HseGNtq$Yd8dc;skRg3G1! zav=8y$sS_&-tH)Ewii&Ic=j;7TdcKAcEM1<^z2;@9ywZY-DT|#$6{czwKThkEV`*R zxfHc(XVLqC#C8}l=AHHnnN_<*C72dPTLT{az6SOmdfC$IPOCFc-ClHOy<5!+#P{;t zA;A!#Sggb-p;(LqiNw7eAQS&#?@Js!a+LjtUM?b{yRq0hCx1wsQx1kGbdc!oY}Sfb zu4l;ZeL2PJW3GHYpVxbOdJ7ezqKU&okB=GX%**&9%^{PyBEHxTXEndqBrNnXu`Q;g zZ^?$Qd_IrT)zwwhJ0suL9#o)w7S)%nsijZ=|`rC}7^ z%H?)uc5|%Of3tQMJ3311X9QcCdGGKK7&uvQ^BtSox%8jw_ zk7fA`#bQQ7mEKn*x7WDh@p#2$@n|$kS67!2Yi0`Qb>M!{Xw=97AKiE#kH^jS$C?en zCnl!=3Q9Tm3E$ejfmVMb!IoxT|I;}pXJ+~3b3f(9S6`*OySwDy_zswq?t3Y2nW@Rm zkP{Co2Uk8HW!qyxMlR3dS?8tI-$-V94(+4s^gQ!8-`l;FU`sQDC(n{w$^kHbd&+kA zXVvT_)&pvGHC9dBi?D0w768s&yh^CEoqv8jPr&Cx{r+}*4Nd$$p)t2`mxWx8@!L~u z>1bn1N1J25tWJY9zZ5<7PZPL1re)=c8!=wsf>Hw@|1` zz4zfYdiU)DVBfxdRow}!DGvbf_`S3?1-NkOI!%u^;90IvZRuzux0Evy;7BUXkDvJt z(@IChMUge@0ndK>`9i$UWC?Y)6Y%*+T`O2#aVyNs%+cg&WF(d5x$sV=l@5Njckhad cqMD}hKd!rT*X}rD8UO$Q07*qoM6N<$g2-Ba0{{R3 literal 0 HcmV?d00001 From 9196ab3e61db8e4da546e47fd64974d167438612 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 29 Dec 2024 08:47:09 -0500 Subject: [PATCH 243/340] Improve equal access (#269) * Respect deny equals mode if viewer cannot edit * Revert default mode to allow equals --- .../main/java/com/lishid/openinv/OpenInv.java | 42 ++++++++++--------- plugin/src/main/resources/config.yml | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 3c5e59e1..d20d924d 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -255,30 +255,32 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean viewOnly = edit != null && !edit.hasPermission(player); - if (!ownContainer && !viewOnly) { - for (int level = 4; level > 0; --level) { - String permission = "openinv.access.level." + level; - // If the target doesn't have this access level... - if (!target.hasPermission(permission)) { - // If the viewer does have the access level, all good. - if (player.hasPermission(permission)) { - break; - } - // Otherwise check next access level. - continue; - } + if (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) { + this.accessor.openInventory(player, inventory, viewOnly); + } - // If the player doesn't have an equal access level or equal access is a denial, deny. - if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) { - return null; + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + // If the target doesn't have this access level... + if (!target.hasPermission(permission)) { + // If the viewer does have the access level, all good. + if (player.hasPermission(permission)) { + break; } + // Otherwise check next access level. + continue; + } - // Since this is a tie, setting decides view state. - if (config.getAccessEqualMode() == AccessEqualMode.VIEW) { - viewOnly = true; - } - break; + // If the viewer doesn't have an equal access level or equal access is a denial, deny. + if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) { + return null; + } + + // Since this is a tie, setting decides view state. + if (config.getAccessEqualMode() == AccessEqualMode.VIEW) { + viewOnly = true; } + break; } return this.accessor.openInventory(player, inventory, viewOnly); diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index fb8b4b08..a69d6d62 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,6 +1,6 @@ config-version: 8 settings: - equal-access: view + equal-access: allow command: open: no-args-opens-self: false From 7f1f6ccc82f0bdcb1310510740f3d0ab8079bc6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:39:40 +0000 Subject: [PATCH 244/340] Bump com.github.jikoo:planarwrappers from 3.2.3 to 3.3.0 (#270) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0047f5bc..dcd0cae4 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ planarwrappers com.github.jikoo compile - 3.2.3 + 3.3.0 From 56f8cf4d7138646153977ed1f4a0b59766283d8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 13:11:50 -0500 Subject: [PATCH 245/340] Bump softprops/action-gh-release from 2.1.0 to 2.2.0 (#271) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.1.0 to 2.2.0. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.1.0...v2.2.0) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index c50ddd64..fbcd66eb 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -34,7 +34,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.1.0 + uses: softprops/action-gh-release@v2.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From f5c31c32d390d866a4a370a4aa7905d220225a67 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 4 Jan 2025 21:37:29 -0500 Subject: [PATCH 246/340] Correct edit other permission See discussion #266 --- plugin/src/main/java/com/lishid/openinv/OpenInv.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index d20d924d..94ebd6f3 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -250,7 +250,7 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final if (inventory instanceof ISpecialPlayerInventory) { edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; } else if (inventory instanceof ISpecialEnderChest) { - edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER; + edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_EDIT_OTHER; } boolean viewOnly = edit != null && !edit.hasPermission(player); From e948fdc011ed8d6a779832cabf35d105c6423ccf Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 10 Jan 2025 10:35:48 -0500 Subject: [PATCH 247/340] Improve compatibility of view-only inventories (#273) --- .../openinv/internal/ISpecialEnderChest.java | 8 ++ .../openinv/internal/ISpecialInventory.java | 8 ++ .../internal/ISpecialPlayerInventory.java | 26 +--- .../container/bukkit/OpenDummyInventory.java | 11 +- .../bukkit/OpenDummyPlayerInventory.java | 134 ++++++++++++++++++ .../container/menu/OpenChestMenu.java | 2 +- .../container/menu/OpenInventoryMenu.java | 4 +- .../container/bukkit/OpenDummyInventory.java | 11 +- .../bukkit/OpenDummyPlayerInventory.java | 134 ++++++++++++++++++ .../container/menu/OpenChestMenu.java | 2 +- .../container/menu/OpenInventoryMenu.java | 4 +- 11 files changed, 314 insertions(+), 30 deletions(-) create mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java create mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialEnderChest.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialEnderChest.java index e46cc80f..4b35c4c8 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialEnderChest.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialEnderChest.java @@ -16,9 +16,17 @@ package com.lishid.openinv.internal; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + /** * An {@link ISpecialInventory} representing an ender chest. */ public interface ISpecialEnderChest extends ISpecialInventory { + @Override + default @NotNull InventoryType getBukkitType() { + return InventoryType.ENDER_CHEST; + } + } diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java index 270557b5..430cca5f 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java @@ -18,6 +18,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.NotNull; @@ -33,6 +34,13 @@ public interface ISpecialInventory { */ @NotNull Inventory getBukkitInventory(); + /** + * Get the {@link InventoryType} corresponding to this {@code ISpecialInventory}. + * + * @return the type of Bukkit inventory + */ + @NotNull InventoryType getBukkitType(); + /** * Set the owning {@link Player} instance to a newly-joined user. * diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java index 7c787344..a571e0a9 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java @@ -16,35 +16,17 @@ package com.lishid.openinv.internal; -import java.util.List; -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.Inventory; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; /** * An {@link ISpecialInventory} representing a player inventory. */ public interface ISpecialPlayerInventory extends ISpecialInventory { - /* - * Player inventory usage varies from all other inventories - as the inventory is technically open at all times, - * if the player is online or has been online while the inventory is open, they are in the viewer list. - */ @Override - default boolean isInUse() { - Inventory inventory = getBukkitInventory(); - List viewers = inventory.getViewers(); - - if (viewers.size() != 1) { - return !viewers.isEmpty(); - } - - HumanEntity viewer = viewers.get(0); - - if (!viewer.getUniqueId().equals(getPlayer().getUniqueId())) { - return true; - } - - return viewer.getOpenInventory().getTopInventory().equals(inventory); + default @NotNull InventoryType getBukkitType() { + return InventoryType.PLAYER; } } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java index 61b89810..6fdab659 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java @@ -4,6 +4,7 @@ import net.minecraft.world.Container; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,8 +18,16 @@ */ public class OpenDummyInventory extends CraftInventory implements ViewOnly { - public OpenDummyInventory(Container inventory) { + private final InventoryType type; + + public OpenDummyInventory(Container inventory, InventoryType type) { super(inventory); + this.type = type; + } + + @Override + public @NotNull InventoryType getType() { + return type; } @Override diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java new file mode 100644 index 00000000..17bb2a04 --- /dev/null +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java @@ -0,0 +1,134 @@ +package com.lishid.openinv.internal.v1_21_R2.container.bukkit; + +import net.minecraft.world.Container; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory { + + public OpenDummyPlayerInventory(Container inventory) { + super(inventory, InventoryType.PLAYER); + } + + @Override + public HumanEntity getHolder() { + return (HumanEntity) super.getHolder(); + } + + @Override + public @NotNull ItemStack[] getArmorContents() { + return new ItemStack[4]; + } + + @Override + public @NotNull ItemStack[] getExtraContents() { + return new ItemStack[4]; + } + + @Override + public @Nullable ItemStack getHelmet() { + return null; + } + + @Override + public @Nullable ItemStack getChestplate() { + return null; + } + + @Override + public @Nullable ItemStack getLeggings() { + return null; + } + + @Override + public @Nullable ItemStack getBoots() { + return null; + } + + @Override + public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) { + + } + + @Override + public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) { + return null; + } + + @Override + public void setArmorContents(@Nullable ItemStack[] items) { + + } + + @Override + public void setExtraContents(@Nullable ItemStack[] items) { + + } + + @Override + public void setHelmet(@Nullable ItemStack helmet) { + + } + + @Override + public void setChestplate(@Nullable ItemStack chestplate) { + + } + + @Override + public void setLeggings(@Nullable ItemStack leggings) { + + } + + @Override + public void setBoots(@Nullable ItemStack boots) { + + } + + @Override + public @NotNull ItemStack getItemInMainHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInMainHand(@Nullable ItemStack item) { + + } + + @Override + public @NotNull ItemStack getItemInOffHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInOffHand(@Nullable ItemStack item) { + + } + + @Override + public @NotNull ItemStack getItemInHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInHand(@Nullable ItemStack stack) { + + } + + @Override + public int getHeldItemSlot() { + return 0; + } + + @Override + public void setHeldItemSlot(int slot) { + + } + +} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java index d12c373a..ff2c1026 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java @@ -143,7 +143,7 @@ protected void preSlotSetup() {} protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { Inventory top; if (viewOnly) { - top = new OpenDummyInventory(container); + top = new OpenDummyInventory(container, container.getBukkitType()); } else { top = container.getBukkitInventory(); } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java index 0d0f2a1e..dc80ae30 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java @@ -2,7 +2,7 @@ import com.google.common.base.Preconditions; import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyPlayerInventory; import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventorySelf; import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop; import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment; @@ -102,7 +102,7 @@ protected void preSlotSetup() { protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { org.bukkit.inventory.Inventory bukkitInventory; if (viewOnly) { - bukkitInventory = new OpenDummyInventory(container); + bukkitInventory = new OpenDummyPlayerInventory(container); } else if (ownContainer) { bukkitInventory = new OpenPlayerInventorySelf(container, offset); } else { diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java index 0a140ae0..d859d8db 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java @@ -4,6 +4,7 @@ import net.minecraft.world.Container; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,8 +18,16 @@ */ public class OpenDummyInventory extends CraftInventory implements ViewOnly { - public OpenDummyInventory(Container inventory) { + private final InventoryType type; + + public OpenDummyInventory(Container inventory, InventoryType type) { super(inventory); + this.type = type; + } + + @Override + public @NotNull InventoryType getType() { + return type; } @Override diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java new file mode 100644 index 00000000..ced1128c --- /dev/null +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java @@ -0,0 +1,134 @@ +package com.lishid.openinv.internal.v1_21_R3.container.bukkit; + +import net.minecraft.world.Container; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory { + + public OpenDummyPlayerInventory(Container inventory) { + super(inventory, InventoryType.PLAYER); + } + + @Override + public HumanEntity getHolder() { + return (HumanEntity) super.getHolder(); + } + + @Override + public @NotNull ItemStack[] getArmorContents() { + return new ItemStack[4]; + } + + @Override + public @NotNull ItemStack[] getExtraContents() { + return new ItemStack[4]; + } + + @Override + public @Nullable ItemStack getHelmet() { + return null; + } + + @Override + public @Nullable ItemStack getChestplate() { + return null; + } + + @Override + public @Nullable ItemStack getLeggings() { + return null; + } + + @Override + public @Nullable ItemStack getBoots() { + return null; + } + + @Override + public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) { + + } + + @Override + public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) { + return null; + } + + @Override + public void setArmorContents(@Nullable ItemStack[] items) { + + } + + @Override + public void setExtraContents(@Nullable ItemStack[] items) { + + } + + @Override + public void setHelmet(@Nullable ItemStack helmet) { + + } + + @Override + public void setChestplate(@Nullable ItemStack chestplate) { + + } + + @Override + public void setLeggings(@Nullable ItemStack leggings) { + + } + + @Override + public void setBoots(@Nullable ItemStack boots) { + + } + + @Override + public @NotNull ItemStack getItemInMainHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInMainHand(@Nullable ItemStack item) { + + } + + @Override + public @NotNull ItemStack getItemInOffHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInOffHand(@Nullable ItemStack item) { + + } + + @Override + public @NotNull ItemStack getItemInHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInHand(@Nullable ItemStack stack) { + + } + + @Override + public int getHeldItemSlot() { + return 0; + } + + @Override + public void setHeldItemSlot(int slot) { + + } + +} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java index f6557c59..125f7689 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java @@ -143,7 +143,7 @@ protected void preSlotSetup() {} protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { Inventory top; if (viewOnly) { - top = new OpenDummyInventory(container); + top = new OpenDummyInventory(container, container.getBukkitType()); } else { top = container.getBukkitInventory(); } diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java index 91732433..2d26e091 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java @@ -2,7 +2,7 @@ import com.google.common.base.Preconditions; import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyPlayerInventory; import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventorySelf; import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop; import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment; @@ -102,7 +102,7 @@ protected void preSlotSetup() { protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { org.bukkit.inventory.Inventory bukkitInventory; if (viewOnly) { - bukkitInventory = new OpenDummyInventory(container); + bukkitInventory = new OpenDummyPlayerInventory(container); } else if (ownContainer) { bukkitInventory = new OpenPlayerInventorySelf(container, offset); } else { From eb98bd79d35edfb8f09f7625ee0a4903533a1777 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 10 Jan 2025 11:01:18 -0500 Subject: [PATCH 248/340] Update release action --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index fbcd66eb..93cf9e26 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -34,7 +34,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.2.0 + uses: softprops/action-gh-release@v2.2.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From f8df83b31dfcb92f3bd4d8064a250a4c56bd00b3 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 10 Jan 2025 11:01:37 -0500 Subject: [PATCH 249/340] Bump version to 5.1.5 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- internal/v1_21_R3/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index 13edc837..eaf11611 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.5-SNAPSHOT + 5.1.5 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 4b19242a..3c76ce04 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.5-SNAPSHOT + 5.1.5 openinvapi diff --git a/common/pom.xml b/common/pom.xml index 6a9ec796..2fd3a410 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.5-SNAPSHOT + 5.1.5 openinvcommon diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index d2061a28..28fb1fe7 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.5-SNAPSHOT + 5.1.5 openinvadapter1_21_R2 diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml index f6885be2..ee25d93d 100644 --- a/internal/v1_21_R3/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.5-SNAPSHOT + 5.1.5 openinvadapter1_21_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index 022642b4..04bd05d2 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.5-SNAPSHOT + 5.1.5 openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R3 - 5.1.5-SNAPSHOT + 5.1.5 compile com.lishid openinvadapter1_21_R2 - 5.1.5-SNAPSHOT + 5.1.5 compile diff --git a/pom.xml b/pom.xml index dcd0cae4..30e003e0 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.5-SNAPSHOT + 5.1.5 pom From 6656e431b64187fdc56f867f970f5683fbb60a21 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 10 Jan 2025 11:02:10 -0500 Subject: [PATCH 250/340] Bump version to 5.1.6-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- internal/v1_21_R3/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index eaf11611..d03d99b1 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.5 + 5.1.6-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 3c76ce04..5efcc58c 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.5 + 5.1.6-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index 2fd3a410..289cf4a3 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.5 + 5.1.6-SNAPSHOT openinvcommon diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index 28fb1fe7..da5fc6af 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.5 + 5.1.6-SNAPSHOT openinvadapter1_21_R2 diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml index ee25d93d..681e32bd 100644 --- a/internal/v1_21_R3/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.5 + 5.1.6-SNAPSHOT openinvadapter1_21_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index 04bd05d2..06ff92df 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.5 + 5.1.6-SNAPSHOT openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R3 - 5.1.5 + 5.1.6-SNAPSHOT compile com.lishid openinvadapter1_21_R2 - 5.1.5 + 5.1.6-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index 30e003e0..0350af98 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.5 + 5.1.6-SNAPSHOT pom From 206fff2c0a35d7095cf00923964b2aae817c234e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 18 Jan 2025 11:56:22 -0500 Subject: [PATCH 251/340] Fix off-hand update relying on Spigot method (#277) --- .../internal/v1_21_R2/container/slot/ContentOffHand.java | 9 ++++++++- .../internal/v1_21_R3/container/slot/ContentOffHand.java | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java index b015bb8e..dc97b4c6 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java @@ -1,8 +1,10 @@ package com.lishid.openinv.internal.v1_21_R2.container.slot; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.inventory.Slot; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -37,7 +39,12 @@ public void setChanged() { if (holder.connection != null && !holder.connection.isDisconnected() && holder.containerMenu != holder.inventoryMenu) { - holder.resendItemInHands(); + holder.connection.send( + new ClientboundContainerSetSlotPacket( + holder.inventoryMenu.containerId, + holder.inventoryMenu.incrementStateId(), + InventoryMenu.SHIELD_SLOT, + holder.getOffhandItem())); } } }; diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java index df66fec8..cc7f3337 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java @@ -1,8 +1,10 @@ package com.lishid.openinv.internal.v1_21_R3.container.slot; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.inventory.Slot; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -37,7 +39,12 @@ public void setChanged() { if (holder.connection != null && !holder.connection.isDisconnected() && holder.containerMenu != holder.inventoryMenu) { - holder.resendItemInHands(); + holder.connection.send( + new ClientboundContainerSetSlotPacket( + holder.inventoryMenu.containerId, + holder.inventoryMenu.incrementStateId(), + InventoryMenu.SHIELD_SLOT, + holder.getOffhandItem())); } } }; From 8a388b56202dfa8df1152469f761e46261507a90 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 18 Jan 2025 12:45:44 -0500 Subject: [PATCH 252/340] Fix view-only ender chest (#278) Fixes access permissions being checked against self --- .../internal/v1_21_R2/container/menu/OpenChestMenu.java | 5 +++-- .../internal/v1_21_R3/container/menu/OpenChestMenu.java | 5 +++-- plugin/src/main/java/com/lishid/openinv/OpenInv.java | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java index ff2c1026..73babce1 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java @@ -124,10 +124,11 @@ protected OpenChestMenu( protected void preSlotSetup() {} protected @NotNull Slot getUpperSlot(int index, int x, int y) { + Slot slot = new Slot(container, index, x, y); if (viewOnly) { - return new SlotViewOnly(container, index, x, y); + return SlotViewOnly.wrap(slot); } - return new Slot(container, index, x, y); + return slot; } diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java index 125f7689..de3dcf29 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java +++ b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java @@ -124,10 +124,11 @@ protected OpenChestMenu( protected void preSlotSetup() {} protected @NotNull Slot getUpperSlot(int index, int x, int y) { + Slot slot = new Slot(container, index, x, y); if (viewOnly) { - return new SlotViewOnly(container, index, x, y); + return SlotViewOnly.wrap(slot); } - return new Slot(container, index, x, y); + return slot; } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 94ebd6f3..86f1ecff 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -256,7 +256,7 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean viewOnly = edit != null && !edit.hasPermission(player); if (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) { - this.accessor.openInventory(player, inventory, viewOnly); + return this.accessor.openInventory(player, inventory, viewOnly); } for (int level = 4; level > 0; --level) { From 19eafddfacba3f4641da62c352046864fadc8eb0 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 18 Jan 2025 13:57:48 -0500 Subject: [PATCH 253/340] Bump version to 5.1.6 for release --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- internal/v1_21_R3/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index d03d99b1..aee83fa4 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.6-SNAPSHOT + 5.1.6 openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index 5efcc58c..cf7cf6ef 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.6-SNAPSHOT + 5.1.6 openinvapi diff --git a/common/pom.xml b/common/pom.xml index 289cf4a3..30644fe4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.6-SNAPSHOT + 5.1.6 openinvcommon diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index da5fc6af..874d3d2b 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.6-SNAPSHOT + 5.1.6 openinvadapter1_21_R2 diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml index 681e32bd..523c55b3 100644 --- a/internal/v1_21_R3/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.6-SNAPSHOT + 5.1.6 openinvadapter1_21_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index 06ff92df..601bc1f6 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.6-SNAPSHOT + 5.1.6 openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R3 - 5.1.6-SNAPSHOT + 5.1.6 compile com.lishid openinvadapter1_21_R2 - 5.1.6-SNAPSHOT + 5.1.6 compile diff --git a/pom.xml b/pom.xml index 0350af98..75a9c60c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.6-SNAPSHOT + 5.1.6 pom From 6c69350c30daa6e7e92968f866ebd88b863a3c42 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 18 Jan 2025 13:58:16 -0500 Subject: [PATCH 254/340] Bump version to 5.1.7-SNAPSHOT for development --- addon/togglepersist/pom.xml | 2 +- api/pom.xml | 2 +- common/pom.xml | 2 +- internal/v1_21_R2/pom.xml | 2 +- internal/v1_21_R3/pom.xml | 2 +- plugin/pom.xml | 6 +++--- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml index aee83fa4..e927bf53 100644 --- a/addon/togglepersist/pom.xml +++ b/addon/togglepersist/pom.xml @@ -8,7 +8,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.6 + 5.1.7-SNAPSHOT openinvtogglepersist diff --git a/api/pom.xml b/api/pom.xml index cf7cf6ef..8f93172e 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.6 + 5.1.7-SNAPSHOT openinvapi diff --git a/common/pom.xml b/common/pom.xml index 30644fe4..b8d9a944 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ com.lishid openinvparent - 5.1.6 + 5.1.7-SNAPSHOT openinvcommon diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml index 874d3d2b..ea088dfe 100644 --- a/internal/v1_21_R2/pom.xml +++ b/internal/v1_21_R2/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.6 + 5.1.7-SNAPSHOT openinvadapter1_21_R2 diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml index 523c55b3..733f3b4b 100644 --- a/internal/v1_21_R3/pom.xml +++ b/internal/v1_21_R3/pom.xml @@ -23,7 +23,7 @@ openinvparent com.lishid ../../pom.xml - 5.1.6 + 5.1.7-SNAPSHOT openinvadapter1_21_R3 diff --git a/plugin/pom.xml b/plugin/pom.xml index 601bc1f6..9657ea6b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -21,7 +21,7 @@ openinvparent com.lishid - 5.1.6 + 5.1.7-SNAPSHOT openinvplugin @@ -47,13 +47,13 @@ com.lishid openinvadapter1_21_R3 - 5.1.6 + 5.1.7-SNAPSHOT compile com.lishid openinvadapter1_21_R2 - 5.1.6 + 5.1.7-SNAPSHOT compile diff --git a/pom.xml b/pom.xml index 75a9c60c..0bb06df2 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ common - 5.1.6 + 5.1.7-SNAPSHOT pom From 348fde917322c9846a7d29822a0f19e3ead768d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:35:05 +0000 Subject: [PATCH 255/340] Bump org.jetbrains:annotations from 26.0.1 to 26.0.2 (#283) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bb06df2..7e8a9ed9 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ annotations org.jetbrains provided - 26.0.1 + 26.0.2 spigot-api From 7ab3e0c03f80b27936a654ea5fa7329c37be34ff Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 9 Feb 2025 22:15:37 -0500 Subject: [PATCH 256/340] Compile against Paper (#282) * Convert to Gradle * Reduce script usage * Simplify release process --- .github/workflows/ci.yml | 23 +- .github/workflows/draft_release.yml | 27 +- .github/workflows/external_release.yml | 2 +- .github/workflows/resource_pack_ci.yml | 4 +- .gitignore | 3 + README.MD | 17 +- addon/togglepersist/build.gradle.kts | 21 + addon/togglepersist/pom.xml | 72 --- .../src/main/resources/plugin.yml | 2 +- api/build.gradle.kts | 13 + api/pom.xml | 49 -- build.gradle.kts | 24 + buildSrc/build.gradle.kts | 7 + .../src/main/kotlin/openinv-base.gradle.kts | 34 ++ .../src/main/kotlin/remap-spigot.gradle.kts | 55 ++ common/build.gradle.kts | 7 + common/pom.xml | 30 -- gradle.properties | 9 + gradle/libs.versions.toml | 19 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 234 +++++++++ gradlew.bat | 89 ++++ internal/common/build.gradle.kts | 51 ++ .../internal/common}/InternalAccessor.java | 14 +- .../common}/container/AnySilentContainer.java | 10 +- .../common}/container/OpenEnderChest.java | 30 +- .../common}/container/OpenInventory.java | 38 +- .../common}/container/Placeholders.java | 7 +- .../container/bukkit/OpenDummyInventory.java | 4 +- .../bukkit/OpenDummyPlayerInventory.java | 2 +- .../container/bukkit/OpenPlayerInventory.java | 8 +- .../bukkit/OpenPlayerInventorySelf.java | 4 +- .../common}/container/menu/OpenChestMenu.java | 28 +- .../container/menu/OpenEnderChestMenu.java | 6 +- .../container/menu/OpenInventoryMenu.java | 20 +- .../common}/container/slot/Content.java | 2 +- .../container/slot/ContentCrafting.java | 11 +- .../container/slot/ContentCraftingResult.java | 4 +- .../common}/container/slot/ContentCursor.java | 11 +- .../common}/container/slot/ContentDrop.java | 11 +- .../container/slot/ContentEquipment.java | 8 +- .../common}/container/slot/ContentList.java | 2 +- .../container/slot/ContentOffHand.java | 7 +- .../container/slot/ContentViewOnly.java | 2 +- .../container/slot/SlotPlaceholder.java | 2 +- .../common}/container/slot/SlotViewOnly.java | 34 +- .../internal/common}/player/OpenPlayer.java | 11 +- .../common}/player/PlayerManager.java | 29 +- internal/spigot/build.gradle.kts | 47 ++ .../reobf/container/AnySilentContainer.java | 194 +++++++ .../internal/reobf}/player/PlayerManager.java | 21 +- internal/v1_21_R2/pom.xml | 77 --- .../internal/v1_21_R2/InternalAccessor.java | 81 --- .../v1_21_R2/container/OpenEnderChest.java | 196 ------- .../v1_21_R2/container/OpenInventory.java | 364 ------------- .../v1_21_R2/container/Placeholders.java | 183 ------- .../container/menu/OpenChestMenu.java | 479 ------------------ .../container/menu/OpenEnderChestMenu.java | 53 -- .../container/slot/ContentEquipment.java | 78 --- .../v1_21_R2/container/slot/ContentList.java | 58 --- .../container/slot/ContentOffHand.java | 53 -- .../container/slot/ContentViewOnly.java | 56 -- internal/v1_21_R3/pom.xml | 77 --- .../container/AnySilentContainer.java | 210 -------- .../container/bukkit/OpenDummyInventory.java | 172 ------- .../bukkit/OpenDummyPlayerInventory.java | 134 ----- .../container/bukkit/OpenPlayerInventory.java | 221 -------- .../bukkit/OpenPlayerInventorySelf.java | 26 - .../container/menu/OpenInventoryMenu.java | 262 ---------- .../v1_21_R3/container/slot/Content.java | 69 --- .../container/slot/ContentCrafting.java | 131 ----- .../container/slot/ContentCraftingResult.java | 48 -- .../container/slot/ContentCursor.java | 117 ----- .../v1_21_R3/container/slot/ContentDrop.java | 88 ---- .../container/slot/SlotPlaceholder.java | 20 - .../v1_21_R3/container/slot/SlotViewOnly.java | 151 ------ .../internal/v1_21_R3/player/OpenPlayer.java | 178 ------- jitpack.yml | 8 +- plugin/build.gradle.kts | 42 ++ plugin/pom.xml | 140 ----- .../main/java/com/lishid/openinv/OpenInv.java | 6 - .../lishid/openinv/util/InternalAccessor.java | 31 +- plugin/src/main/resources/plugin.yml | 4 +- pom.xml | 221 -------- resource-pack/build.gradle.kts | 17 + .../openinv/models/item/crafting_output.json | 0 .../assets/openinv/models/item/cursor.json | 0 .../assets/openinv/models/item/drop.json | 0 .../openinv/models/item/empty_boots.json | 0 .../openinv/models/item/empty_chestplate.json | 0 .../openinv/models/item/empty_helmet.json | 0 .../openinv/models/item/empty_leggings.json | 0 .../openinv/models/item/empty_shield.json | 0 .../openinv/models/item/not_a_slot.json | 0 .../openinv/textures/item/crafting_output.png | Bin .../assets/openinv/textures/item/cursor.png | Bin .../assets/openinv/textures/item/drop.png | Bin .../openinv/textures/item/empty_boots.png | Bin .../textures/item/empty_chestplate.png | Bin .../openinv/textures/item/empty_helmet.png | Bin .../openinv/textures/item/empty_leggings.png | Bin .../openinv/textures/item/empty_shield.png | Bin .../openinv/textures/item/not_a_slot.png | Bin .../minecraft/models/item/crafting_table.json | 0 .../assets/minecraft/models/item/dropper.json | 0 .../minecraft/models/item/leather_boots.json | 0 .../models/item/leather_chestplate.json | 0 .../minecraft/models/item/leather_helmet.json | 0 .../models/item/leather_leggings.json | 0 .../assets/minecraft/models/item/shield.json | 0 .../minecraft/models/item/white_banner.json | 0 .../models/item/white_stained_glass_pane.json | 0 .../minecraft/items/crafting_table.json | 0 .../assets/minecraft/items/dropper.json | 0 .../assets/minecraft/items/leather_boots.json | 0 .../minecraft/items/leather_chestplate.json | 0 .../minecraft/items/leather_helmet.json | 0 .../minecraft/items/leather_leggings.json | 0 .../assets/minecraft/items/shield.json | 0 .../assets/minecraft/items/white_banner.json | 0 .../items/white_stained_glass_pane.json | 0 .../{ => openinv-legibility-pack}/pack.mcmeta | 0 .../{ => openinv-legibility-pack}/pack.png | Bin scripts/generate_changelog.sh | 82 --- scripts/get_spigot_versions.sh | 52 -- ...igot_dependencies.sh => install_spigot.sh} | 28 +- scripts/set_curseforge_env.sh | 16 +- scripts/set_release_env.sh | 32 -- scripts/tag_release.sh | 0 settings.gradle.kts | 31 ++ 131 files changed, 1153 insertions(+), 4503 deletions(-) create mode 100644 addon/togglepersist/build.gradle.kts delete mode 100644 addon/togglepersist/pom.xml create mode 100644 api/build.gradle.kts delete mode 100644 api/pom.xml create mode 100644 build.gradle.kts create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/openinv-base.gradle.kts create mode 100644 buildSrc/src/main/kotlin/remap-spigot.gradle.kts create mode 100644 common/build.gradle.kts delete mode 100644 common/pom.xml create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 internal/common/build.gradle.kts rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/InternalAccessor.java (83%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/AnySilentContainer.java (96%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/OpenEnderChest.java (83%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/OpenInventory.java (89%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/Placeholders.java (97%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/bukkit/OpenDummyInventory.java (96%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/bukkit/OpenDummyPlayerInventory.java (97%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/bukkit/OpenPlayerInventory.java (96%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/bukkit/OpenPlayerInventorySelf.java (79%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/menu/OpenChestMenu.java (94%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/menu/OpenEnderChestMenu.java (85%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/menu/OpenInventoryMenu.java (91%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/Content.java (96%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentCrafting.java (89%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentCraftingResult.java (90%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentCursor.java (88%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentDrop.java (82%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentEquipment.java (89%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentList.java (95%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentOffHand.java (86%) rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/ContentViewOnly.java (95%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/SlotPlaceholder.java (89%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/container/slot/SlotViewOnly.java (65%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/player/OpenPlayer.java (93%) rename internal/{v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2 => common/src/main/java/com/lishid/openinv/internal/common}/player/PlayerManager.java (91%) create mode 100644 internal/spigot/build.gradle.kts create mode 100644 internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java rename internal/{v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3 => spigot/src/main/java/com/lishid/openinv/internal/reobf}/player/PlayerManager.java (93%) delete mode 100644 internal/v1_21_R2/pom.xml delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java delete mode 100644 internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java delete mode 100644 internal/v1_21_R3/pom.xml delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java delete mode 100644 internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java create mode 100644 plugin/build.gradle.kts delete mode 100644 plugin/pom.xml delete mode 100644 pom.xml create mode 100644 resource-pack/build.gradle.kts rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/crafting_output.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/cursor.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/drop.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/empty_boots.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/empty_chestplate.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/empty_helmet.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/empty_leggings.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/empty_shield.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/models/item/not_a_slot.json (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/crafting_output.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/cursor.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/drop.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/empty_boots.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/empty_chestplate.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/empty_helmet.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/empty_leggings.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/empty_shield.png (100%) rename resource-pack/{ => openinv-legibility-pack}/assets/openinv/textures/item/not_a_slot.png (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/crafting_table.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/dropper.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/leather_boots.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/leather_chestplate.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/leather_helmet.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/leather_leggings.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/shield.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/white_banner.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/crafting_table.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/dropper.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/leather_boots.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/leather_chestplate.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/leather_helmet.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/leather_leggings.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/shield.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/white_banner.json (100%) rename resource-pack/{ => openinv-legibility-pack}/openinv_44/assets/minecraft/items/white_stained_glass_pane.json (100%) rename resource-pack/{ => openinv-legibility-pack}/pack.mcmeta (100%) rename resource-pack/{ => openinv-legibility-pack}/pack.png (100%) delete mode 100644 scripts/generate_changelog.sh delete mode 100644 scripts/get_spigot_versions.sh rename scripts/{install_spigot_dependencies.sh => install_spigot.sh} (54%) mode change 100644 => 100755 mode change 100644 => 100755 scripts/set_curseforge_env.sh delete mode 100644 scripts/set_release_env.sh mode change 100644 => 100755 scripts/tag_release.sh create mode 100644 settings.gradle.kts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fe7fd56..e7310eed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: tags-ignore: - '**' paths-ignore: - - resource-pack/** + - resource-pack/openinv-legibility-pack/** # Enable running CI via other Actions, i.e. for drafting releases and handling PRs. workflow_call: @@ -21,14 +21,15 @@ jobs: with: distribution: 'temurin' java-version: '21' - cache: 'maven' - # Install Spigot dependencies if necessary. - - name: Install Spigot Dependencies - run: . scripts/install_spigot_dependencies.sh + # TODO convert to a Gradle plugin later for portability + - name: Install Spigot dependency + run: ./scripts/install_spigot.sh 1.21.4 - - name: Build With Maven - run: mvn -e clean package -am -P all + - uses: gradle/actions/setup-gradle@v4 + + - name: Build with Gradle + run: ./gradlew build # Upload artifacts - name: Upload Distributable Jar @@ -36,10 +37,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: dist - path: ./target/*.jar - - name: Upload API Jar - id: upload-api - uses: actions/upload-artifact@v4 - with: - name: api - path: ./api/target/openinvapi*.jar + path: ./dist/* diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 93cf9e26..5e982467 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -12,26 +12,12 @@ jobs: needs: [ run-ci ] runs-on: ubuntu-latest steps: - # Fetch all history - used to assemble changelog. - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set Release Variables - run: bash ./scripts/set_release_env.sh - - name: Download build uses: actions/download-artifact@v4 with: name: dist path: dist - - name: Package resource pack - run: |- - pushd resource-pack - zip -r ../dist/openinv-legibility-pack.zip . - popd - - name: Create Release id: create-release uses: softprops/action-gh-release@v2.2.1 @@ -39,7 +25,18 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: name: ${{ env.VERSIONED_NAME }} - body: ${{ env.GENERATED_CHANGELOG }} + # May actually want to manually specify Paper versions all the time; they won't all + # need to be specified in code thanks to Mojang-mapped server internals. + # A bit annoying, but not terribly so. + # Instead, fetch min and max (which will also be Spigot version) and manually list intermediate versions? + body: |- + ## Supported server versions + ### Paper + TODO COMMA-SEPARATED VERSIONS + ### Spigot + TODO VERSION + + TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE. draft: true prerelease: false files: ./dist/** diff --git a/.github/workflows/external_release.yml b/.github/workflows/external_release.yml index 87a2d692..593239d9 100644 --- a/.github/workflows/external_release.yml +++ b/.github/workflows/external_release.yml @@ -34,4 +34,4 @@ jobs: game_versions: "${{ env.CURSEFORGE_MINECRAFT_VERSIONS }}" release_type: release changelog_type: markdown - changelog: "${{ env.CURSEFORGE_CHANGELOG }}" + changelog: "${{ github.event.release.body }}" diff --git a/.github/workflows/resource_pack_ci.yml b/.github/workflows/resource_pack_ci.yml index 557701ac..4b209ef8 100644 --- a/.github/workflows/resource_pack_ci.yml +++ b/.github/workflows/resource_pack_ci.yml @@ -7,7 +7,7 @@ on: tags-ignore: - '**' paths: - - resource-pack/** + - resource-pack/openinv-legibility-pack/** jobs: build: @@ -20,5 +20,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: openinv-legibility-pack - path: ./resource-pack/ + path: ./resource-pack/openinv-legibility-pack/ compression-level: 9 diff --git a/.gitignore b/.gitignore index b48a4778..27ac0af7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,12 @@ **/.project **/.classpath **/.idea/ +**/.gradle/ **.iml **/target/ **/bin/ **/out/ +**/build/ +**/dist/ **/dependency-reduced-pom.xml **/pom.xml.versionsBackup diff --git a/README.MD b/README.MD index 7354892e..3126f273 100644 --- a/README.MD +++ b/README.MD @@ -36,22 +36,19 @@ The OpenInv API is available via [JitPack](https://jitpack.io/). ```xml - com.github.jikoo.OpenInv - openinvapi + com.github.Jikoo + OpenInv ${openinv.version} ``` +Note that since JitPack only builds the API now, the "full" OpenInv jar on JitPack is actually the openinvapi artifact. This is a change from previous dependency declaration that I hope to revert. ### Compilation -To compile, the relevant Spigot jars must be installed in the local repository. +To compile, the relevant Spigot jar must be installed in the local repository. As OpenInv is compiled against Mojang's mappings, you must run BuildTools with the `--remapped` argument: `java -jar BuildTools.jar --remapped --rev $serverVersion` -`$serverVersion` is the version of the server, i.e. `1.18.1` +`$serverVersion` is the version of the server, i.e. `1.21.4` -To compile just the API, execute Maven as usual: -`mvn clean package` - -To compile the full plugin, use the provided profile with `-P all`: -`mvn clean package -am -P all` -For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html). +Once Spigot is installed, execute the gradle wrapper: +`./gradlew build` diff --git a/addon/togglepersist/build.gradle.kts b/addon/togglepersist/build.gradle.kts new file mode 100644 index 00000000..78cb9095 --- /dev/null +++ b/addon/togglepersist/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + `openinv-base` +} + +dependencies { + implementation(project(":openinvapi")) +} + +tasks.processResources { + expand("version" to version) +} + +tasks.register("distributeAddons") { + into(rootProject.layout.projectDirectory.dir("dist")) + from(tasks.jar) + rename("openinvtogglepersist.*\\.jar", "OITogglePersist.jar") +} + +tasks.assemble { + dependsOn(tasks.named("distributeAddons")) +} diff --git a/addon/togglepersist/pom.xml b/addon/togglepersist/pom.xml deleted file mode 100644 index e927bf53..00000000 --- a/addon/togglepersist/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 5.1.7-SNAPSHOT - - - openinvtogglepersist - - - - annotations - org.jetbrains - - - org.spigotmc - spigot-api - - - openinvapi - com.lishid - provided - - - - - OITogglePersist - - - src/main/resources - true - - - - - - maven-compiler-plugin - - - maven-resources-plugin - - - copy-final-jar - package - - copy-resources - - - ${project.parent.build.directory} - - - ${project.build.directory} - - ${project.build.finalName}.jar - - - - - - - - - - - - diff --git a/addon/togglepersist/src/main/resources/plugin.yml b/addon/togglepersist/src/main/resources/plugin.yml index 50a77051..3760e30f 100644 --- a/addon/togglepersist/src/main/resources/plugin.yml +++ b/addon/togglepersist/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: OITogglePersist main: com.github.jikoo.openinv.togglepersist.TogglePersist -version: ${project.version} +version: ${version} author: Jikoo description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions. api-version: "1.20" diff --git a/api/build.gradle.kts b/api/build.gradle.kts new file mode 100644 index 00000000..0731f2f3 --- /dev/null +++ b/api/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + `openinv-base` + `maven-publish` +} + +publishing { + publications { + create("jitpack") { + groupId = "com.github.Jikoo.OpenInv" + artifactId = "openinvapi" + } + } +} diff --git a/api/pom.xml b/api/pom.xml deleted file mode 100644 index 8f93172e..00000000 --- a/api/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - 4.0.0 - - - openinvparent - com.lishid - 5.1.7-SNAPSHOT - - - openinvapi - OpenInvAPI - - - - annotations - org.jetbrains - - - spigot-api - org.spigotmc - - - - - - - maven-compiler-plugin - - - - - diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..7b53201e --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + `java-library` + alias(libs.plugins.paperweight) apply false + alias(libs.plugins.shadow) apply false +} + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") +} + +// Allow submodules to target higher Java release versions. +// Not currently necessary (as lowest supported version is in the 1.21 range) +// but may become relevant in the future. +java.disableAutoTargetJvm() + +// Task to delete ./dist where final files are output. +tasks.register("cleanDist") { + delete("dist") +} + +tasks.clean { + // Also delete distribution folder when cleaning. + dependsOn(tasks.named("cleanDist")) +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..52b9cc0a --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} diff --git a/buildSrc/src/main/kotlin/openinv-base.gradle.kts b/buildSrc/src/main/kotlin/openinv-base.gradle.kts new file mode 100644 index 00000000..9ef5574d --- /dev/null +++ b/buildSrc/src/main/kotlin/openinv-base.gradle.kts @@ -0,0 +1,34 @@ +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.kotlin.dsl.maven +import org.gradle.kotlin.dsl.repositories +import org.gradle.kotlin.dsl.dependencies + +plugins { + `java-library` +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(21) +} + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://hub.spigotmc.org/nexus/content/groups/public/") +} + +dependencies { + val libs = versionCatalogs.named("libs") + compileOnly(libs.findLibrary("annotations").orElseThrow()) + compileOnly(libs.findLibrary("spigotapi").orElseThrow()) +} + +tasks { + withType().configureEach { + options.release = 21 + options.encoding = Charsets.UTF_8.name() + } + withType().configureEach { + options.encoding = Charsets.UTF_8.name() + } +} diff --git a/buildSrc/src/main/kotlin/remap-spigot.gradle.kts b/buildSrc/src/main/kotlin/remap-spigot.gradle.kts new file mode 100644 index 00000000..a1a6fab9 --- /dev/null +++ b/buildSrc/src/main/kotlin/remap-spigot.gradle.kts @@ -0,0 +1,55 @@ +import java.nio.file.Path +import java.nio.file.Paths + +val spigotRemap = configurations.create("spigotRemap") + +repositories { + mavenCentral() +} + +dependencies { + spigotRemap("net.md-5:SpecialSource:1.11.4:shaded") +} + +abstract class RemapTask + @Inject constructor(private val project: Project): DefaultTask() { + + @get:Input + abstract var spigotVersion: String + + @TaskAction + fun remapInputs() { + inputs.files.forEach { + remap(spigotVersion, it.toPath(), it.toPath().resolveSibling(it.name.replace(".jar", "-obf.jar"))) + } + } + + private fun remap(spigotVersion: String, jarPath: Path, obfPath: Path) { + // https://www.spigotmc.org/threads/510208/#post-4184317 + val specialsource = project.configurations.named("spigotRemap").get().incoming.artifacts.artifacts + .first { it.id.componentIdentifier.toString() == "net.md-5:SpecialSource:1.11.4" }.file.path + val repo = Paths.get(project.repositories.mavenLocal().url) + val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVersion/") + val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVersion/") + + // Remap original Mojang-mapped jar to obfuscated intermediary + val mojangServer = spigotDir.resolve("spigot-$spigotVersion-remapped-mojang.jar") + val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVersion-maps-mojang.txt") + remapPartial(specialsource, mojangServer, mojangMappings, jarPath, obfPath, true) + + // Remap obfuscated intermediary jar to Spigot and replace original + val obfServer = spigotDir.resolve("spigot-$spigotVersion-remapped-obf.jar") + val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVersion-maps-spigot.csrg") + remapPartial(specialsource, obfServer, spigotMappings, obfPath, jarPath, false) + } + + private fun remapPartial(specialSource: String, serverJar: Path, mapping: Path, input: Path, output: Path, reverse: Boolean) { + project.providers.exec { + commandLine("java", "-cp", "$specialSource${File.pathSeparator}$serverJar", + "net.md_5.specialsource.SpecialSource", "--live", + "-i", "$input", "-o", "$output", + "-m", "$mapping", + if (reverse) "--reverse" else "") + }.result.get() + } +} diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 00000000..1be01183 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + `openinv-base` +} + +dependencies { + implementation(project(":openinvapi")) +} diff --git a/common/pom.xml b/common/pom.xml deleted file mode 100644 index b8d9a944..00000000 --- a/common/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - 4.0.0 - - - com.lishid - openinvparent - 5.1.7-SNAPSHOT - - - openinvcommon - - - - annotations - org.jetbrains - - - org.spigotmc - spigot-api - - - openinvapi - com.lishid - - - - diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..72adefbb --- /dev/null +++ b/gradle.properties @@ -0,0 +1,9 @@ +# Project meta +group = com.lishid.openinv +version = 5.1.7-SNAPSHOT +description = A Bukkit plugin for opening normally-inaccessible inventories. + +# Gradle configuration +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..5c5a9257 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,19 @@ +[versions] +spigotapi = "1.21.4-R0.1-SNAPSHOT" +spigot = "1.21.4-R0.1-SNAPSHOT" +specialsource = "1.11.4" +planarwrappers = "3.3.0" +annotations = "26.0.2" +paperweight = "2.0.0-beta.14" +shadow = "8.3.5" + +[libraries] +spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } +spigot = { module = "org.spigotmc:spigot", version.ref = "spigot" } +specialsource = { module = "net.md-5:SpecialSource", version.ref = "specialsource" } +planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" } +annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } + +[plugins] +paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" } +shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b GIT binary patch literal 59821 zcma&NV|1p`(k7gaZQHhOJ9%QKV?D8LCmq{1JGRYE(y=?XJw0>InKkE~^UnAEs2gk5 zUVGPCwX3dOb!}xiFmPB95NK!+5D<~S0s;d1zn&lrfAn7 zC?Nb-LFlib|DTEqB8oDS5&$(u1<5;wsY!V`2F7^=IR@I9so5q~=3i_(hqqG<9SbL8Q(LqDrz+aNtGYWGJ2;p*{a-^;C>BfGzkz_@fPsK8{pTT~_VzB$E`P@> z7+V1WF2+tSW=`ZRj3&0m&d#x_lfXq`bb-Y-SC-O{dkN2EVM7@!n|{s+2=xSEMtW7( zz~A!cBpDMpQu{FP=y;sO4Le}Z)I$wuFwpugEY3vEGfVAHGqZ-<{vaMv-5_^uO%a{n zE_Zw46^M|0*dZ`;t%^3C19hr=8FvVdDp1>SY>KvG!UfD`O_@weQH~;~W=fXK_!Yc> z`EY^PDJ&C&7LC;CgQJeXH2 zjfM}2(1i5Syj)Jj4EaRyiIl#@&lC5xD{8hS4Wko7>J)6AYPC-(ROpVE-;|Z&u(o=X z2j!*>XJ|>Lo+8T?PQm;SH_St1wxQPz)b)Z^C(KDEN$|-6{A>P7r4J1R-=R7|FX*@! zmA{Ja?XE;AvisJy6;cr9Q5ovphdXR{gE_7EF`ji;n|RokAJ30Zo5;|v!xtJr+}qbW zY!NI6_Wk#6pWFX~t$rAUWi?bAOv-oL6N#1>C~S|7_e4 zF}b9(&a*gHk+4@J26&xpiWYf2HN>P;4p|TD4f586umA2t@cO1=Fx+qd@1Ae#Le>{-?m!PnbuF->g3u)7(n^llJfVI%Q2rMvetfV5 z6g|sGf}pV)3_`$QiKQnqQ<&ghOWz4_{`rA1+7*M0X{y(+?$|{n zs;FEW>YzUWg{sO*+D2l6&qd+$JJP_1Tm;To<@ZE%5iug8vCN3yH{!6u5Hm=#3HJ6J zmS(4nG@PI^7l6AW+cWAo9sFmE`VRcM`sP7X$^vQY(NBqBYU8B|n-PrZdNv8?K?kUTT3|IE`-A8V*eEM2=u*kDhhKsmVPWGns z8QvBk=BPjvu!QLtlF0qW(k+4i+?H&L*qf262G#fks9}D5-L{yiaD10~a;-j!p!>5K zl@Lh+(9D{ePo_S4F&QXv|q_yT`GIPEWNHDD8KEcF*2DdZD;=J6u z|8ICSoT~5Wd!>g%2ovFh`!lTZhAwpIbtchDc{$N%<~e$E<7GWsD42UdJh1fD($89f2on`W`9XZJmr*7lRjAA8K0!(t8-u>2H*xn5cy1EG{J;w;Q-H8Yyx+WW(qoZZM7p(KQx^2-yI6Sw?k<=lVOVwYn zY*eDm%~=|`c{tUupZ^oNwIr!o9T;H3Fr|>NE#By8SvHb&#;cyBmY1LwdXqZwi;qn8 zK+&z{{95(SOPXAl%EdJ3jC5yV^|^}nOT@M0)|$iOcq8G{#*OH7=DlfOb; z#tRO#tcrc*yQB5!{l5AF3(U4>e}nEvkoE_XCX=a3&A6Atwnr&`r&f2d%lDr8f?hBB zr1dKNypE$CFbT9I?n){q<1zHmY>C=5>9_phi79pLJG)f=#dKdQ7We8emMjwR*qIMF zE_P-T*$hX#FUa%bjv4Vm=;oxxv`B*`weqUn}K=^TXjJG=UxdFMSj-QV6fu~;- z|IsUq`#|73M%Yn;VHJUbt<0UHRzbaF{X@76=8*-IRx~bYgSf*H(t?KH=?D@wk*E{| z2@U%jKlmf~C^YxD=|&H?(g~R9-jzEb^y|N5d`p#2-@?BUcHys({pUz4Zto7XwKq2X zSB~|KQGgv_Mh@M!*{nl~2~VV_te&E7K39|WYH zCxfd|v_4!h$Ps2@atm+gj14Ru)DhivY&(e_`eA)!O1>nkGq|F-#-6oo5|XKEfF4hR z%{U%ar7Z8~B!foCd_VRHr;Z1c0Et~y8>ZyVVo9>LLi(qb^bxVkbq-Jq9IF7!FT`(- zTMrf6I*|SIznJLRtlP)_7tQ>J`Um>@pP=TSfaPB(bto$G1C zx#z0$=zNpP-~R);kM4O)9Mqn@5Myv5MmmXOJln312kq#_94)bpSd%fcEo7cD#&|<` zrcal$(1Xv(nDEquG#`{&9Ci~W)-zd_HbH-@2F6+|a4v}P!w!Q*h$#Zu+EcZeY>u&?hn#DCfC zVuye5@Ygr+T)0O2R1*Hvlt>%rez)P2wS}N-i{~IQItGZkp&aeY^;>^m7JT|O^{`78 z$KaK0quwcajja;LU%N|{`2o&QH@u%jtH+j!haGj;*ZCR*`UgOXWE>qpXqHc?g&vA& zt-?_g8k%ZS|D;()0Lf!>7KzTSo-8hUh%OA~i76HKRLudaNiwo*E9HxmzN4y>YpZNO zUE%Q|H_R_UmX=*f=2g=xyP)l-DP}kB@PX|(Ye$NOGN{h+fI6HVw`~Cd0cKqO;s6aiYLy7sl~%gs`~XaL z^KrZ9QeRA{O*#iNmB7_P!=*^pZiJ5O@iE&X2UmUCPz!)`2G3)5;H?d~3#P|)O(OQ_ zua+ZzwWGkWflk4j^Lb=x56M75_p9M*Q50#(+!aT01y80x#rs9##!;b-BH?2Fu&vx} za%4!~GAEDsB54X9wCF~juV@aU}fp_(a<`Ig0Pip8IjpRe#BR?-niYcz@jI+QY zBU9!8dAfq@%p;FX)X=E7?B=qJJNXlJ&7FBsz;4&|*z{^kEE!XbA)(G_O6I9GVzMAF z8)+Un(6od`W7O!!M=0Z)AJuNyN8q>jNaOdC-zAZ31$Iq%{c_SYZe+(~_R`a@ zOFiE*&*o5XG;~UjsuW*ja-0}}rJdd@^VnQD!z2O~+k-OSF%?hqcFPa4e{mV1UOY#J zTf!PM=KMNAzbf(+|AL%K~$ahX0Ol zbAxKu3;v#P{Qia{_WzHl`!@!8c#62XSegM{tW1nu?Ee{sQq(t{0TSq67YfG;KrZ$n z*$S-+R2G?aa*6kRiTvVxqgUhJ{ASSgtepG3hb<3hlM|r>Hr~v_DQ>|Nc%&)r0A9go z&F3Ao!PWKVq~aWOzLQIy&R*xo>}{UTr}?`)KS&2$3NR@a+>+hqK*6r6Uu-H};ZG^| zfq_Vl%YE1*uGwtJ>H*Y(Q9E6kOfLJRlrDNv`N;jnag&f<4#UErM0ECf$8DASxMFF& zK=mZgu)xBz6lXJ~WZR7OYw;4&?v3Kk-QTs;v1r%XhgzSWVf|`Sre2XGdJb}l1!a~z zP92YjnfI7OnF@4~g*LF>G9IZ5c+tifpcm6#m)+BmnZ1kz+pM8iUhwag`_gqr(bnpy zl-noA2L@2+?*7`ZO{P7&UL~ahldjl`r3=HIdo~Hq#d+&Q;)LHZ4&5zuDNug@9-uk; z<2&m#0Um`s=B}_}9s&70Tv_~Va@WJ$n~s`7tVxi^s&_nPI0`QX=JnItlOu*Tn;T@> zXsVNAHd&K?*u~a@u8MWX17VaWuE0=6B93P2IQ{S$-WmT+Yp!9eA>@n~=s>?uDQ4*X zC(SxlKap@0R^z1p9C(VKM>nX8-|84nvIQJ-;9ei0qs{}X>?f%&E#%-)Bpv_p;s4R+ z;PMpG5*rvN&l;i{^~&wKnEhT!S!LQ>udPzta#Hc9)S8EUHK=%x+z@iq!O{)*XM}aI zBJE)vokFFXTeG<2Pq}5Na+kKnu?Ch|YoxdPb&Z{07nq!yzj0=xjzZj@3XvwLF0}Pa zn;x^HW504NNfLY~w!}5>`z=e{nzGB>t4ntE>R}r7*hJF3OoEx}&6LvZz4``m{AZxC zz6V+^73YbuY>6i9ulu)2`ozP(XBY5n$!kiAE_Vf4}Ih)tlOjgF3HW|DF+q-jI_0p%6Voc^e;g28* z;Sr4X{n(X7eEnACWRGNsHqQ_OfWhAHwnSQ87@PvPcpa!xr9`9+{QRn;bh^jgO8q@v zLekO@-cdc&eOKsvXs-eMCH8Y{*~3Iy!+CANy+(WXYS&6XB$&1+tB?!qcL@@) zS7XQ|5=o1fr8yM7r1AyAD~c@Mo`^i~hjx{N17%pDX?j@2bdBEbxY}YZxz!h#)q^1x zpc_RnoC3`V?L|G2R1QbR6pI{Am?yW?4Gy`G-xBYfebXvZ=(nTD7u?OEw>;vQICdPJBmi~;xhVV zisVvnE!bxI5|@IIlDRolo_^tc1{m)XTbIX^<{TQfsUA1Wv(KjJED^nj`r!JjEA%MaEGqPB z9YVt~ol3%e`PaqjZt&-)Fl^NeGmZ)nbL;92cOeLM2H*r-zA@d->H5T_8_;Jut0Q_G zBM2((-VHy2&eNkztIpHk&1H3M3@&wvvU9+$RO%fSEa_d5-qZ!<`-5?L9lQ1@AEpo* z3}Zz~R6&^i9KfRM8WGc6fTFD%PGdruE}`X$tP_*A)_7(uI5{k|LYc-WY*%GJ6JMmw zNBT%^E#IhekpA(i zcB$!EB}#>{^=G%rQ~2;gbObT9PQ{~aVx_W6?(j@)S$&Ja1s}aLT%A*mP}NiG5G93- z_DaRGP77PzLv0s32{UFm##C2LsU!w{vHdKTM1X)}W%OyZ&{3d^2Zu-zw?fT=+zi*q z^fu6CXQ!i?=ljsqSUzw>g#PMk>(^#ejrYp(C)7+@Z1=Mw$Rw!l8c9}+$Uz;9NUO(kCd#A1DX4Lbis0k; z?~pO(;@I6Ajp}PL;&`3+;OVkr3A^dQ(j?`by@A!qQam@_5(w6fG>PvhO`#P(y~2ue zW1BH_GqUY&>PggMhhi@8kAY;XWmj>y1M@c`0v+l~l0&~Kd8ZSg5#46wTLPo*Aom-5 z>qRXyWl}Yda=e@hJ%`x=?I42(B0lRiR~w>n6p8SHN~B6Y>W(MOxLpv>aB)E<1oEcw z%X;#DJpeDaD;CJRLX%u!t23F|cv0ZaE183LXxMq*uWn)cD_ zp!@i5zsmcxb!5uhp^@>U;K>$B|8U@3$65CmhuLlZ2(lF#hHq-<<+7ZN9m3-hFAPgA zKi;jMBa*59ficc#TRbH_l`2r>z(Bm_XEY}rAwyp~c8L>{A<0@Q)j*uXns^q5z~>KI z)43=nMhcU1ZaF;CaBo>hl6;@(2#9yXZ7_BwS4u>gN%SBS<;j{{+p}tbD8y_DFu1#0 zx)h&?`_`=ti_6L>VDH3>PPAc@?wg=Omdoip5j-2{$T;E9m)o2noyFW$5dXb{9CZ?c z);zf3U526r3Fl+{82!z)aHkZV6GM@%OKJB5mS~JcDjieFaVn}}M5rtPnHQVw0Stn- zEHs_gqfT8(0b-5ZCk1%1{QQaY3%b>wU z7lyE?lYGuPmB6jnMI6s$1uxN{Tf_n7H~nKu+h7=%60WK-C&kEIq_d4`wU(*~rJsW< zo^D$-(b0~uNVgC+$J3MUK)(>6*k?92mLgpod{Pd?{os+yHr&t+9ZgM*9;dCQBzE!V zk6e6)9U6Bq$^_`E1xd}d;5O8^6?@bK>QB&7l{vAy^P6FOEO^l7wK4K=lLA45gQ3$X z=$N{GR1{cxO)j;ZxKI*1kZIT9p>%FhoFbRK;M(m&bL?SaN zzkZS9xMf={o@gpG%wE857u@9dq>UKvbaM1SNtMA9EFOp7$BjJQVkIm$wU?-yOOs{i z1^(E(WwZZG{_#aIzfpGc@g5-AtK^?Q&vY#CtVpfLbW?g0{BEX4Vlk(`AO1{-D@31J zce}#=$?Gq+FZG-SD^z)-;wQg9`qEO}Dvo+S9*PUB*JcU)@S;UVIpN7rOqXmEIerWo zP_lk!@RQvyds&zF$Rt>N#_=!?5{XI`Dbo0<@>fIVgcU*9Y+ z)}K(Y&fdgve3ruT{WCNs$XtParmvV;rjr&R(V&_#?ob1LzO0RW3?8_kSw)bjom#0; zeNllfz(HlOJw012B}rgCUF5o|Xp#HLC~of%lg+!pr(g^n;wCX@Yk~SQOss!j9f(KL zDiI1h#k{po=Irl)8N*KU*6*n)A8&i9Wf#7;HUR^5*6+Bzh;I*1cICa|`&`e{pgrdc zs}ita0AXb$c6{tu&hxmT0faMG0GFc)unG8tssRJd%&?^62!_h_kn^HU_kBgp$bSew zqu)M3jTn;)tipv9Wt4Ll#1bmO2n?^)t^ZPxjveoOuK89$oy4(8Ujw{nd*Rs*<+xFi z{k*9v%sl?wS{aBSMMWdazhs0#gX9Has=pi?DhG&_0|cIyRG7c`OBiVG6W#JjYf7-n zIQU*Jc+SYnI8oG^Q8So9SP_-w;Y00$p5+LZ{l+81>v7|qa#Cn->312n=YQd$PaVz8 zL*s?ZU*t-RxoR~4I7e^c!8TA4g>w@R5F4JnEWJpy>|m5la2b#F4d*uoz!m=i1;`L` zB(f>1fAd~;*wf%GEbE8`EA>IO9o6TdgbIC%+en!}(C5PGYqS0{pa?PD)5?ds=j9{w za9^@WBXMZ|D&(yfc~)tnrDd#*;u;0?8=lh4%b-lFPR3ItwVJp};HMdEw#SXg>f-zU zEiaj5H=jzRSy(sWVd%hnLZE{SUj~$xk&TfheSch#23)YTcjrB+IVe0jJqsdz__n{- zC~7L`DG}-Dgrinzf7Jr)e&^tdQ}8v7F+~eF*<`~Vph=MIB|YxNEtLo1jXt#9#UG5` zQ$OSk`u!US+Z!=>dGL>%i#uV<5*F?pivBH@@1idFrzVAzttp5~>Y?D0LV;8Yv`wAa{hewVjlhhBM z_mJhU9yWz9Jexg@G~dq6EW5^nDXe(sU^5{}qbd0*yW2Xq6G37f8{{X&Z>G~dUGDFu zgmsDDZZ5ZmtiBw58CERFPrEG>*)*`_B75!MDsOoK`T1aJ4GZ1avI?Z3OX|Hg?P(xy zSPgO$alKZuXd=pHP6UZy0G>#BFm(np+dekv0l6gd=36FijlT8^kI5; zw?Z*FPsibF2d9T$_L@uX9iw*>y_w9HSh8c=Rm}f>%W+8OS=Hj_wsH-^actull3c@!z@R4NQ4qpytnwMaY z)>!;FUeY?h2N9tD(othc7Q=(dF zZAX&Y1ac1~0n(z}!9{J2kPPnru1?qteJPvA2m!@3Zh%+f1VQt~@leK^$&ZudOpS!+ zw#L0usf!?Df1tB?9=zPZ@q2sG!A#9 zKZL`2cs%|Jf}wG=_rJkwh|5Idb;&}z)JQuMVCZSH9kkG%zvQO01wBN)c4Q`*xnto3 zi7TscilQ>t_SLij{@Fepen*a(`upw#RJAx|JYYXvP1v8f)dTHv9pc3ZUwx!0tOH?c z^Hn=gfjUyo!;+3vZhxNE?LJgP`qYJ`J)umMXT@b z{nU(a^xFfofcxfHN-!Jn*{Dp5NZ&i9#9r{)s^lUFCzs5LQL9~HgxvmU#W|iNs0<3O z%Y2FEgvts4t({%lfX1uJ$w{JwfpV|HsO{ZDl2|Q$-Q?UJd`@SLBsMKGjFFrJ(s?t^ z2Llf`deAe@YaGJf)k2e&ryg*m8R|pcjct@rOXa=64#V9!sp=6tC#~QvYh&M~zmJ;% zr*A}V)Ka^3JE!1pcF5G}b&jdrt;bM^+J;G^#R08x@{|ZWy|547&L|k6)HLG|sN<~o z?y`%kbfRN_vc}pwS!Zr}*q6DG7;be0qmxn)eOcD%s3Wk`=@GM>U3ojhAW&WRppi0e zudTj{ufwO~H7izZJmLJD3uPHtjAJvo6H=)&SJ_2%qRRECN#HEU_RGa(Pefk*HIvOH zW7{=Tt(Q(LZ6&WX_Z9vpen}jqge|wCCaLYpiw@f_%9+-!l{kYi&gT@Cj#D*&rz1%e z@*b1W13bN8^j7IpAi$>`_0c!aVzLe*01DY-AcvwE;kW}=Z{3RJLR|O~^iOS(dNEnL zJJ?Dv^ab++s2v!4Oa_WFDLc4fMspglkh;+vzg)4;LS{%CR*>VwyP4>1Tly+!fA-k? z6$bg!*>wKtg!qGO6GQ=cAmM_RC&hKg$~(m2LdP{{*M+*OVf07P$OHp*4SSj9H;)1p z^b1_4p4@C;8G7cBCB6XC{i@vTB3#55iRBZiml^jc4sYnepCKUD+~k}TiuA;HWC6V3 zV{L5uUAU9CdoU+qsFszEwp;@d^!6XnX~KI|!o|=r?qhs`(-Y{GfO4^d6?8BC0xonf zKtZc1C@dNu$~+p#m%JW*J7alfz^$x`U~)1{c7svkIgQ3~RK2LZ5;2TAx=H<4AjC8{ z;)}8OfkZy7pSzVsdX|wzLe=SLg$W1+`Isf=o&}npxWdVR(i8Rr{uzE516a@28VhVr zVgZ3L&X(Q}J0R2{V(}bbNwCDD5K)<5h9CLM*~!xmGTl{Mq$@;~+|U*O#nc^oHnFOy z9Kz%AS*=iTBY_bSZAAY6wXCI?EaE>8^}WF@|}O@I#i69ljjWQPBJVk zQ_rt#J56_wGXiyItvAShJpLEMtW_)V5JZAuK#BAp6bV3K;IkS zK0AL(3ia99!vUPL#j>?<>mA~Q!mC@F-9I$9Z!96ZCSJO8FDz1SP3gF~m`1c#y!efq8QN}eHd+BHwtm%M5586jlU8&e!CmOC z^N_{YV$1`II$~cTxt*dV{-yp61nUuX5z?N8GNBuZZR}Uy_Y3_~@Y3db#~-&0TX644OuG^D3w_`?Yci{gTaPWST8`LdE)HK5OYv>a=6B%R zw|}>ngvSTE1rh`#1Rey0?LXTq;bCIy>TKm^CTV4BCSqdpx1pzC3^ca*S3fUBbKMzF z6X%OSdtt50)yJw*V_HE`hnBA)1yVN3Ruq3l@lY;%Bu+Q&hYLf_Z@fCUVQY-h4M3)- zE_G|moU)Ne0TMjhg?tscN7#ME6!Rb+y#Kd&-`!9gZ06o3I-VX1d4b1O=bpRG-tDK0 zSEa9y46s7QI%LmhbU3P`RO?w#FDM(}k8T`&>OCU3xD=s5N7}w$GntXF;?jdVfg5w9OR8VPxp5{uw zD+_;Gb}@7Vo_d3UV7PS65%_pBUeEwX_Hwfe2e6Qmyq$%0i8Ewn%F7i%=CNEV)Qg`r|&+$ zP6^Vl(MmgvFq`Zb715wYD>a#si;o+b4j^VuhuN>+sNOq6Qc~Y;Y=T&!Q4>(&^>Z6* zwliz!_16EDLTT;v$@W(s7s0s zi*%p>q#t)`S4j=Ox_IcjcllyT38C4hr&mlr6qX-c;qVa~k$MG;UqdnzKX0wo0Xe-_)b zrHu1&21O$y5828UIHI@N;}J@-9cpxob}zqO#!U%Q*ybZ?BH#~^fOT_|8&xAs_rX24 z^nqn{UWqR?MlY~klh)#Rz-*%&e~9agOg*fIN`P&v!@gcO25Mec23}PhzImkdwVT|@ zFR9dYYmf&HiUF4xO9@t#u=uTBS@k*97Z!&hu@|xQnQDkLd!*N`!0JN7{EUoH%OD85 z@aQ2(w-N)1_M{;FV)C#(a4p!ofIA3XG(XZ2E#%j_(=`IWlJAHWkYM2&(+yY|^2TB0 z>wfC-+I}`)LFOJ%KeBb1?eNxGKeq?AI_eBE!M~$wYR~bB)J3=WvVlT8ZlF2EzIFZt zkaeyj#vmBTGkIL9mM3cEz@Yf>j=82+KgvJ-u_{bBOxE5zoRNQW3+Ahx+eMGem|8xo zL3ORKxY_R{k=f~M5oi-Z>5fgqjEtzC&xJEDQ@`<)*Gh3UsftBJno-y5Je^!D?Im{j za*I>RQ=IvU@5WKsIr?kC$DT+2bgR>8rOf3mtXeMVB~sm%X7W5`s=Tp>FR544tuQ>9qLt|aUSv^io&z93luW$_OYE^sf8DB?gx z4&k;dHMWph>Z{iuhhFJr+PCZ#SiZ9e5xM$A#0yPtVC>yk&_b9I676n|oAH?VeTe*1 z@tDK}QM-%J^3Ns6=_vh*I8hE?+=6n9nUU`}EX|;Mkr?6@NXy8&B0i6h?7%D=%M*Er zivG61Wk7e=v;<%t*G+HKBqz{;0Biv7F+WxGirONRxJij zon5~(a`UR%uUzfEma99QGbIxD(d}~oa|exU5Y27#4k@N|=hE%Y?Y3H%rcT zHmNO#ZJ7nPHRG#y-(-FSzaZ2S{`itkdYY^ZUvyw<7yMBkNG+>$Rfm{iN!gz7eASN9-B3g%LIEyRev|3)kSl;JL zX7MaUL_@~4ot3$woD0UA49)wUeu7#lj77M4ar8+myvO$B5LZS$!-ZXw3w;l#0anYz zDc_RQ0Ome}_i+o~H=CkzEa&r~M$1GC!-~WBiHiDq9Sdg{m|G?o7g`R%f(Zvby5q4; z=cvn`M>RFO%i_S@h3^#3wImmWI4}2x4skPNL9Am{c!WxR_spQX3+;fo!y(&~Palyjt~Xo0uy6d%sX&I`e>zv6CRSm)rc^w!;Y6iVBb3x@Y=`hl9jft zXm5vilB4IhImY5b->x{!MIdCermpyLbsalx8;hIUia%*+WEo4<2yZ6`OyG1Wp%1s$ zh<|KrHMv~XJ9dC8&EXJ`t3ETz>a|zLMx|MyJE54RU(@?K&p2d#x?eJC*WKO9^d17# zdTTKx-Os3k%^=58Sz|J28aCJ}X2-?YV3T7ee?*FoDLOC214J4|^*EX`?cy%+7Kb3(@0@!Q?p zk>>6dWjF~y(eyRPqjXqDOT`4^Qv-%G#Zb2G?&LS-EmO|ixxt79JZlMgd^~j)7XYQ; z62rGGXA=gLfgy{M-%1gR87hbhxq-fL)GSfEAm{yLQP!~m-{4i_jG*JsvUdqAkoc#q6Yd&>=;4udAh#?xa2L z7mFvCjz(hN7eV&cyFb%(U*30H@bQ8-b7mkm!=wh2|;+_4vo=tyHPQ0hL=NR`jbsSiBWtG ztMPPBgHj(JTK#0VcP36Z`?P|AN~ybm=jNbU=^3dK=|rLE+40>w+MWQW%4gJ`>K!^- zx4kM*XZLd(E4WsolMCRsdvTGC=37FofIyCZCj{v3{wqy4OXX-dZl@g`Dv>p2`l|H^ zS_@(8)7gA62{Qfft>vx71stILMuyV4uKb7BbCstG@|e*KWl{P1$=1xg(7E8MRRCWQ1g)>|QPAZot~|FYz_J0T+r zTWTB3AatKyUsTXR7{Uu) z$1J5SSqoJWt(@@L5a)#Q6bj$KvuC->J-q1!nYS6K5&e7vNdtj- zj9;qwbODLgIcObqNRGs1l{8>&7W?BbDd!87=@YD75B2ep?IY|gE~t)$`?XJ45MG@2 zz|H}f?qtEb_p^Xs$4{?nA=Qko3Lc~WrAS`M%9N60FKqL7XI+v_5H-UDiCbRm`fEmv z$pMVH*#@wQqml~MZe+)e4Ts3Gl^!Z0W3y$;|9hI?9(iw29b7en0>Kt2pjFXk@!@-g zTb4}Kw!@u|V!wzk0|qM*zj$*-*}e*ZXs#Y<6E_!BR}3^YtjI_byo{F+w9H9?f%mnBh(uE~!Um7)tgp2Ye;XYdVD95qt1I-fc@X zXHM)BfJ?^g(s3K|{N8B^hamrWAW|zis$`6|iA>M-`0f+vq(FLWgC&KnBDsM)_ez1# zPCTfN8{s^K`_bum2i5SWOn)B7JB0tzH5blC?|x;N{|@ch(8Uy-O{B2)OsfB$q0@FR z27m3YkcVi$KL;;4I*S;Z#6VfZcZFn!D2Npv5pio)sz-`_H*#}ROd7*y4i(y(YlH<4 zh4MmqBe^QV_$)VvzWgMXFy`M(vzyR2u!xx&%&{^*AcVLrGa8J9ycbynjKR~G6zC0e zlEU>zt7yQtMhz>XMnz>ewXS#{Bulz$6HETn?qD5v3td>`qGD;Y8&RmkvN=24=^6Q@DYY zxMt}uh2cSToMkkIWo1_Lp^FOn$+47JXJ*#q=JaeiIBUHEw#IiXz8cStEsw{UYCA5v_%cF@#m^Y!=+qttuH4u}r6gMvO4EAvjBURtLf& z6k!C|OU@hv_!*qear3KJ?VzVXDKqvKRtugefa7^^MSWl0fXXZR$Xb!b6`eY4A1#pk zAVoZvb_4dZ{f~M8fk3o?{xno^znH1t;;E6K#9?erW~7cs%EV|h^K>@&3Im}c7nm%Y zbLozFrwM&tSNp|46)OhP%MJ(5PydzR>8)X%i3!^L%3HCoCF#Y0#9vPI5l&MK*_ z6G8Y>$`~c)VvQle_4L_AewDGh@!bKkJeEs_NTz(yilnM!t}7jz>fmJb89jQo6~)%% z@GNIJ@AShd&K%UdQ5vR#yT<-goR+D@Tg;PuvcZ*2AzSWN&wW$Xc+~vW)pww~O|6hL zBxX?hOyA~S;3rAEfI&jmMT4f!-eVm%n^KF_QT=>!A<5tgXgi~VNBXqsFI(iI$Tu3x0L{<_-%|HMG4Cn?Xs zq~fvBhu;SDOCD7K5(l&i7Py-;Czx5byV*3y%#-Of9rtz?M_owXc2}$OIY~)EZ&2?r zLQ(onz~I7U!w?B%LtfDz)*X=CscqH!UE=mO?d&oYvtj|(u)^yomS;Cd>Men|#2yuD zg&tf(*iSHyo;^A03p&_j*QXay9d}qZ0CgU@rnFNDIT5xLhC5_tlugv()+w%`7;ICf z>;<#L4m@{1}Og76*e zHWFm~;n@B1GqO8s%=qu)+^MR|jp(ULUOi~v;wE8SB6^mK@adSb=o+A_>Itjn13AF& zDZe+wUF9G!JFv|dpj1#d+}BO~s*QTe3381TxA%Q>P*J#z%( z5*8N^QWxgF73^cTKkkvgvIzf*cLEyyKw)Wf{#$n{uS#(rAA~>TS#!asqQ2m_izXe3 z7$Oh=rR;sdmVx3G)s}eImsb<@r2~5?vcw*Q4LU~FFh!y4r*>~S7slAE6)W3Up2OHr z2R)+O<0kKo<3+5vB}v!lB*`%}gFldc+79iahqEx#&Im@NCQU$@PyCZbcTt?K{;o@4 z312O9GB)?X&wAB}*-NEU zn@6`)G`FhT8O^=Cz3y+XtbwO{5+{4-&?z!esFts-C zypwgI^4#tZ74KC+_IW|E@kMI=1pSJkvg$9G3Va(!reMnJ$kcMiZ=30dTJ%(Ws>eUf z;|l--TFDqL!PZbLc_O(XP0QornpP;!)hdT#Ts7tZ9fcQeH&rhP_1L|Z_ha#JOroe^qcsLi`+AoBWHPM7}gD z+mHuPXd14M?nkp|nu9G8hPk;3=JXE-a204Fg!BK|$MX`k-qPeD$2OOqvF;C(l8wm13?>i(pz7kRyYm zM$IEzf`$}B%ezr!$(UO#uWExn%nTCTIZzq&8@i8sP#6r8 z*QMUzZV(LEWZb)wbmf|Li;UpiP;PlTQ(X4zreD`|`RG!7_wc6J^MFD!A=#K*ze>Jg z?9v?p(M=fg_VB0+c?!M$L>5FIfD(KD5ku*djwCp+5GVIs9^=}kM2RFsxx0_5DE%BF zykxwjWvs=rbi4xKIt!z$&v(`msFrl4n>a%NO_4`iSyb!UiAE&mDa+apc zPe)#!ToRW~rqi2e1bdO1RLN5*uUM@{S`KLJhhY-@TvC&5D(c?a(2$mW-&N%h5IfEM zdFI6`6KJiJQIHvFiG-34^BtO3%*$(-Ht_JU*(KddiUYoM{coadlG&LVvke&*p>Cac z^BPy2Zteiq1@ulw0e)e*ot7@A$RJui0$l^{lsCt%R;$){>zuRv9#w@;m=#d%%TJmm zC#%eFOoy$V)|3*d<OC1iP+4R7D z8FE$E8l2Y?(o-i6wG=BKBh0-I?i3WF%hqdD7VCd;vpk|LFP!Et8$@voH>l>U8BY`Q zC*G;&y6|!p=7`G$*+hxCv!@^#+QD3m>^azyZoLS^;o_|plQaj-wx^ zRV&$HcY~p)2|Zqp0SYU?W3zV87s6JP-@D~$t0 zvd;-YL~JWc*8mtHz_s(cXus#XYJc5zdC=&!4MeZ;N3TQ>^I|Pd=HPjVP*j^45rs(n zzB{U4-44=oQ4rNN6@>qYVMH4|GmMIz#z@3UW-1_y#eNa+Q%(41oJ5i(DzvMO^%|?L z^r_+MZtw0DZ0=BT-@?hUtA)Ijk~Kh-N8?~X5%KnRH7cb!?Yrd8gtiEo!v{sGrQk{X zvV>h{8-DqTyuAxIE(hb}jMVtga$;FIrrKm>ye5t%M;p!jcH1(Bbux>4D#MVhgZGd> z=c=nVb%^9T?iDgM&9G(mV5xShc-lBLi*6RShenDqB%`-2;I*;IHg6>#ovKQ$M}dDb z<$USN%LMqa5_5DR7g7@(oAoQ%!~<1KSQr$rmS{UFQJs5&qBhgTEM_Y7|0Wv?fbP`z z)`8~=v;B)+>Jh`V*|$dTxKe`HTBkho^-!!K#@i{9FLn-XqX&fQcGsEAXp)BV7(`Lk zC{4&+Pe-0&<)C0kAa(MTnb|L;ZB5i|b#L1o;J)+?SV8T*U9$Vxhy}dm3%!A}SK9l_6(#5(e*>8|;4gNKk7o_%m_ zEaS=Z(ewk}hBJ>v`jtR=$pm_Wq3d&DU+6`BACU4%qdhH1o^m8hT2&j<4Z8!v=rMCk z-I*?48{2H*&+r<{2?wp$kh@L@=rj8c`EaS~J>W?)trc?zP&4bsNagS4yafuDoXpi5`!{BVqJ1$ZC3`pf$`LIZ(`0&Ik+!_Xa=NJW`R2 zd#Ntgwz`JVwC4A61$FZ&kP)-{T|rGO59`h#1enAa`cWxRR8bKVvvN6jBzAYePrc&5 z+*zr3en|LYB2>qJp479rEALk5d*X-dfKn6|kuNm;2-U2+P3_rma!nWjZQ-y*q3JS? zBE}zE-!1ZBR~G%v!$l#dZ*$UV4$7q}xct}=on+Ba8{b>Y9h*f-GW0D0o#vJ0%ALg( ztG2+AjWlG#d;myA(i&dh8Gp?y9HD@`CTaDAy?c&0unZ%*LbLIg4;m{Kc?)ws3^>M+ zt5>R)%KIJV*MRUg{0$#nW=Lj{#8?dD$yhjBOrAeR#4$H_Dc(eyA4dNjZEz1Xk+Bqt zB&pPl+?R{w8GPv%VI`x`IFOj320F1=cV4aq0(*()Tx!VVxCjua;)t}gTr=b?zY+U! zkb}xjXZ?hMJN{Hjw?w&?gz8Ow`htX z@}WG*_4<%ff8(!S6bf3)p+8h2!Rory>@aob$gY#fYJ=LiW0`+~l7GI%EX_=8 z{(;0&lJ%9)M9{;wty=XvHbIx|-$g4HFij`J$-z~`mW)*IK^MWVN+*>uTNqaDmi!M8 zurj6DGd)g1g(f`A-K^v)3KSOEoZXImXT06apJum-dO_%oR)z6Bam-QC&CNWh7kLOE zcxLdVjYLNO2V?IXWa-ys30Jbxw(Xm?U1{4kDs9`gZQHh8X{*w9=H&Zz&-6RL?uq#R zxN+k~JaL|gdsdvY_u6}}MHC?a@ElFeipA1Lud#M~)pp2SnG#K{a@tSpvXM;A8gz9> zRVDV5T1%%!LsNRDOw~LIuiAiKcj<%7WpgjP7G6mMU1#pFo6a-1>0I5ZdhxnkMX&#L z=Vm}?SDlb_LArobqpnU!WLQE*yVGWgs^4RRy4rrJwoUUWoA~ZJUx$mK>J6}7{CyC4 zv=8W)kKl7TmAnM%m;anEDPv5tzT{A{ON9#FPYF6c=QIc*OrPp96tiY&^Qs+#A1H>Y z<{XtWt2eDwuqM zQ_BI#UIP;2-olOL4LsZ`vTPv-eILtuB7oWosoSefWdM}BcP>iH^HmimR`G`|+9waCO z&M375o@;_My(qYvPNz;N8FBZaoaw3$b#x`yTBJLc8iIP z--la{bzK>YPP|@Mke!{Km{vT8Z4|#An*f=EmL34?!GJfHaDS#41j~8c5KGKmj!GTh&QIH+DjEI*BdbSS2~6VTt}t zhAwNQNT6%c{G`If3?|~Fp7iwee(LaUS)X9@I29cIb61} z$@YBq4hSplr&liE@ye!y&7+7n$fb+8nS~co#^n@oCjCwuKD61x$5|0ShDxhQES5MP z(gH|FO-s6#$++AxnkQR!3YMgKcF)!&aqr^a3^{gAVT`(tY9@tqgY7@ z>>ul3LYy`R({OY7*^Mf}UgJl(N7yyo$ag;RIpYHa_^HKx?DD`%Vf1D0s^ zjk#OCM5oSzuEz(7X`5u~C-Y~n4B}_3*`5B&8tEdND@&h;H{R`o%IFpIJ4~Kw!kUjehGT8W!CD7?d8sg_$KKp%@*dW)#fI1#R<}kvzBVpaog_2&W%c_jJfP` z6)wE+$3+Hdn^4G}(ymPyasc1<*a7s2yL%=3LgtZLXGuA^jdM^{`KDb%%}lr|ONDsl zy~~jEuK|XJ2y<`R{^F)Gx7DJVMvpT>gF<4O%$cbsJqK1;v@GKXm*9l3*~8^_xj*Gs z=Z#2VQ6`H@^~#5Pv##@CddHfm;lbxiQnqy7AYEH(35pTg^;u&J2xs-F#jGLuDw2%z z`a>=0sVMM+oKx4%OnC9zWdbpq*#5^yM;og*EQKpv`^n~-mO_vj=EgFxYnga(7jO?G z`^C87B4-jfB_RgN2FP|IrjOi;W9AM1qS}9W@&1a9Us>PKFQ9~YE!I~wTbl!m3$Th? z)~GjFxmhyyGxN}t*G#1^KGVXm#o(K0xJyverPe}mS=QgJ$#D}emQDw+dHyPu^&Uv> z4O=3gK*HLFZPBY|!VGq60Of6QrAdj`nj1h!$?&a;Hgaj{oo{l0P3TzpJK_q_eW8Ng zP6QF}1{V;xlolCs?pGegPoCSxx@bshb#3ng4Fkp4!7B0=&+1%187izf@}tvsjZ6{m z4;K>sR5rm97HJrJ`w}Y`-MZN$Wv2N%X4KW(N$v2@R1RkRJH2q1Ozs0H`@ zd5)X-{!{<+4Nyd=hQ8Wm3CCd}ujm*a?L79ztfT7@&(?B|!pU5&%9Rl!`i;suAg0+A zxb&UYpo-z}u6CLIndtH~C|yz&!OV_I*L;H#C7ie_5uB1fNRyH*<^d=ww=gxvE%P$p zRHKI{^{nQlB9nLhp9yj-so1is{4^`{Xd>Jl&;dX;J)#- z=fmE5GiV?-&3kcjM1+XG7&tSq;q9Oi4NUuRrIpoyp*Fn&nVNFdUuGQ_g)g>VzXGdneB7`;!aTUE$t* z5iH+8XPxrYl)vFo~+vmcU-2) zq!6R(T0SsoDnB>Mmvr^k*{34_BAK+I=DAGu){p)(ndZqOFT%%^_y;X(w3q-L``N<6 zw9=M zoQ8Lyp>L_j$T20UUUCzYn2-xdN}{e@$8-3vLDN?GbfJ>7*qky{n!wC#1NcYQr~d51 zy;H!am=EI#*S&TCuP{FA3CO)b0AAiN*tLnDbvKwxtMw-l;G2T@EGH)YU?-B`+Y=!$ zypvDn@5V1Tr~y~U0s$ee2+CL3xm_BmxD3w}d_Pd@S%ft#v~_j;6sC6cy%E|dJy@wj z`+(YSh2CrXMxI;yVy*=O@DE2~i5$>nuzZ$wYHs$y`TAtB-ck4fQ!B8a;M=CxY^Nf{ z+UQhn0jopOzvbl(uZZ1R-(IFaprC$9hYK~b=57@ zAJ8*pH%|Tjotzu5(oxZyCQ{5MAw+6L4)NI!9H&XM$Eui-DIoDa@GpNI=I4}m>Hr^r zZjT?xDOea}7cq+TP#wK1p3}sbMK{BV%(h`?R#zNGIP+7u@dV5#zyMau+w}VC1uQ@p zrFUjrJAx6+9%pMhv(IOT52}Dq{B9njh_R`>&j&5Sbub&r*hf4es)_^FTYdDX$8NRk zMi=%I`)hN@N9>X&Gu2RmjKVsUbU>TRUM`gwd?CrL*0zxu-g#uNNnnicYw=kZ{7Vz3 zULaFQ)H=7%Lm5|Z#k?<{ux{o4T{v-e zTLj?F(_qp{FXUzOfJxEyKO15Nr!LQYHF&^jMMBs z`P-}WCyUYIv>K`~)oP$Z85zZr4gw>%aug1V1A)1H(r!8l&5J?ia1x_}Wh)FXTxZUE zs=kI}Ix2cK%Bi_Hc4?mF^m`sr6m8M(n?E+k7Tm^Gn}Kf= zfnqoyVU^*yLypz?s+-XV5(*oOBwn-uhwco5b(@B(hD|vtT8y7#W{>RomA_KchB&Cd zcFNAD9mmqR<341sq+j+2Ra}N5-3wx5IZqg6Wmi6CNO#pLvYPGNER}Q8+PjvIJ42|n zc5r@T*p)R^U=d{cT2AszQcC6SkWiE|hdK)m{7ul^mU+ED1R8G#)#X}A9JSP_ubF5p z8Xxcl;jlGjPwow^p+-f_-a~S;$lztguPE6SceeUCfmRo=Qg zKHTY*O_ z;pXl@z&7hniVYVbGgp+Nj#XP^Aln2T!D*{(Td8h{8Dc?C)KFfjPybiC`Va?Rf)X>y z;5?B{bAhPtbmOMUsAy2Y0RNDQ3K`v`gq)#ns_C&ec-)6cq)d^{5938T`Sr@|7nLl; zcyewuiSUh7Z}q8iIJ@$)L3)m)(D|MbJm_h&tj^;iNk%7K-YR}+J|S?KR|29K?z-$c z<+C4uA43yfSWBv*%z=-0lI{ev`C6JxJ};A5N;lmoR(g{4cjCEn33 z-ef#x^uc%cM-f^_+*dzE?U;5EtEe;&8EOK^K}xITa?GH`tz2F9N$O5;)`Uof4~l+t z#n_M(KkcVP*yMYlk_~5h89o zlf#^qjYG8Wovx+f%x7M7_>@r7xaXa2uXb?_*=QOEe_>ErS(v5-i)mrT3&^`Oqr4c9 zDjP_6T&NQMD`{l#K&sHTm@;}ed_sQ88X3y`ON<=$<8Qq{dOPA&WAc2>EQ+U8%>yWR zK%(whl8tB;{C)yRw|@Gn4%RhT=bbpgMZ6erACc>l5^p)9tR`(2W-D*?Ph6;2=Fr|G- zdF^R&aCqyxqWy#P7#G8>+aUG`pP*ow93N=A?pA=aW0^^+?~#zRWcf_zlKL8q8-80n zqGUm=S8+%4_LA7qrV4Eq{FHm9#9X15%ld`@UKyR7uc1X*>Ebr0+2yCye6b?i=r{MPoqnTnYnq z^?HWgl+G&@OcVx4$(y;{m^TkB5Tnhx2O%yPI=r*4H2f_6Gfyasq&PN^W{#)_Gu7e= zVHBQ8R5W6j;N6P3O(jsRU;hkmLG(Xs_8=F&xh@`*|l{~0OjUVlgm z7opltSHg7Mb%mYamGs*v1-#iW^QMT**f+Nq*AzIvFT~Ur3KTD26OhIw1WQsL(6nGg znHUo-4e15cXBIiyqN};5ydNYJ6zznECVVR44%(P0oW!yQ!YH)FPY?^k{IrtrLo7Zo`?sg%%oMP9E^+H@JLXicr zi?eoI?LODRPcMLl90MH32rf8btf69)ZE~&4d%(&D{C45egC6bF-XQ;6QKkbmqW>_H z{86XDZvjiN2wr&ZPfi;^SM6W+IP0);50m>qBhzx+docpBkkiY@2bSvtPVj~E`CfEu zhQG5G>~J@dni5M5Jmv7GD&@%UR`k3ru-W$$onI259jM&nZ)*d3QFF?Mu?{`+nVzkx z=R*_VH=;yeU?9TzQ3dP)q;P)4sAo&k;{*Eky1+Z!10J<(cJC3zY9>bP=znA=<-0RR zMnt#<9^X7BQ0wKVBV{}oaV=?JA=>R0$az^XE%4WZcA^Em>`m_obQyKbmf-GA;!S-z zK5+y5{xbkdA?2NgZ0MQYF-cfOwV0?3Tzh8tcBE{u%Uy?Ky4^tn^>X}p>4&S(L7amF zpWEio8VBNeZ=l!%RY>oVGOtZh7<>v3?`NcHlYDPUBRzgg z0OXEivCkw<>F(>1x@Zk=IbSOn+frQ^+jI*&qdtf4bbydk-jgVmLAd?5ImK+Sigh?X zgaGUlbf^b-MH2@QbqCawa$H1Vb+uhu{zUG9268pa{5>O&Vq8__Xk5LXDaR1z$g;s~;+Ae82wq#l;wo08tX(9uUX6NJWq1vZLh3QbP$# zL`udY|Qp*4ER`_;$%)2 zmcJLj|FD`(;ts0bD{}Ghq6UAVpEm#>j`S$wHi0-D_|)bEZ}#6) zIiqH7Co;TB`<6KrZi1SF9=lO+>-_3=Hm%Rr7|Zu-EzWLSF{9d(H1v*|UZDWiiqX3} zmx~oQ6%9~$=KjPV_ejzz7aPSvTo+3@-a(OCCoF_u#2dHY&I?`nk zQ@t8#epxAv@t=RUM09u?qnPr6=Y5Pj;^4=7GJ`2)Oq~H)2V)M1sC^S;w?hOB|0zXT zQdf8$)jslO>Q}(4RQ$DPUF#QUJm-k9ysZFEGi9xN*_KqCs9Ng(&<;XONBDe1Joku? z*W!lx(i&gvfXZ4U(AE@)c0FI2UqrFLOO$&Yic|`L;Vyy-kcm49hJ^Mj^H9uY8Fdm2 z?=U1U_5GE_JT;Tx$2#I3rAAs(q@oebIK=19a$N?HNQ4jw0ljtyGJ#D}z3^^Y=hf^Bb--297h6LQxi0-`TB|QY2QPg92TAq$cEQdWE ze)ltSTVMYe0K4wte6;^tE+^>|a>Hit_3QDlFo!3Jd`GQYTwlR#{<^MzG zK!vW&))~RTKq4u29bc<+VOcg7fdorq-kwHaaCQe6tLB{|gW1_W_KtgOD0^$^|`V4C# z*D_S9Dt_DIxpjk3my5cBFdiYaq||#0&0&%_LEN}BOxkb3v*d$4L|S|z z!cZZmfe~_Y`46v=zul=aixZTQCOzb(jx>8&a%S%!(;x{M2!*$od2!Pwfs>RZ-a%GOZdO88rS)ZW~{$656GgW)$Q=@!x;&Nn~!K)lr4gF*%qVO=hlodHA@2)keS2 zC}7O=_64#g&=zY?(zhzFO3)f5=+`dpuyM!Q)zS&otpYB@hhn$lm*iK2DRt+#1n|L%zjM}nB*$uAY^2JIw zV_P)*HCVq%F))^)iaZD#R9n^{sAxBZ?Yvi1SVc*`;8|F2X%bz^+s=yS&AXjysDny)YaU5RMotF-tt~FndTK ziRve_5b!``^ZRLG_ks}y_ye0PKyKQSsQCJuK5()b2ThnKPFU?An4;dK>)T^4J+XjD zEUsW~H?Q&l%K4<1f5^?|?lyCQe(O3?!~OU{_Wxs#|Ff8?a_WPQUKvP7?>1()Cy6oLeA zjEF^d#$6Wb${opCc^%%DjOjll%N2=GeS6D-w=Ap$Ux2+0v#s#Z&s6K*)_h{KFfgKjzO17@p1nKcC4NIgt+3t}&}F z@cV; zZ1r#~?R@ZdSwbFNV(fFl2lWI(Zf#nxa<6f!nBZD>*K)nI&Fun@ngq@Ge!N$O< zySt*mY&0moUXNPe~Fg=%gIu)tJ;asscQ!-AujR@VJBRoNZNk;z4hs4T>Ud!y=1NwGs-k zlTNeBOe}=)Epw=}+dfX;kZ32h$t&7q%Xqdt-&tlYEWc>>c3(hVylsG{Ybh_M8>Cz0ZT_6B|3!_(RwEJus9{;u-mq zW|!`{BCtnao4;kCT8cr@yeV~#rf76=%QQs(J{>Mj?>aISwp3{^BjBO zLV>XSRK+o=oVDBnbv?Y@iK)MiFSl{5HLN@k%SQZ}yhPiu_2jrnI?Kk?HtCv>wN$OM zSe#}2@He9bDZ27hX_fZey=64#SNU#1~=icK`D>a;V-&Km>V6ZdVNj7d2 z-NmAoOQm_aIZ2lXpJhlUeJ95eZt~4_S zIfrDs)S$4UjyxKSaTi#9KGs2P zfSD>(y~r+bU4*#|r`q+be_dopJzKK5JNJ#rR978ikHyJKD>SD@^Bk$~D0*U38Y*IpYcH>aaMdZq|YzQ-Ixd(_KZK!+VL@MWGl zG!k=<%Y-KeqK%``uhx}0#X^@wS+mX@6Ul@90#nmYaKh}?uw>U;GS4fn3|X%AcV@iY z8v+ePk)HxSQ7ZYDtlYj#zJ?5uJ8CeCg3efmc#|a%2=u>+vrGGRg$S@^mk~0f;mIu! zWMA13H1<@hSOVE*o0S5D8y=}RiL#jQpUq42D}vW$z*)VB*FB%C?wl%(3>ANaY)bO@ zW$VFutemwy5Q*&*9HJ603;mJJkB$qp6yxNOY0o_4*y?2`qbN{m&*l{)YMG_QHXXa2 z+hTmlA;=mYwg{Bfusl zyF&}ib2J;#q5tN^e)D62fWW*Lv;Rnb3GO-JVtYG0CgR4jGujFo$Waw zSNLhc{>P~>{KVZE1Vl1!z)|HFuN@J7{`xIp_)6>*5Z27BHg6QIgqLqDJTmKDM+ON* zK0Fh=EG`q13l z+m--9UH0{ZGQ%j=OLO8G2WM*tgfY}bV~>3Grcrpehjj z6Xe<$gNJyD8td3EhkHjpKk}7?k55Tu7?#;5`Qcm~ki;BeOlNr+#PK{kjV>qfE?1No zMA07}b>}Dv!uaS8Hym0TgzxBxh$*RX+Fab6Gm02!mr6u}f$_G4C|^GSXJMniy^b`G z74OC=83m0G7L_dS99qv3a0BU({t$zHQsB-RI_jn1^uK9ka_%aQuE2+~J2o!7`735Z zb?+sTe}Gd??VEkz|KAPMfj(1b{om89p5GIJ^#Aics_6DD%WnNGWAW`I<7jT|Af|8g zZA0^)`p8i#oBvX2|I&`HC8Pn&0>jRuMF4i0s=}2NYLmgkZb=0w9tvpnGiU-gTUQhJ zR6o4W6ZWONuBZAiN77#7;TR1^RKE(>>OL>YU`Yy_;5oj<*}ac99DI(qGCtn6`949f ziMpY4k>$aVfffm{dNH=-=rMg|u?&GIToq-u;@1-W&B2(UOhC-O2N5_px&cF-C^tWp zXvChm9@GXEcxd;+Q6}u;TKy}$JF$B`Ty?|Y3tP$N@Rtoy(*05Wj-Ks32|2y2ZM>bM zi8v8E1os!yorR!FSeP)QxtjIKh=F1ElfR8U7StE#Ika;h{q?b?Q+>%78z^>gTU5+> zxQ$a^rECmETF@Jl8fg>MApu>btHGJ*Q99(tMqsZcG+dZ6Yikx7@V09jWCiQH&nnAv zY)4iR$Ro223F+c3Q%KPyP9^iyzZsP%R%-i^MKxmXQHnW6#6n7%VD{gG$E;7*g86G< zu$h=RN_L2(YHO3@`B<^L(q@^W_0#U%mLC9Q^XEo3LTp*~(I%?P_klu-c~WJxY1zTI z^PqntLIEmdtK~E-v8yc&%U+jVxW5VuA{VMA4Ru1sk#*Srj0Pk#tZuXxkS=5H9?8eb z)t38?JNdP@#xb*yn=<*_pK9^lx%;&yH6XkD6-JXgdddZty8@Mfr9UpGE!I<37ZHUe z_Rd+LKsNH^O)+NW8Ni-V%`@J_QGKA9ZCAMSnsN>Ych9VW zCE7R_1FVy}r@MlkbxZ*TRIGXu`ema##OkqCM9{wkWQJg^%3H${!vUT&vv2250jAWN zw=h)C!b2s`QbWhBMSIYmWqZ_~ReRW;)U#@C&ThctSd_V!=HA=kdGO-Hl57an|M1XC?~3f0{7pyjWY}0mChU z2Fj2(B*r(UpCKm-#(2(ZJD#Y|Or*Vc5VyLpJ8gO1;fCm@EM~{DqpJS5FaZ5%|ALw) zyumBl!i@T57I4ITCFmdbxhaOYud}i!0YkdiNRaQ%5$T5>*HRBhyB~<%-5nj*b8=i= z(8g(LA50%0Zi_eQe}Xypk|bt5e6X{aI^jU2*c?!p*$bGk=?t z+17R){lx~Z{!B34Zip~|A;8l@%*Gc}kT|kC0*Ny$&fI3@%M! zqk_zvN}7bM`x@jqFOtaxI?*^Im5ix@=`QEv;__i;Tek-&7kGm6yP17QANVL>*d0B=4>i^;HKb$k8?DYFMr38IX4azK zBbwjF%$>PqXhJh=*7{zH5=+gi$!nc%SqFZlwRm zmpctOjZh3bwt!Oc>qVJhWQf>`HTwMH2ibK^eE*j!&Z`-bs8=A`Yvnb^?p;5+U=Fb8 z@h>j_3hhazd$y^Z-bt%3%E3vica%nYnLxW+4+?w{%|M_=w^04U{a6^22>M_?{@mXP zS|Qjcn4&F%WN7Z?u&I3fU(UQVw4msFehxR*80dSb=a&UG4zDQp&?r2UGPy@G?0FbY zVUQ?uU9-c;f9z06$O5FO1TOn|P{pLcDGP?rfdt`&uw|(Pm@$n+A?)8 zP$nG(VG&aRU*(_5z#{+yVnntu`6tEq>%9~n^*ao}`F6ph_@6_8|AfAXtFfWee_14` zKKURYV}4}=UJmxv7{RSz5QlwZtzbYQs0;t3?kx*7S%nf-aY&lJ@h?-BAn%~0&&@j) zQd_6TUOLXErJ`A3vE?DJIbLE;s~s%eVt(%fMzUq^UfZV9c?YuhO&6pwKt>j(=2CkgTNEq7&c zfeGN+%5DS@b9HO>zsoRXv@}(EiA|t5LPi}*R3?(-=iASADny<{D0WiQG>*-BSROk4vI6%$R>q64J&v-T+(D<_(b!LD z9GL;DV;;N3!pZYg23mcg81tx>7)=e%f|i{6Mx0GczVpc}{}Mg(W_^=Wh0Rp+xXgX` z@hw|5=Je&nz^Xa>>vclstYt;8c2PY)87Ap;z&S&`yRN>yQVV#K{4&diVR7Rm;S{6m z6<+;jwbm`==`JuC6--u6W7A@o4&ZpJV%5+H)}toy0afF*!)AaG5=pz_i9}@OG%?$O z2cec6#@=%xE3K8;^ps<2{t4SnqH+#607gAHP-G4^+PBiC1s>MXf&bQ|Pa;WBIiErV z?3VFpR9JFl9(W$7p3#xe(Bd?Z93Uu~jHJFo7U3K_x4Ej-=N#=a@f;kPV$>;hiN9i9 z<6elJl?bLI$o=|d6jlihA4~bG;Fm2eEnlGxZL`#H%Cdes>uJfMJ4>@1SGGeQ81DwxGxy7L5 zm05Ik*WpSgZvHh@Wpv|2i|Y#FG?Y$hbRM5ZF0Z7FB3cY0+ei#km9mDSPI}^!<<`vr zuv$SPg2vU{wa)6&QMY)h1hbbxvR2cc_6WcWR`SH& z&KuUQcgu}!iW2Wqvp~|&&LSec9>t(UR_|f$;f-fC&tSO-^-eE0B~Frttnf+XN(#T) z^PsuFV#(pE#6ztaI8(;ywN%CtZh?w&;_)w_s@{JiA-SMjf&pQk+Bw<}f@Q8-xCQMwfaf zMgHsAPU=>>Kw~uDFS(IVRN{$ak(SV(hrO!UqhJ?l{lNnA1>U24!=>|q_p404Xd>M# z7?lh^C&-IfeIr`Dri9If+bc%oU0?|Rh8)%BND5;_9@9tuM)h5Kcw6}$Ca7H_n)nOf0pd`boCXItb`o11 zb`)@}l6I_h>n+;`g+b^RkYs7;voBz&Gv6FLmyvY|2pS)z#P;t8k;lS>49a$XeVDc4 z(tx2Pe3N%Gd(!wM`E7WRBZy)~vh_vRGt&esDa0NCua)rH#_39*H0!gIXpd>~{rGx+ zJKAeXAZ-z5n=mMVqlM5Km;b;B&KSJlScD8n?2t}kS4Wf9@MjIZSJ2R?&=zQn zs_`=+5J$47&mP4s{Y{TU=~O_LzSrXvEP6W?^pz<#Y*6Fxg@$yUGp31d(h+4x>xpb< zH+R639oDST6F*0iH<9NHC^Ep*8D4-%p2^n-kD6YEI<6GYta6-I;V^ZH3n5}syTD=P z3b6z=jBsdP=FlXcUe@I|%=tY4J_2j!EVNEzph_42iO3yfir|Dh>nFl&Lu9!;`!zJB zCis9?_(%DI?$CA(00pkzw^Up`O;>AnPc(uE$C^a9868t$m?5Q)CR%!crI$YZpiYK6m= z!jv}82He`QKF;10{9@roL2Q7CF)OeY{~dBp>J~X#c-Z~{YLAxNmn~kWQW|2u!Yq00 zl5LKbzl39sVCTpm9eDW_T>Z{x@s6#RH|P zA~_lYas7B@SqI`N=>x50Vj@S)QxouKC(f6Aj zz}7e5e*5n?j@GO;mCYEo^Jp_*BmLt3!N)(T>f#L$XHQWzZEVlJo(>qH@7;c%fy zS-jm^Adju9Sm8rOKTxfTU^!&bg2R!7C_-t+#mKb_K?0R72%26ASF;JWA_prJ8_SVW zOSC7C&CpSrgfXRp8r)QK34g<~!1|poTS7F;)NseFsbwO$YfzEeG3oo!qe#iSxQ2S# z1=Fxc9J;2)pCab-9o-m8%BLjf(*mk#JJX3k9}S7Oq)dV0jG)SOMbw7V^Z<5Q0Cy$< z^U0QUVd4(96W03OA1j|x%{sd&BRqIERDb6W{u1p1{J(a;fd6lnWzjeS`d?L3-0#o7 z{Qv&L7!Tm`9|}u=|IbwS_jgH(_V@o`S*R(-XC$O)DVwF~B&5c~m!zl14ydT6sK+Ly zn+}2hQ4RTC^8YvrQ~vk$f9u=pTN{5H_yTOcza9SVE&nt_{`ZC8zkmFji=UyD`G4~f zUfSTR=Kju>6u+y&|Bylb*W&^P|8fvEbQH3+w*DrKq|9xMzq2OiZyM=;(?>~4+O|jn zC_Et05oc>e%}w4ye2Fm%RIR??VvofwZS-}BL@X=_4jdHp}FlMhW_IW?Zh`4$z*Wr!IzQHa3^?1|);~VaWmsIcmc6 zJs{k0YW}OpkfdoTtr4?9F6IX6$!>hhA+^y_y@vvA_Gr7u8T+i-< zDX(~W5W{8mfbbM-en&U%{mINU#Q8GA`byo)iLF7rMVU#wXXY`a3ji3m{4;x53216i z`zA8ap?>_}`tQj7-%$K78uR}R$|@C2)qgop$}o=g(jOv0ishl!E(R73N=i0~%S)6+ z1xFP7|H0yt3Z_Re*_#C2m3_X{=zi1C&3CM7e?9-Y5lCtAlA%RFG9PDD=Quw1dfYnZ zdUL)#+m`hKx@PT`r;mIx_RQ6Txbti+&;xQorP;$H=R2r)gPMO9>l+!p*Mt04VH$$M zSLwJ81IFjQ5N!S#;MyBD^IS`2n04kuYbZ2~4%3%tp0jn^**BZQ05ELp zY%yntZ=52s6U5Y93Aao)v~M3y?6h7mZcVGp63pK*d&!TRjW99rUU;@s#3kYB76Bs$|LRwkH>L!0Xe zE=dz1o}phhnOVYZFsajQsRA^}IYZnk9Wehvo>gHPA=TPI?2A`plIm8=F1%QiHx*Zn zi)*Y@)$aXW0v1J|#+R2=$ysooHZ&NoA|Wa}htd`=Eud!(HD7JlT8ug|yeBZmpry(W z)pS>^1$N#nuo3PnK*>Thmaxz4pLcY?PP2r3AlhJ7jw(TI8V#c}>Ym;$iPaw+83L+* z!_QWpYs{UWYcl0u z(&(bT0Q*S_uUX9$jC;Vk%oUXw=A-1I+!c18ij1CiUlP@pfP9}CHAVm{!P6AEJ(7Dn z?}u#}g`Q?`*|*_0Rrnu8{l4PP?yCI28qC~&zlwgLH2AkfQt1?B#3AOQjW&10%@@)Q zDG?`6$8?Nz(-sChL8mRs#3z^uOA>~G=ZIG*mgUibWmgd{a|Tn4nkRK9O^37E(()Q% zPR0#M4e2Q-)>}RSt1^UOCGuv?dn|IT3#oW_$S(YR+jxAzxCD_L25p_dt|^>g+6Kgj zJhC8n)@wY;Y7JI6?wjU$MQU|_Gw*FIC)x~^Eq1k41BjLmr}U>6#_wxP0-2Ka?uK14u5M-lAFSX$K1K{WH!M1&q}((MWWUp#Uhl#n_yT5dFs4X`>vmM& z*1!p0lACUVqp&sZG1GWATvZEENs^0_7Ymwem~PlFN3hTHVBv(sDuP;+8iH07a)s(# z%a7+p1QM)YkS7>kbo${k2N1&*%jFP*7UABJ2d||c!eSXWM*<4(_uD7;1XFDod@cT$ zP>IC%^fbC${^QrUXy$f)yBwY^g@}}kngZKa1US!lAa+D=G4wklukaY8AEW%GL zh40pnuv*6D>9`_e14@wWD^o#JvxYVG-~P)+<)0fW zP()DuJN?O*3+Ab!CP-tGr8S4;JN-Ye^9D%(%8d{vb_pK#S1z)nZzE^ezD&%L6nYbZ z*62>?u)xQe(Akd=e?vZbyb5)MMNS?RheZDHU?HK<9;PBHdC~r{MvF__%T)-9ifM#cR#2~BjVJYbA>xbPyl9yNX zX)iFVvv-lfm`d?tbfh^j*A|nw)RszyD<#e>llO8X zou=q3$1|M@Ob;F|o4H0554`&y9T&QTa3{yn=w0BLN~l;XhoslF-$4KGNUdRe?-lcV zS4_WmftU*XpP}*wFM^oKT!D%_$HMT#V*j;9weoOq0mjbl1271$F)`Q(C z76*PAw3_TE{vntIkd=|(zw)j^!@j ^tV@s0U~V+mu)vv`xgL$Z9NQLnuRdZ;95D|1)!0Aybwv}XCE#xz1k?ZC zxAU)v@!$Sm*?)t2mWrkevNFbILU9&znoek=d7jn*k+~ptQ)6z`h6e4B&g?Q;IK+aH z)X(BH`n2DOS1#{AJD-a?uL)@Vl+`B=6X3gF(BCm>Q(9+?IMX%?CqgpsvK+b_de%Q> zj-GtHKf!t@p2;Gu*~#}kF@Q2HMevg~?0{^cPxCRh!gdg7MXsS}BLtG_a0IY0G1DVm z2F&O-$Dzzc#M~iN`!j38gAn`6*~h~AP=s_gy2-#LMFoNZ0<3q+=q)a|4}ur7F#><%j1lnr=F42Mbti zi-LYs85K{%NP8wE1*r4Mm+ZuZ8qjovmB;f##!E*M{*A(4^~vg!bblYi1M@7tq^L8- zH7tf_70iWXqcSQgENGdEjvLiSLicUi3l0H*sx=K!!HLxDg^K|s1G}6Tam|KBV>%YeU)Q>zxQe;ddnDTWJZ~^g-kNeycQ?u242mZs`i8cP)9qW`cwqk)Jf?Re0=SD=2z;Gafh(^X-=WJ$i7Z9$Pao56bTwb+?p>L3bi9 zP|qi@;H^1iT+qnNHBp~X>dd=Us6v#FPDTQLb9KTk%z{&OWmkx3uY(c6JYyK3w|z#Q zMY%FPv%ZNg#w^NaW6lZBU+}Znwc|KF(+X0RO~Q6*O{T-P*fi@5cPGLnzWMSyoOPe3 z(J;R#q}3?z5Ve%crTPZQFLTW81cNY-finw!LH9wr$(C)p_@v?(y#b-R^Pv!}_#7t+A?pHEUMY zoQZIwSETTKeS!W{H$lyB1^!jn4gTD{_mgG?#l1Hx2h^HrpCXo95f3utP-b&%w80F} zXFs@Jp$lbIL64@gc?k*gJ;OForPaapOH7zNMB60FdNP<*9<@hEXJk9Rt=XhHR-5_$Ck-R?+1py&J3Y9^sBBZuj?GwSzua;C@9)@JZpaI zE?x6{H8@j9P06%K_m%9#nnp0Li;QAt{jf-7X%Pd2jHoI4As-9!UR=h6Rjc z!3{UPWiSeLG&>1V5RlM@;5HhQW_&-wL2?%k@dvRS<+@B6Yaj*NG>qE5L*w~1ATP$D zmWu6(OE=*EHqy{($~U4zjxAwpPn42_%bdH9dMphiUU|) z*+V@lHaf%*GcXP079>vy5na3h^>X=n;xc;VFx)`AJEk zYZFlS#Nc-GIHc}j06;cOU@ zAD7Egkw<2a8TOcfO9jCp4U4oI*`|jpbqMWo(={gG3BjuM3QTGDG`%y|xithFck}0J zG}N#LyhCr$IYP`#;}tdm-7^9=72+CBfBsOZ0lI=LC_a%U@(t3J_I1t(UdiJ^@NubM zvvA0mGvTC%{fj53M^|Ywv$KbW;n8B-x{9}Z!K6v-tw&Xe_D2{7tX?eVk$sA*0826( zuGz!K7$O#;K;1w<38Tjegl)PmRso`fc&>fAT5s z7hzQe-_`lx`}2=c)jz6;yn(~F6#M@z_7@Z(@GWbIAo6A2&;aFf&>CVHpqoPh5#~=G zav`rZ3mSL2qwNL+Pg>aQv;%V&41e|YU$!fQ9Ksle!XZERpjAowHtX zi#0lnw{(zmk&}t`iFEMmx-y7FWaE*vA{Hh&>ieZg{5u0-3@a8BY)Z47E`j-H$dadu zIP|PXw1gjO@%aSz*O{GqZs_{ke|&S6hV{-dPkl*V|3U4LpqhG0eVdqfeNX28hrafI zE13WOsRE|o?24#`gQJs@v*EwL{@3>Ffa;knvI4@VEG2I>t-L(KRS0ShZ9N!bwXa}e zI0}@2#PwFA&Y9o}>6(ZaSaz>kw{U=@;d{|dYJ~lyjh~@bBL>n}#@KjvXUOhrZ`DbnAtf5bz3LD@0RpmAyC-4cgu<7rZo&C3~A_jA*0)v|Ctcdu} zt@c7nQ6hSDC@76c4hI&*v|5A0Mj4eQ4kVb0$5j^*$@psB zdouR@B?l6E%a-9%i(*YWUAhxTQ(b@z&Z#jmIb9`8bZ3Um3UW!@w4%t0#nxsc;*YrG z@x$D9Yj3EiA(-@|IIzi@!E$N)j?gedGJpW!7wr*7zKZwIFa>j|cy<(1`VV_GzWN=1 zc%OO)o*RRobvTZE<9n1s$#V+~5u8ZwmDaysD^&^cxynksn!_ypmx)Mg^8$jXu5lMo zK3K_8GJh#+7HA1rO2AM8cK(#sXd2e?%3h2D9GD7!hxOEKJZK&T`ZS0e*c9c36Y-6yz2D0>Kvqy(EuiQtUQH^~M*HY!$e z20PGLb2Xq{3Ceg^sn+99K6w)TkprP)YyNU(+^PGU8}4&Vdw*u;(`Bw!Um76gL_aMT z>*82nmA8Tp;~hwi0d3S{vCwD};P(%AVaBr=yJ zqB?DktZ#)_VFh_X69lAHQw(ZNE~ZRo2fZOIP;N6fD)J*3u^YGdgwO(HnI4pb$H#9) zizJ<>qI*a6{+z=j+SibowDLKYI*Je2Y>~=*fL@i*f&8**s~4l&B&}$~nwhtbOTr=G zFx>{y6)dpJPqv={_@*!q0=jgw3^j`qi@!wiWiT_$1`SPUgaG&9z9u9=m5C8`GpMaM zyMRSv2llS4F}L?233!)f?mvcYIZ~U z7mPng^=p)@Z*Fp9owSYA`Fe4OjLiJ`rdM`-U(&z1B1`S`ufK_#T@_BvenxDQU`deH$X5eMVO=;I4EJjh6?kkG2oc6AYF6|(t)L0$ukG}Zn=c+R`Oq;nC)W^ z{ek!A?!nCsfd_5>d&ozG%OJmhmnCOtARwOq&p!FzWl7M))YjqK8|;6sOAc$w2%k|E z`^~kpT!j+Y1lvE0B)mc$Ez_4Rq~df#vC-FmW;n#7E)>@kMA6K30!MdiC19qYFnxQ* z?BKegU_6T37%s`~Gi2^ewVbciy-m5%1P3$88r^`xN-+VdhhyUj4Kzg2 zlKZ|FLUHiJCZL8&<=e=F2A!j@3D@_VN%z?J;uw9MquL`V*f^kYTrpoWZ6iFq00uO+ zD~Zwrs!e4cqGedAtYxZ76Bq3Ur>-h(m1~@{x@^*YExmS*vw9!Suxjlaxyk9P#xaZK z)|opA2v#h=O*T42z>Mub2O3Okd3GL86KZM2zlfbS z{Vps`OO&3efvt->OOSpMx~i7J@GsRtoOfQ%vo&jZ6^?7VhBMbPUo-V^Znt%-4k{I# z8&X)=KY{3lXlQg4^FH^{jw0%t#2%skLNMJ}hvvyd>?_AO#MtdvH;M^Y?OUWU6BdMX zJ(h;PM9mlo@i)lWX&#E@d4h zj4Z0Czj{+ipPeW$Qtz_A52HA<4$F9Qe4CiNQSNE2Q-d1OPObk4?7-&`={{yod5Iy3kB=PK3%0oYSr`Gca120>CHbC#SqE*ivL2R(YmI1A|nAT?JmK*2qj_3p#?0h)$#ixdmP?UejCg9%AS2 z8I(=_QP(a(s)re5bu-kcNQc-&2{QZ%KE*`NBx|v%K2?bK@Ihz_e<5Y(o(gQ-h+s&+ zjpV>uj~?rfJ!UW5Mop~ro^|FP3Z`@B6A=@f{Wn78cm`)3&VJ!QE+P9&$;3SDNH>hI z_88;?|LHr%1kTX0t*xzG-6BU=LRpJFZucRBQ<^zy?O5iH$t>o}C}Fc+kM1EZu$hm% zTTFKrJkXmCylFgrA;QAA(fX5Sia5TNo z?=Ujz7$Q?P%kM$RKqRQisOexvV&L+bolR%`u`k;~!o(HqgzV9I6w9|g*5SVZN6+kT9H$-3@%h%k7BBnB zPn+wmPYNG)V2Jv`&$LoI*6d0EO^&Nh`E* z&1V^!!Szd`8_uf%OK?fuj~! z%p9QLJ?V*T^)72<6p1ONqpmD?Wm((40>W?rhjCDOz?#Ei^sXRt|GM3ULLnoa8cABQ zA)gCqJ%Q5J%D&nJqypG-OX1`JLT+d`R^|0KtfGQU+jw79la&$GHTjKF>*8BI z0}l6TC@XB6`>7<&{6WX2kX4k+0SaI`$I8{{mMHB}tVo*(&H2SmZLmW* z+P8N>(r}tR?f!O)?)df>HIu>$U~e~tflVmwk*+B1;TuqJ+q_^`jwGwCbCgSevBqj$ z<`Fj*izeO)_~fq%wZ0Jfvi6<3v{Afz;l5C^C7!i^(W>%5!R=Ic7nm(0gJ~9NOvHyA zqWH2-6w^YmOy(DY{VrN6ErvZREuUMko@lVbdLDq*{A+_%F>!@6Z)X9kR1VI1+Ler+ zLUPtth=u~23=CqZoAbQ`uGE_91kR(8Ie$mq1p`q|ilkJ`Y-ob_=Nl(RF=o7k{47*I)F%_XMBz9uwRH8q1o$TkV@8Pwl zzi`^7i;K6Ak7o58a_D-V0AWp;H8pSjbEs$4BxoJkkC6UF@QNL)0$NU;Wv0*5 z0Ld;6tm7eR%u=`hnUb)gjHbE2cP?qpo3f4w%5qM0J*W_Kl6&z4YKX?iD@=McR!gTyhpGGYj!ljQm@2GL^J70`q~4CzPv@sz`s80FgiuxjAZ zLq61rHv1O>>w1qOEbVBwGu4%LGS!!muKHJ#JjfT>g`aSn>83Af<9gM3XBdY)Yql|{ zUds}u*;5wuus)D>HmexkC?;R&*Z`yB4;k;4T*(823M&52{pOd1yXvPJ3PPK{Zs>6w zztXy*HSH0scZHn7qIsZ8y-zftJ*uIW;%&-Ka0ExdpijI&xInDg-Bv-Q#Islcbz+R! zq|xz?3}G5W@*7jSd`Hv9q^5N*yN=4?Lh=LXS^5KJC=j|AJ5Y(f_fC-c4YQNtvAvn|(uP9@5Co{dL z?7|=jqTzD8>(6Wr&(XYUEzT~-VVErf@|KeFpKjh=v51iDYN_`Kg&XLOIG;ZI8*U$@ zKig{dy?1H}UbW%3jp@7EVSD>6c%#abQ^YfcO(`)*HuvNc|j( zyUbYozBR15$nNU$0ZAE%ivo4viW?@EprUZr6oX=4Sc!-WvrpJdF`3SwopKPyX~F>L zJ>N>v=_plttTSUq6bYu({&rkq)d94m5n~Sk_MO*gY*tlkPFd2m=Pi>MK)ObVV@Sgs zmXMNMvvcAuz+<$GLR2!j4w&;{)HEkxl{$B^*)lUKIn&p5_huD6+%WDoH4`p}9mkw$ zXCPw6Y7tc%rn$o_vy>%UNBC`0@+Ih-#T05AT)ooKt?94^ROI5;6m2pIM@@tdT=&WP z{u09xEVdD}{(3v}8AYUyT82;LV%P%TaJa%f)c36?=90z>Dzk5mF2}Gs0jYCmufihid8(VFcZWs8#59;JCn{!tHu5kSBbm zL`F{COgE01gg-qcP2Lt~M9}mALg@i?TZp&i9ZM^G<3`WSDh}+Ceb3Q!QecJ|N;Xrs z{wH{D8wQ2+mEfBX#M8)-32+~q4MRVr1UaSPtw}`iwx@x=1Xv-?UT{t}w}W(J&WKAC zrZ%hssvf*T!rs}}#atryn?LB=>0U%PLwA9IQZt$$UYrSw`7++}WR7tfE~*Qg)vRrM zT;(1>Zzka?wIIz8vfrG86oc^rjM@P7^i8D~b(S23AoKYj9HBC(6kq9g`1gN@|9^xO z{~h zbxGMHqGZ@eJ17bgES?HQnwp|G#7I>@p~o2zxWkgZUYSUeB*KT{1Q z*J3xZdWt`eBsA}7(bAHNcMPZf_BZC(WUR5B8wUQa=UV^e21>|yp+uop;$+#JwXD!> zunhJVCIKgaol0AM_AwJNl}_k&q|uD?aTE@{Q*&hxZ=k_>jcwp}KwG6mb5J*pV@K+- zj*`r0WuEU_8O=m&1!|rj9FG7ad<2px63;Gl z9lJrXx$~mPnuiqIH&n$jSt*ReG}1_?r4x&iV#3e_z+B4QbhHwdjiGu^J3vcazPi`| zaty}NFSWe=TDry*a*4XB)F;KDI$5i9!!(5p@5ra4*iW;FlGFV0P;OZXF!HCQ!oLm1 zsK+rY-FnJ?+yTBd0}{*Y6su|hul)wJ>RNQ{eau*;wWM{vWM`d0dTC-}Vwx6@cd#P? zx$Qyk^2*+_ZnMC}q0)+hE-q)PKoox#;pc%DNJ&D5+if6X4j~p$A7-s&AjDkSEV)aM z(<3UOw*&f)+^5F0Mpzw3zB1ZHl*B?C~Cx) zuNg*>5RM9F5{EpU@a2E7hAE`m<89wbQ2Lz&?Egu-^sglNXG5Q;{9n(%&*kEb0vApd zRHrY@22=pkFN81%x)~acZeu`yvK zovAVJNykgxqkEr^hZksHkpxm>2I8FTu2%+XLs@?ym0n;;A~X>i32{g6NOB@o4lk8{ zB}7Z2MNAJi>9u=y%s4QUXaNdt@SlAZr54!S6^ETWoik6gw=k-itu_}Yl_M9!l+Rbv z(S&WD`{_|SE@@(|Wp7bq1Zq}mc4JAG?mr2WN~6}~u`7M_F@J9`sr0frzxfuqSF~mA z$m$(TWAuCIE99yLSwi%R)8geQhs;6VBlRhJb(4Cx zu)QIF%_W9+21xI45U>JknBRaZ9nYkgAcK6~E|Zxo!B&z9zQhjsi^fgwZI%K@rYbMq znWBXg1uCZ+ljGJrsW7@x3h2 z;kn!J!bwCeOrBx;oPkZ}FeP%wExyf4=XMp)N8*lct~SyfK~4^-75EZFpHYO5AnuRM z!>u?>Vj3+j=uiHc<=cD~JWRphDSwxFaINB42-{@ZJTWe85>-RcQ&U%?wK)vjz z5u5fJYkck##j(bP7W0*RdW#BmAIK`D3=(U~?b`cJ&U2jHj}?w6 z_4BM)#EoJ6)2?pcR4AqBd)qAUn@RtNQq})FIQoBK4ie+GB(Vih2D|Ds>RJo2zE~C- z7mI)7p)5(-O6JRh6a@VZ5~piVC+Xv=O-)=0eTMSJsRE^c1@bPQWlr}E31VqO-%739 zdcmE{`1m;5LH8w|7euK>>>U#Iod8l1yivC>;YWsg=z#07E%cU9x1yw#3l6AcIm%79 zGi^zH6rM#CZMow(S(8dcOq#5$kbHnQV6s?MRsU3et!!YK5H?OV9vf2qy-UHCn>}2d zTwI(A_fzmmCtE@10yAGgU7R&|Fl$unZJ_^0BgCEDE6(B*SzfkapE9#0N6adc>}dtH zJ#nt^F~@JMJg4=Pv}OdUHyPt-<<9Z&c0@H@^4U?KwZM&6q0XjXc$>K3c&3iXLD9_%(?)?2kmZ=Ykb;)M`Tw=%_d=e@9eheGG zk0<`4so}r={C{zr|6+_1mA_=a56(XyJq||g6Es1E6%fPg#l{r+vk9;)r6VB7D84nu zE0Z1EIxH{Y@}hT+|#$0xn+CdMy6Uhh80eK~nfMEIpM z`|G1v!USmx81nY8XkhEOSWto}pc#{Ut#`Pqb}9j$FpzkQ7`0<-@5D_!mrLah98Mpr zz(R7;ZcaR-$aKqUaO!j z=7QT;Bu0cvYBi+LDfE_WZ`e@YaE_8CCxoRc?Y_!Xjnz~Gl|aYjN2&NtT5v4#q3od2 zkCQZHe#bn(5P#J**Fj4Py%SaaAKJsmV6}F_6Z7V&n6QAu8UQ#9{gkq+tB=VF_Q6~^ zf(hXvhJ#tC(eYm6g|I>;55Lq-;yY*COpTp4?J}hGQ42MIVI9CgEC{3hYw#CZfFKVG zgD(steIg8veyqX%pYMoulq zMUmbj8I`t>mC`!kZ@A>@PYXy*@NprM@e}W2Q+s?XIRM-U1FHVLM~c60(yz1<46-*j zW*FjTnBh$EzI|B|MRU11^McTPIGVJrzozlv$1nah_|t4~u}Ht^S1@V8r@IXAkN;lH z_s|WHlN90k4X}*#neR5bX%}?;G`X!1#U~@X6bbhgDYKJK17~oFF0&-UB#()c$&V<0 z7o~Pfye$P@$)Lj%T;axz+G1L_YQ*#(qO zQND$QTz(~8EF1c3<%;>dAiD$>8j@7WS$G_+ktE|Z?Cx<}HJb=!aChR&4z ziD&FwsiZ)wxS4k6KTLn>d~!DJ^78yb>?Trmx;GLHrbCBy|Bip<@sWdAfP0I~;(Ybr zoc-@j?wA!$ zIP0m3;LZy+>dl#&Ymws@7|{i1+OFLYf@+8+)w}n?mHUBCqg2=-Hb_sBb?=q))N7Ej zDIL9%@xQFOA!(EQmchHiDN%Omrr;WvlPIN5gW;u#ByV)x2aiOd2smy&;vA2+V!u|D zc~K(OVI8} z0t|e0OQ7h23e01O;%SJ}Q#yeDh`|jZR7j-mL(T4E;{w^}2hzmf_6PF|`gWVj{I?^2T3MBK>{?nMXed4kgNox2DP!jvP9v`;pa6AV)OD zDt*Vd-x7s{-;E?E5}3p-V;Y#dB-@c5vTWfS7<=>E+tN$ME`Z7K$px@!%{5{uV`cH80|IzU! zDs9=$%75P^QKCRQ`mW7$q9U?mU@vrFMvx)NNDrI(uk>xwO;^($EUvqVev#{W&GdtR z0ew;Iwa}(-5D28zABlC{WnN{heSY5Eq5Fc=TN^9X#R}0z53!xP85#@;2E=&oNYHyo z46~#Sf!1M1X!rh}ioe`>G2SkPH{5nCoP`GT@}rH;-LP1Q7U_ypw4+lwsqiBql80aA zJE<(88yw$`xzNiSnU(hsyJqHGac<}{Av)x9lQ=&py9djsh0uc}6QkmKN3{P!TEy;P zzLDVQj4>+0r<9B0owxBt5Uz`!M_VSS|{(?`_e+qD9b=vZHoo6>?u;!IP zM7sqoyP>kWY|=v06gkhaGRUrO8n@zE?Yh8$om@8%=1}*!2wdIWsbrCg@;6HfF?TEN z+B_xtSvT6H3in#8e~jvD7eE|LTQhO_>3b823&O_l$R$CFvP@3~)L7;_A}JpgN@ax{ z2d9Ra)~Yh%75wsmHK8e87yAn-ZMiLo6#=<&PgdFsJw1bby-j&3%&4=9dQFltFR(VB z@=6XmyNN4yr^^o$ON8d{PQ=!OX17^CrdM~7D-;ZrC!||<+FEOxI_WI3 zCA<35va%4v>gcEX-@h8esj=a4szW7x z{0g$hwoWRQG$yK{@3mqd-jYiVofJE!Wok1*nV7Gm&Ssq#hFuvj1sRyHg(6PFA5U*Q z8Rx>-blOs=lb`qa{zFy&n4xY;sd$fE+<3EI##W$P9M{B3c3Si9gw^jlPU-JqD~Cye z;wr=XkV7BSv#6}DrsXWFJ3eUNrc%7{=^sP>rp)BWKA9<}^R9g!0q7yWlh;gr_TEOD|#BmGq<@IV;ue zg+D2}cjpp+dPf&Q(36sFU&K8}hA85U61faW&{lB`9HUl-WWCG|<1XANN3JVAkRYvr5U z4q6;!G*MTdSUt*Mi=z_y3B1A9j-@aK{lNvxK%p23>M&=KTCgR!Ee8c?DAO2_R?Bkaqr6^BSP!8dHXxj%N1l+V$_%vzHjq zvu7p@%Nl6;>y*S}M!B=pz=aqUV#`;h%M0rUHfcog>kv3UZAEB*g7Er@t6CF8kHDmK zTjO@rejA^ULqn!`LwrEwOVmHx^;g|5PHm#B6~YD=gjJ!043F+&#_;D*mz%Q60=L9O zve|$gU&~As5^uz@2-BfQ!bW)Khn}G+Wyjw-19qI#oB(RSNydn0t~;tAmK!P-d{b-@ z@E5|cdgOS#!>%#Rj6ynkMvaW@37E>@hJP^82zk8VXx|3mR^JCcWdA|t{0nPmYFOxN z55#^-rlqobcr==<)bi?E?SPymF*a5oDDeSdO0gx?#KMoOd&G(2O@*W)HgX6y_aa6i zMCl^~`{@UR`nMQE`>n_{_aY5nA}vqU8mt8H`oa=g0SyiLd~BxAj2~l$zRSDHxvDs; zI4>+M$W`HbJ|g&P+$!U7-PHX4RAcR0szJ*(e-417=bO2q{492SWrqDK+L3#ChUHtz z*@MP)e^%@>_&#Yk^1|tv@j4%3T)diEXATx4K*hcO`sY$jk#jN5WD<=C3nvuVs zRh||qDHnc~;Kf59zr0;c7VkVSUPD%NnnJC_l3F^#f_rDu8l}l8qcAz0FFa)EAt32I zUy_JLIhU_J^l~FRH&6-iv zSpG2PRqzDdMWft>Zc(c)#tb%wgmWN%>IOPmZi-noqS!^Ft zb81pRcQi`X#UhWK70hy4tGW1mz|+vI8c*h@fFGJtW3r>qV>1Z0r|L>7I3un^gcep$ zAAWfZHRvB|E*kktY$qQP_$YG60C z@X~tTQjB3%@`uz!qxtxF+LE!+=nrS^07hn`EgAp!h|r03h7B!$#OZW#ACD+M;-5J!W+{h z|6I;5cNnE(Y863%1(oH}_FTW})8zYb$7czPg~Szk1+_NTm6SJ0MS_|oSz%e(S~P-& zSFp;!k?uFayytV$8HPwuyELSXOs^27XvK-DOx-Dl!P|28DK6iX>p#Yb%3`A&CG0X2 zS43FjN%IB}q(!hC$fG}yl1y9W&W&I@KTg6@K^kpH8=yFuP+vI^+59|3%Zqnb5lTDAykf9S#X`3N(X^SpdMyWQGOQRjhiwlj!0W-yD<3aEj^ z&X%=?`6lCy~?`&WSWt?U~EKFcCG_RJ(Qp7j=$I%H8t)Z@6Vj zA#>1f@EYiS8MRHZphpMA_5`znM=pzUpBPO)pXGYpQ6gkine{ z6u_o!P@Q+NKJ}k!_X7u|qfpAyIJb$_#3@wJ<1SE2Edkfk9C!0t%}8Yio09^F`YGzp zaJHGk*-ffsn85@)%4@`;Fv^8q(-Wk7r=Q8pT&hD`5(f?M{gfzGbbwh8(}G#|#fDuk z7v1W)5H9wkorE0ZZjL0Q1=NRGY>zwgfm81DdoaVwNH;or{{e zSyybt)m<=zXoA^RALYG-2touH|L*BLvmm9cdMmn+KGopyR@4*=&0 z&4g|FLoreZOhRmh=)R0bg~T2(8V_q7~42-zvb)+y959OAv!V$u(O z3)%Es0M@CRFmG{5sovIq4%8Ahjk#*5w{+)+MWQoJI_r$HxL5km1#6(e@{lK3Udc~n z0@g`g$s?VrnQJ$!oPnb?IHh-1qA`Rz$)Ai<6w$-MJW-gKNvOhL+XMbE7&mFt`x1KY z>k4(!KbbpZ`>`K@1J<(#vVbjx@Z@(6Q}MF#Mnbr-f55)vXj=^j+#)=s+ThMaV~E`B z8V=|W_fZWDwiso8tNMTNse)RNBGi=gVwgg%bOg8>mbRN%7^Um-7oj4=6`$|(K7!+t^90a{$1 z8Z>}<#!bm%ZEFQ{X(yBZMc>lCz0f1I2w9SquGh<9<=AO&g6BZte6hn>Qmvv;Rt)*c zJfTr2=~EnGD8P$v3R|&1RCl&7)b+`=QGapiPbLg_pxm`+HZurtFZ;wZ=`Vk*do~$wBxoW&=j0OTbQ=Q%S8XJ%~qoa3Ea|au5 zo}_(P;=!y z-AjFrERh%8la!z6Fn@lR?^E~H12D? z8#ht=1F;7@o4$Q8GDj;sSC%Jfn01xgL&%F2wG1|5ikb^qHv&9hT8w83+yv&BQXOQy zMVJSBL(Ky~p)gU3#%|blG?I zR9rP^zUbs7rOA0X52Ao=GRt@C&zlyjNLv-}9?*x{y(`509qhCV*B47f2hLrGl^<@S zuRGR!KwHei?!CM10pBKpDIoBNyRuO*>3FU?HjipIE#B~y3FSfOsMfj~F9PNr*H?0o zHyYB^G(YyNh{SxcE(Y-`x5jFMKb~HO*m+R%rq|ic4fzJ#USpTm;X7K+E%xsT_3VHK ze?*uc4-FsILUH;kL>_okY(w`VU*8+l>o>JmiU#?2^`>arnsl#)*R&nf_%>A+qwl%o z{l(u)M?DK1^mf260_oteV3#E_>6Y4!_hhVDM8AI6MM2V*^_M^sQ0dmHu11fy^kOqX zqzps-c5efIKWG`=Es(9&S@K@)ZjA{lj3ea7_MBPk(|hBFRjHVMN!sNUkrB;(cTP)T97M$ z0Dtc&UXSec<+q?y>5=)}S~{Z@ua;1xt@=T5I7{`Z=z_X*no8s>mY;>BvEXK%b`a6(DTS6t&b!vf_z#HM{Uoy z_5fiB(zpkF{})ruka$iX*~pq1ZxD?q68dIoIZSVls9kFGsTwvr4{T_LidcWtt$u{k zJlW7moRaH6+A5hW&;;2O#$oKyEN8kx z`LmG)Wfq4ykh+q{I3|RfVpkR&QH_x;t41UwxzRFXt^E2B$domKT@|nNW`EHwyj>&< zJatrLQ=_3X%vd%nHh^z@vIk(<5%IRAa&Hjzw`TSyVMLV^L$N5Kk_i3ey6byDt)F^U zuM+Ub4*8+XZpnnPUSBgu^ijLtQD>}K;eDpe1bNOh=fvIfk`&B61+S8ND<(KC%>y&? z>opCnY*r5M+!UrWKxv0_QvTlJc>X#AaI^xoaRXL}t5Ej_Z$y*|w*$6D+A?Lw-CO-$ zitm^{2Ct82-<0IW)0KMNvJHgBrdsIR0v~=H?n6^}l{D``Me90`^o|q!olsF?UX3YS zq^6Vu>Ijm>>PaZI8G@<^NGw{Cx&%|PwYrfwR!gX_%AR=L3BFsf8LxI|K^J}deh0Zd zV?$3r--FEX`#INxsOG6_=!v)DI>0q|BxT)z-G6kzA01M?rba+G_mwNMQD1mbVbNTW zmBi*{s_v_Ft9m2Avg!^78(QFu&n6mbRJ2bAv!b;%yo{g*9l2)>tsZJOOp}U~8VUH`}$8p_}t*XIOehezolNa-a2x0BS})Y9}& z*TPgua{Ewn-=wVrmJUeU39EKx+%w%=ixQWKDLpwaNJs65#6o7Ln7~~X+p_o2BR1g~ zVCfxLzxA{HlWAI6^H;`juI=&r1jQrUv_q0Z1Ja-tjdktrrP>GOC*#p?*xfQU5MqjM zsBe!9lh(u8)w$e@Z|>aUHI5o;MGw*|Myiz3-f0;pHg~Q#%*Kx8MxH%AluVXjG2C$) zWL-K63@Q`#y9_k_+}eR(x4~dp7oV-ek0H>Igy8p#i4GN{>#v=pFYUQT(g&b$OeTy- zX_#FDgNF8XyfGY6R!>inYn8IR2RDa&O!(6NIHrC0H+Qpam1bNa=(`SRKjixBTtm&e z`j9porEci!zdlg1RI0Jw#b(_Tb@RQK1Zxr_%7SUeH6=TrXt3J@js`4iDD0=I zoHhK~I7^W8^Rcp~Yaf>2wVe|Hh1bXa_A{oZ9eG$he;_xYvTbTD#moBy zY57-f2Ef1TP^lBi&p5_s7WGG9|0T}dlfxOxXvScJO1Cnq`c`~{Dp;{;l<-KkCDE+p zmexJkd}zCgE{eF=)K``-qC~IT6GcRog_)!X?fK^F8UDz$(zFUrwuR$qro5>qqn>+Z z%<5>;_*3pZ8QM|yv9CAtrAx;($>4l^_$_-L*&?(77!-=zvnCVW&kUcZMb6;2!83si z518Y%R*A3JZ8Is|kUCMu`!vxDgaWjs7^0j(iTaS4HhQ)ldR=r)_7vYFUr%THE}cPF z{0H45FJ5MQW^+W>P+eEX2kLp3zzFe*-pFVAdDZRybv?H|>`9f$AKVjFWJ=wegO7hO zOIYCtd?Vj{EYLT*^gl35|HbMX|NAEUf2ra9dy1=O;figB>La=~eA^#>O6n4?EMugV zbbt{Dbfef5l^(;}5kZ@!XaWwF8z0vUr6r|+QN*|WpF z^*osUHzOnE$lHuWYO$G7>}Y)bY0^9UY4eDV`E{s+{}Z$O$2*lMEYl zTA`ki(<0(Yrm~}15V-E^e2W6`*`%ydED-3G@$UFm6$ZtLx z+av`BhsHcAWqdxPWfu2*%{}|Sptax4_=NpDMeWy$* zZM6__s`enB$~0aT1BU^2k`J9F%+n+lL_|8JklWOCVYt*0%o*j4w1CsB_H^tVpYT_LLyKuyk=CV6~1M<7~^FylL*+AIFf3h>J=x$ygY-BG}4LJ z8XxYPY!v7dO3PVwEoY=`)6krokmR^|Mg5ztX_^#QR}ibr^X-|_St#rtv3gukh0(#A=};NPlNz57ZDFJ9hf#NP50zS)+Fo=StX)i@ zWS?W}i6LjB>kAB~lupAPyIjFb)izFgRq*iS*(Jt509jNr3r72{Gj`5DGoj;J&k5G@Rm!dJ($ox>SbxR)fc zz|Phug;~A7!p@?|mMva@rWuf2fSDK_ZxN3vVmlYz>rrf?LpiNs)^z!y{As@`55JC~ zS*GD3#N-ptY!2<613UelAJ;M4EEI$dm)`8#n$|o{ce^dlyoUY3bsy2hgnj-;ovubb zg2h1rZA6Ot}K_cpYBpIuF&CyK~5R0Wv;kG|3A^8K3nk{rw$Be8u@aos#qvKQKJyVU$cX6biw&Ep#+q7upFX z%qo&`WZ){<%zh@BTl{MO@v9#;t+cb7so0Uz49Fmo1e4>y!vUyIHadguZS0T7-x#_drMXz*16*c zymR0u^`ZQpXN}2ofegbpSedL%F9aypdQcrzjzPlBW0j zMlPzC&ePZ@Cq!?d%9oQNEg0`rHALm8l#lUdXMVEqDvb(AID~H(?H9z!e9G98fG@IzhajKr)3{L_Clu1(Bwg`RM!-(MOuZi zbeDsj9I3(~EITsE=3Z)a|l_rn8W92U0DB70gF7YYfO0j!)h?QobY1lSR>0 z_TVw@$eP~3k8r9;%g%RlZzCJ2%f}DvY`rsZ$;ak&^~-`i%B%+O!pnADeVyV!dHj|} zzOj#q4eRx9Q8c2Z7vy9L&fGLj+3_?fp}+8o`Xpwyi(81H|7P8#65%FIS*lOi={o&v z4NV$xu7az4Nb50dRGZv<tdZCx4Ek<_o3!mAT} zL5l*|K3Qr-)W8paaG z&R6{ped_4e2cy}ejD0!dt{*PaC*^L@eB%(1Fmc%Y#4)~!jF#lCGfj#E??4LG-T;!M z>Uha}f;W>ib_ZL-I7-v9KZQls^G!-JmL^w;=^}?!RXK;m4$#MwI2AH-l7M2-0 zVMK8k^+4+>2S0k^N_40EDa#`7c;2!&3-o6MHsnBfRnq@>E@)=hDulVq-g5SQWDWbt zj6H5?QS2gRZ^Zvbs~cW|8jagJV|;^zqC0e=D1oUsQPJ3MCb+eRGw(XgIY9y8v_tXq z9$(xWntWpx_Uronmvho{JfyYdV{L1N$^s^|-Nj`Ll`lUsiWTjm&8fadUGMXreJGw$ zQ**m+Tj|(XG}DyUKY~2?&9&n6SJ@9VKa9Hcayv{ar^pNr0WHy zP$bQv&8O!vd;GoT!pLwod-42qB^`m!b7nP@YTX}^+1hzA$}LSLh}Ln|?`%8xGMazw z8WT!LoYJ-Aq3=2p6ZSP~uMgSSWv3f`&-I06tU}WhZsA^6nr&r17hjQIZE>^pk=yZ% z06}dfR$85MjWJPq)T?OO(RxoaF+E#4{Z7)i9}Xsb;Nf+dzig61HO;@JX1Lf9)R5j9)Oi6vPL{H z&UQ9ln=$Q8jnh6-t;`hKM6pHftdd?$=1Aq16jty4-TF~`Gx=C&R242uxP{Y@Q~%O3 z*(16@x+vJsbW@^3tzY=-5MHi#(kB};CU%Ep`mVY1j$MAPpYJBB3x$ue`%t}wZ-@CG z(lBv36{2HMjxT)2$n%(UtHo{iW9>4HX4>)%k8QNnzIQYXrm-^M%#Qk%9odbUrZDz1YPdY`2Z4w~p!5tb^m(mUfk}kZ9+EsmenQ)5iwiaulcy zCJ#2o4Dz?@%)aAKfVXYMF;3t@aqNh2tBBlBkCdj`F31b=h93y(46zQ-YK@+zX5qM9 z&=KkN&3@Ptp*>UD$^q-WpG|9O)HBXz{D>p!`a36aPKkgz7uxEo0J>-o+4HHVD9!Hn z${LD0d{tuGsW*wvZoHc8mJroAs(3!FK@~<}Pz1+vY|Gw}Lwfxp{4DhgiQ_SSlV)E| zZWZxYZLu2EB1=g_y@(ieCQC_1?WNA0J0*}eMZfxCCs>oL;?kHdfMcKB+A)Qull$v( z2x6(38utR^-(?DG>d1GyU()8>ih3ud0@r&I$`ZSS<*1n6(76=OmP>r_JuNCdS|-8U zxGKXL1)Lc2kWY@`_kVBt^%7t9FyLVYX(g%a6>j=yURS1!V<9ieT$$5R+yT!I>}jI5 z?fem|T=Jq;BfZmsvqz_Ud*m5;&xE66*o*S22vf-L+MosmUPPA}~wy`kntf8rIeP-m;;{`xe}9E~G7J!PYoVH_$q~NzQab?F8vWUja5BJ!T5%5IpyqI#Dkps0B;gQ*z?c#N>spFw|wRE$gY?y4wQbJ zku2sVLh({KQz6e0yo+X!rV#8n8<;bHWd{ZLL_(*9Oi)&*`LBdGWz>h zx+p`Wi00u#V$f=CcMmEmgFjw+KnbK3`mbaKfoCsB{;Q^oJgj*LWnd_(dk9Kcssbj` z?*g8l`%{*LuY!Ls*|Tm`1Gv-tRparW8q4AK(5pfJFY5>@qO( zcY>pt*na>LlB^&O@YBDnWLE$x7>pMdSmb-?qMh79eB+Wa{)$%}^kX@Z3g>fytppz! zl%>pMD(Yw+5=!UgYHLD69JiJ;YhiGeEyZM$Au{ff;i zCBbNQfO{d!b7z^F732XX&qhEsJA1UZtJjJEIPyDq+F`LeAUU_4`%2aTX#3NG3%W8u zC!7OvlB?QJ4s2#Ok^_8SKcu&pBd}L?vLRT8Kow#xARt`5&Cg=ygYuz>>c z4)+Vv$;<$l=is&E{k&4Lf-Lzq#BHuWc;wDfm4Fbd5Sr!40s{UpKT$kzmUi{V0t1yp zPOf%H8ynE$x@dQ_!+ISaI}#%72UcYm7~|D*(Fp8xiFAj$CmQ4oH3C+Q8W=Y_9Sp|B z+k<%5=y{eW=YvTivV(*KvC?qxo)xqcEU9(Te=?ITts~;xA0Jph-vpd4@Zw#?r2!`? zB3#XtIY^wxrpjJv&(7Xjvm>$TIg2ZC&+^j(gT0R|&4cb)=92-2Hti1`& z=+M;*O%_j3>9zW|3h{0Tfh5i)Fa;clGNJpPRcUmgErzC{B+zACiPHbff3SmsCZ&X; zp=tgI=zW-t(5sXFL8;ITHw0?5FL3+*z5F-KcLN130l=jAU6%F=DClRPrzO|zY+HD`zlZ-)JT}X?2g!o zxg4Ld-mx6&*-N0-MQ(z+zJo8c`B39gf{-h2vqH<=^T&o1Dgd>4BnVht+JwLcrjJl1 zsP!8`>3-rSls07q2i1hScM&x0lQyBbk(U=#3hI7Bkh*kj6H*&^p+J?OMiT_3*vw5R zEl&p|QQHZq6f~TlAeDGy(^BC0vUK?V&#ezC0*#R-h}_8Cw8-*${mVfHssathC8%VA zUE^Qd!;Rvym%|f@?-!sEj|73Vg8!$$zj_QBZAOraF5HCFKl=(Ac|_p%-P;6z<2WSf zz(9jF2x7ZR{w+p)ETCW06PVt0YnZ>gW9^sr&~`%a_7j-Ful~*4=o|&TM@k@Px2z>^ t{*Ed16F~3V5p+(suF-++X8+nHtT~NSfJ>UC3v)>lEpV}<+rIR_{{yMcG_L>v literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..d6e308a6 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts new file mode 100644 index 00000000..6ef2e512 --- /dev/null +++ b/internal/common/build.gradle.kts @@ -0,0 +1,51 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { + id -> id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + + paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") +} + +val spigot = tasks.register("spigotRelocations") { + dependsOn(tasks.jar) + from(sourceSets.main.get().output) + relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf") + // TODO pass from Spigot adapter somehow. Can tasks be added by another project? Can we fetch a global config? + relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.v1_21_R3") + archiveClassifier = "spigot" +} + +configurations { + consumable("spigotRelocated") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) + } + } +} + +artifacts { + add("spigotRelocated", spigot) +} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/InternalAccessor.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java similarity index 83% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/InternalAccessor.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java index 4b22c13c..c9aea82d 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/InternalAccessor.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java @@ -1,19 +1,19 @@ -package com.lishid.openinv.internal.v1_21_R3; +package com.lishid.openinv.internal.common; import com.lishid.openinv.internal.Accessor; import com.lishid.openinv.internal.IAnySilentContainer; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.v1_21_R3.container.AnySilentContainer; -import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; -import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; +import com.lishid.openinv.internal.common.container.AnySilentContainer; +import com.lishid.openinv.internal.common.container.OpenEnderChest; +import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.player.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/AnySilentContainer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java similarity index 96% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/AnySilentContainer.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java index 39b52b67..823d3b5f 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/AnySilentContainer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java @@ -14,11 +14,11 @@ * along with this program. If not, see . */ -package com.lishid.openinv.internal.v1_21_R2.container; +package com.lishid.openinv.internal.common.container; import com.lishid.openinv.internal.AnySilentContainerBase; -import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenChestMenu; -import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; +import com.lishid.openinv.internal.common.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -61,10 +61,10 @@ public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) this.lang = lang; try { try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("gameModeForPlayer"); this.serverPlayerGameModeGameType.setAccessible(true); } catch (NoSuchFieldException e) { - logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); + logger.warning("The field ServerPlayerGameMode#gameModeForPlayer is no longer present!"); logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenEnderChest.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java similarity index 83% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenEnderChest.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java index eee8f317..4567ea1e 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenEnderChest.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java @@ -1,9 +1,9 @@ -package com.lishid.openinv.internal.v1_21_R3.container; +package com.lishid.openinv.internal.common.container; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenEnderChestMenu; -import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; +import com.lishid.openinv.internal.common.container.menu.OpenEnderChestMenu; +import com.lishid.openinv.internal.common.player.PlayerManager; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -15,8 +15,8 @@ import net.minecraft.world.inventory.StackedContentsCompatible; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -90,12 +90,12 @@ public boolean isEmpty() { } @Override - public ItemStack getItem(int index) { + public @NotNull ItemStack getItem(int index) { return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY; } @Override - public ItemStack removeItem(int index, int amount) { + public @NotNull ItemStack removeItem(int index, int amount) { ItemStack itemstack = ContainerHelper.removeItem(items, index, amount); if (!itemstack.isEmpty()) { @@ -106,12 +106,12 @@ public ItemStack removeItem(int index, int amount) { } @Override - public ItemStack removeItemNoUpdate(int index) { + public @NotNull ItemStack removeItemNoUpdate(int index) { return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY; } @Override - public void setItem(int index, ItemStack itemStack) { + public void setItem(int index, @NotNull ItemStack itemStack) { if (index >= 0 && index < items.size()) { items.set(index, itemStack); } @@ -128,27 +128,27 @@ public void setChanged() { } @Override - public boolean stillValid(Player player) { + public boolean stillValid(@NotNull Player player) { return true; } @Override - public List getContents() { + public @NotNull List getContents() { return items; } @Override - public void onOpen(CraftHumanEntity craftHumanEntity) { + public void onOpen(@NotNull CraftHumanEntity craftHumanEntity) { transaction.add(craftHumanEntity); } @Override - public void onClose(CraftHumanEntity craftHumanEntity) { + public void onClose(@NotNull CraftHumanEntity craftHumanEntity) { transaction.remove(craftHumanEntity); } @Override - public List getViewers() { + public @NotNull List getViewers() { return transaction; } @@ -174,7 +174,7 @@ public void clearContent() { } @Override - public void fillStackedContents(StackedItemContents stackedContents) { + public void fillStackedContents(@NotNull StackedItemContents stackedContents) { for (ItemStack itemstack : items) { stackedContents.accountStack(itemstack); } diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java similarity index 89% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenInventory.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 8f227240..899be70f 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -1,20 +1,20 @@ -package com.lishid.openinv.internal.v1_21_R3.container; +package com.lishid.openinv.internal.common.container; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventory; -import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenInventoryMenu; -import com.lishid.openinv.internal.v1_21_R3.container.slot.Content; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCrafting; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCraftingResult; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCursor; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentList; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentOffHand; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentViewOnly; -import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly; -import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; +import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory; +import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu; +import com.lishid.openinv.internal.common.container.slot.Content; +import com.lishid.openinv.internal.common.container.slot.ContentCrafting; +import com.lishid.openinv.internal.common.container.slot.ContentCraftingResult; +import com.lishid.openinv.internal.common.container.slot.ContentCursor; +import com.lishid.openinv.internal.common.container.slot.ContentDrop; +import com.lishid.openinv.internal.common.container.slot.ContentEquipment; +import com.lishid.openinv.internal.common.container.slot.ContentList; +import com.lishid.openinv.internal.common.container.slot.ContentOffHand; +import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.player.PlayerManager; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -29,8 +29,8 @@ import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.InventoryType; import org.jetbrains.annotations.NotNull; @@ -308,12 +308,12 @@ public void setMaxStackSize(int maxStackSize) { public void setChanged() {} @Override - public boolean stillValid(Player player) { + public boolean stillValid(@NotNull Player player) { return true; } @Override - public List getContents() { + public @NotNull List getContents() { NonNullList contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); for (int i = 0; i < getContainerSize(); ++i) { contents.set(i, getItem(i)); @@ -337,7 +337,7 @@ public List getViewers() { } @Override - public org.bukkit.entity.Player getOwner() { + public @NotNull org.bukkit.entity.Player getOwner() { return owner.getBukkitEntity(); } diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/Placeholders.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/Placeholders.java similarity index 97% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/Placeholders.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/Placeholders.java index 7a95351c..75a0834e 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/Placeholders.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/Placeholders.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R3.container; +package com.lishid.openinv.internal.common.container; +import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; @@ -20,7 +21,7 @@ import net.minecraft.world.level.block.entity.BannerPatternLayers; import net.minecraft.world.level.block.entity.BannerPatterns; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_21_R3.CraftRegistry; +import org.bukkit.craftbukkit.CraftRegistry; import org.jetbrains.annotations.NotNull; import java.util.EnumMap; @@ -84,7 +85,7 @@ public static void load(@NotNull ConfigurationSection section) throws Exception } public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { - if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { + if (!OpenPlayer.isConnected(serverPlayer.connection)) { return blockedOffline; } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java similarity index 96% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java index 6fdab659..840bbaae 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java @@ -1,9 +1,9 @@ -package com.lishid.openinv.internal.v1_21_R2.container.bukkit; +package com.lishid.openinv.internal.common.container.bukkit; import com.lishid.openinv.internal.ViewOnly; import net.minecraft.world.Container; import org.bukkit.Material; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java similarity index 97% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java index 17bb2a04..0347e135 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenDummyPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R2.container.bukkit; +package com.lishid.openinv.internal.common.container.bukkit; import net.minecraft.world.Container; import org.bukkit.Material; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java similarity index 96% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java index 01be5ba1..a5a6339d 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java @@ -1,12 +1,12 @@ -package com.lishid.openinv.internal.v1_21_R2.container.bukkit; +package com.lishid.openinv.internal.common.container.bukkit; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import com.lishid.openinv.internal.common.container.OpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java similarity index 79% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java index e44eb4c9..165211f2 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/bukkit/OpenPlayerInventorySelf.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R2.container.bukkit; +package com.lishid.openinv.internal.common.container.bukkit; -import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import com.lishid.openinv.internal.common.container.OpenInventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java similarity index 94% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index de3dcf29..9c73170c 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -1,11 +1,11 @@ -package com.lishid.openinv.internal.v1_21_R3.container.menu; +package com.lishid.openinv.internal.common.container.menu; import com.google.common.base.Suppliers; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyInventory; -import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotPlaceholder; -import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.server.level.ServerPlayer; @@ -21,7 +21,7 @@ import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; @@ -177,9 +177,7 @@ public int convertSlot(int rawSlot) { private int getTopSize(ServerPlayer viewer) { MenuType menuType = getType(); - if (menuType == null) { - throw new IllegalStateException("MenuType cannot be null!"); - } else if (menuType == MenuType.GENERIC_9x1) { + if (menuType == MenuType.GENERIC_9x1) { return 9; } else if (menuType == MenuType.GENERIC_9x2) { return 18; @@ -285,7 +283,7 @@ private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { } @Override - public void clicked(int i, int j, ClickType clickType, Player player) { + public void clicked(int i, int j, @NotNull ClickType clickType, @NotNull Player player) { if (viewOnly) { if (clickType == ClickType.QUICK_CRAFT) { sendAllDataToRemote(); @@ -296,13 +294,13 @@ public void clicked(int i, int j, ClickType clickType, Player player) { } @Override - public boolean stillValid(Player player) { + public boolean stillValid(@NotNull Player player) { return true; } // Overrides from here on are purely to modify the sync process to send placeholder items. @Override - protected Slot addSlot(Slot slot) { + protected @NotNull Slot addSlot(@NotNull Slot slot) { slot.index = this.slots.size(); this.slots.add(slot); this.lastSlots.add(ItemStack.EMPTY); @@ -311,7 +309,7 @@ protected Slot addSlot(Slot slot) { } @Override - protected DataSlot addDataSlot(DataSlot dataSlot) { + protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) { this.dataSlots.add(dataSlot); this.remoteDataSlots.add(0); return dataSlot; @@ -325,7 +323,7 @@ protected void addDataSlots(ContainerData containerData) { } @Override - public void addSlotListener(ContainerListener containerListener) { + public void addSlotListener(@NotNull ContainerListener containerListener) { if (!this.containerListeners.contains(containerListener)) { this.containerListeners.add(containerListener); this.broadcastChanges(); @@ -333,7 +331,7 @@ public void addSlotListener(ContainerListener containerListener) { } @Override - public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { + public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { this.synchronizer = containerSynchronizer; this.sendAllDataToRemote(); } @@ -365,7 +363,7 @@ public void broadcastCarriedItem() { } @Override - public void removeSlotListener(ContainerListener containerListener) { + public void removeSlotListener(@NotNull ContainerListener containerListener) { this.containerListeners.remove(containerListener); } diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenEnderChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java similarity index 85% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenEnderChestMenu.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java index 075c0849..bfd6c619 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenEnderChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R3.container.menu; +package com.lishid.openinv.internal.common.container.menu; -import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest; +import com.lishid.openinv.internal.common.container.OpenEnderChest; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; @@ -18,7 +18,7 @@ public OpenEnderChestMenu( } @Override - public ItemStack quickMoveStack(Player player, int index) { + public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) { if (viewOnly) { return ItemStack.EMPTY; } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java similarity index 91% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java index dc80ae30..afe70bad 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenInventoryMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java @@ -1,12 +1,12 @@ -package com.lishid.openinv.internal.v1_21_R2.container.menu; +package com.lishid.openinv.internal.common.container.menu; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyPlayerInventory; -import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventorySelf; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.internal.common.container.bukkit.OpenDummyPlayerInventory; +import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventorySelf; +import com.lishid.openinv.internal.common.container.slot.ContentDrop; +import com.lishid.openinv.internal.common.container.slot.ContentEquipment; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; import com.lishid.openinv.util.Permissions; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EquipmentSlot; @@ -15,8 +15,8 @@ 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.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; @@ -195,7 +195,7 @@ public int countSlots() { } @Override - public ItemStack quickMoveStack(Player player, int index) { + public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) { if (viewOnly) { return ItemStack.EMPTY; } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/Content.java similarity index 96% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/Content.java index 15c04fef..9b63ed54 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/Content.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/Content.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java similarity index 89% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java index fb71cf40..4621af79 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCrafting.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java @@ -1,6 +1,7 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; @@ -34,7 +35,7 @@ static boolean isAvailable(@NotNull ServerPlayer holder) { // Player must be online and not in creative - since the creative client is (semi-)authoritative, // it ignores changes without extra help, and will delete the item as a result. // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. - return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); + return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival(); } @Override @@ -107,12 +108,12 @@ public ItemStack getOrDefault() { } @Override - public boolean mayPickup(Player player) { + public boolean mayPickup(@NotNull Player player) { return isAvailable(); } @Override - public boolean mayPlace(ItemStack itemStack) { + public boolean mayPlace(@NotNull ItemStack itemStack) { return isAvailable(); } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java similarity index 90% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java index 62e9a62b..57b612e5 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCraftingResult.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import com.lishid.openinv.internal.common.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.InventoryMenu; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java similarity index 88% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java index 41cbb3f7..d0b16f17 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentCursor.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java @@ -1,6 +1,7 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; @@ -63,7 +64,7 @@ private boolean isAvailable() { // Player must be online and not in creative - since the creative client is (semi-)authoritative, // it ignores changes without extra help, and will delete the item as a result. // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. - return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); + return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival(); } @Override @@ -93,12 +94,12 @@ public ItemStack getOrDefault() { } @Override - public boolean mayPickup(Player player) { + public boolean mayPickup(@NotNull Player player) { return isAvailable(); } @Override - public boolean mayPlace(ItemStack itemStack) { + public boolean mayPlace(@NotNull ItemStack itemStack) { return isAvailable(); } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java similarity index 82% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java index ebe52541..1d3d8b0e 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentDrop.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java @@ -1,6 +1,7 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; @@ -63,14 +64,14 @@ private SlotDrop(Container container, int index, int x, int y) { @Override public ItemStack getOrDefault() { - return holder.connection != null && !holder.connection.isDisconnected() + return OpenPlayer.isConnected(holder.connection) ? Placeholders.drop : Placeholders.blockedOffline; } @Override - public boolean mayPlace(ItemStack itemStack) { - return holder.connection != null && !holder.connection.isDisconnected(); + public boolean mayPlace(@NotNull ItemStack itemStack) { + return OpenPlayer.isConnected(holder.connection); } @Override diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentEquipment.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java similarity index 89% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentEquipment.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java index 9fe03914..60731e4a 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentEquipment.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; +package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; +import com.lishid.openinv.internal.common.container.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; @@ -65,12 +65,12 @@ public void onlyEquipmentFor(ServerPlayer viewer) { } @Override - public boolean mayPlace(ItemStack var0) { + public boolean mayPlace(@NotNull ItemStack itemStack) { if (viewer == null) { return true; } - return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot; + return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(itemStack) == equipmentSlot; } } diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentList.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentList.java similarity index 95% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentList.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentList.java index e9483b95..5748c51a 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentList.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentList.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; +package com.lishid.openinv.internal.common.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java similarity index 86% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java index cc7f3337..465472df 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentOffHand.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java @@ -1,5 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; +package com.lishid.openinv.internal.common.container.slot; +import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -36,9 +37,7 @@ public Slot asSlot(Container container, int slot, int x, int y) { return new SlotEquipment(container, slot, x, y) { @Override public void setChanged() { - if (holder.connection != null - && !holder.connection.isDisconnected() - && holder.containerMenu != holder.inventoryMenu) { + if (OpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { holder.connection.send( new ClientboundContainerSetSlotPacket( holder.inventoryMenu.containerId, diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentViewOnly.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java similarity index 95% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentViewOnly.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java index 6d78e6e0..46076658 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentViewOnly.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; +package com.lishid.openinv.internal.common.container.slot; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotPlaceholder.java similarity index 89% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotPlaceholder.java index ef0bf7a0..7e7a79ad 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotPlaceholder.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotPlaceholder.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java similarity index 65% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java index 7727775d..7fb43763 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/SlotViewOnly.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java @@ -1,6 +1,6 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; +package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; +import com.lishid.openinv.internal.common.container.Placeholders; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; @@ -45,20 +45,20 @@ public ItemStack getOrDefault() { } @Override - public void onQuickCraft(ItemStack var0, ItemStack var1) { + public void onQuickCraft(@NotNull ItemStack itemStack1, @NotNull ItemStack itemStack2) { } @Override - public void onTake(Player var0, ItemStack var1) { + public void onTake(@NotNull Player player, @NotNull ItemStack itemStack) { } @Override - public boolean mayPlace(ItemStack var0) { + public boolean mayPlace(@NotNull ItemStack itemStack) { return false; } @Override - public ItemStack getItem() { + public @NotNull ItemStack getItem() { return ItemStack.EMPTY; } @@ -68,15 +68,15 @@ public boolean hasItem() { } @Override - public void setByPlayer(ItemStack newStack) { + public void setByPlayer(@NotNull ItemStack newStack) { } @Override - public void setByPlayer(ItemStack newStack, ItemStack oldStack) { + public void setByPlayer(@NotNull ItemStack newStack, @NotNull ItemStack oldStack) { } @Override - public void set(ItemStack var0) { + public void set(@NotNull ItemStack itemStack) { } @Override @@ -89,17 +89,17 @@ public int getMaxStackSize() { } @Override - public int getMaxStackSize(ItemStack itemStack) { + public int getMaxStackSize(@NotNull ItemStack itemStack) { return 0; } @Override - public ItemStack remove(int amount) { + public @NotNull ItemStack remove(int amount) { return ItemStack.EMPTY; } @Override - public boolean mayPickup(Player var0) { + public boolean mayPickup(@NotNull Player player) { return false; } @@ -109,27 +109,27 @@ public boolean isActive() { } @Override - public Optional tryRemove(int var0, int var1, Player var2) { + public @NotNull Optional tryRemove(int var0, int var1, @NotNull Player player) { return Optional.empty(); } @Override - public ItemStack safeTake(int var0, int var1, Player var2) { + public @NotNull ItemStack safeTake(int var0, int var1, @NotNull Player player) { return ItemStack.EMPTY; } @Override - public ItemStack safeInsert(ItemStack itemStack) { + public @NotNull ItemStack safeInsert(@NotNull ItemStack itemStack) { return itemStack; } @Override - public ItemStack safeInsert(ItemStack itemStack, int amount) { + public @NotNull ItemStack safeInsert(@NotNull ItemStack itemStack, int amount) { return itemStack; } @Override - public boolean allowModification(Player var0) { + public boolean allowModification(@NotNull Player player) { return false; } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java similarity index 93% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 4005359a..54a770f7 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -1,4 +1,4 @@ -package com.lishid.openinv.internal.v1_21_R2.player; +package com.lishid.openinv.internal.common.player; import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; @@ -8,9 +8,10 @@ import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_21_R2.CraftServer; -import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftPlayer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -175,4 +176,8 @@ private void setTag( } } + public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) { + return connection != null && !connection.isDisconnected(); + } + } diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java similarity index 91% rename from internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index 2bc534ec..c1e31815 100644 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -1,8 +1,8 @@ -package com.lishid.openinv.internal.v1_21_R2.player; +package com.lishid.openinv.internal.common.player; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; +import com.lishid.openinv.internal.common.container.OpenEnderChest; +import com.lishid.openinv.internal.common.container.OpenInventory; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -23,10 +23,10 @@ import org.bukkit.OfflinePlayer; import org.bukkit.Server; import org.bukkit.World; -import org.bukkit.craftbukkit.v1_21_R2.CraftServer; -import org.bukkit.craftbukkit.v1_21_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -181,16 +181,25 @@ private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loade .resultOrPartial(logger::warning) .map(player.server::getLevel) // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); return; } if (bukkitWorld == null) { - player.spawnIn(null); + spawnInDefaultWorld(player); return; } player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } + private void spawnInDefaultWorld(ServerPlayer player) { + ServerLevel level = player.server.getLevel(Level.OVERWORLD); + if (level != null) { + player.spawnIn(level); + } else { + logger.warning("Tried to load player with invalid world when no fallback was available!"); + } + } + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; @@ -223,7 +232,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) { ServerPlayer player = getHandle(bukkitPlayer); - if (player.connection == null) { + if (!OpenPlayer.isConnected(player.connection)) { return null; } diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts new file mode 100644 index 00000000..d4d58426 --- /dev/null +++ b/internal/spigot/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + `remap-spigot` + `openinv-base` + alias(libs.plugins.shadow) +} + +repositories { + mavenLocal() +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val spigot = candidates.firstOrNull { + it.id.let { + id -> id is ModuleComponentIdentifier && id.module == "spigot-api" + } + } + if (spigot != null) { + select(spigot) + } + because("module is written for Spigot servers") + } +} + +dependencies { + compileOnly(libs.spigotapi) + compileOnly(variantOf(libs.spigot) { classifier("remapped-mojang") }) + + compileOnly(project(":openinvapi")) + compileOnly(project(":openinvcommon")) + + // Reduce duplicate code by lightly remapping common adapter. + implementation(project(":openinvadaptercommon", configuration = "spigotRelocated")) +} + +tasks.shadowJar { + relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf") +} + +// TODO this appears to be a deprecated way to do things +// may want to just move all helper methods here. +tasks.register("reobf") { + notCompatibleWithConfigurationCache("gradle is hard") + dependsOn(tasks.shadowJar) + inputs.files(tasks.shadowJar.get().outputs.files.files) + spigotVersion = libs.spigot.get().version.toString() +} diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java new file mode 100644 index 00000000..fe1ab4fa --- /dev/null +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java @@ -0,0 +1,194 @@ +package com.lishid.openinv.internal.reobf.container; + +import com.lishid.openinv.internal.AnySilentContainerBase; +import com.lishid.openinv.internal.reobf.container.menu.OpenChestMenu; +import com.lishid.openinv.internal.reobf.player.PlayerManager; +import com.lishid.openinv.util.ReflectionHelper; +import com.lishid.openinv.util.lang.LanguageManager; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.TrappedChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.logging.Logger; + +public class AnySilentContainer extends AnySilentContainerBase { + + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; + try { + try { + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + logger.warning("The mapping of ServerPlayerGameMode#gameModeForPlayer has changed!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); + } + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); + + final net.minecraft.world.level.Level level = player.level(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); + + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, Component.translatable("container.enderchest"))); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: do not check if chest is blocked + menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); + + if (menuProvider == null) { + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; + } + + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + } + + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { + return; + } + + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // ServerPlayer#closeContainer cannot be called without entering an + // infinite loop because this method is called during inventory close. + // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent + player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); + // From ServerPlayer#closeContainer + player.doCloseContainer(); + // Regular inventory close will handle the rest - packet sending, etc. + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); + } + } + +} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java similarity index 93% rename from internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/PlayerManager.java rename to internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index ef44fbd1..ea3504fc 100644 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -1,8 +1,8 @@ -package com.lishid.openinv.internal.v1_21_R3.player; +package com.lishid.openinv.internal.reobf.player; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; +import com.lishid.openinv.internal.reobf.container.OpenEnderChest; +import com.lishid.openinv.internal.reobf.container.OpenInventory; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; @@ -181,16 +181,25 @@ private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loade .resultOrPartial(logger::warning) .map(player.server::getLevel) // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null)); + .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); return; } if (bukkitWorld == null) { - player.spawnIn(null); + spawnInDefaultWorld(player); return; } player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } + private void spawnInDefaultWorld(ServerPlayer player) { + ServerLevel level = player.server.getLevel(Level.OVERWORLD); + if (level != null) { + player.spawnIn(level); + } else { + logger.warning("Tried to load player with invalid world when no fallback was available!"); + } + } + private void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; @@ -223,7 +232,7 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) { ServerPlayer player = getHandle(bukkitPlayer); - if (player.connection == null) { + if (!OpenPlayer.isConnected(player.connection)) { return null; } diff --git a/internal/v1_21_R2/pom.xml b/internal/v1_21_R2/pom.xml deleted file mode 100644 index ea088dfe..00000000 --- a/internal/v1_21_R2/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 5.1.7-SNAPSHOT - - - openinvadapter1_21_R2 - OpenInvAdapter1_21_R2 - - - 21 - 21 - 1.21.3-R0.1-SNAPSHOT - - - - - org.spigotmc - spigot-api - ${spigot.version} - - - spigot - org.spigotmc - provided - ${spigot.version} - remapped-mojang - - - openinvapi - com.lishid - - - openinvcommon - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-compiler-plugin - - - net.md-5 - specialsource-maven-plugin - - - - - diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java deleted file mode 100644 index 3b873935..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/InternalAccessor.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2; - -import com.lishid.openinv.internal.Accessor; -import com.lishid.openinv.internal.IAnySilentContainer; -import com.lishid.openinv.internal.ISpecialEnderChest; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.v1_21_R2.container.AnySilentContainer; -import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest; -import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; -import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; -import com.lishid.openinv.util.lang.LanguageManager; -import net.minecraft.world.Container; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class InternalAccessor implements Accessor { - - private final @NotNull Logger logger; - private final @NotNull PlayerManager manager; - private final @NotNull AnySilentContainer anySilentContainer; - - public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - this.logger = logger; - manager = new PlayerManager(logger); - anySilentContainer = new AnySilentContainer(logger, lang); - } - - @Override - public @NotNull PlayerManager getPlayerManager() { - return manager; - } - - @Override - public @NotNull IAnySilentContainer getAnySilentContainer() { - return anySilentContainer; - } - - @Override - public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { - return new OpenInventory(player); - } - - @Override - public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { - return new OpenEnderChest(player); - } - - @Override - public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { - if (!(bukkitInventory instanceof CraftInventory craftInventory)) { - return null; - } - Container container = craftInventory.getInventory(); - if (clazz.isInstance(container)) { - return clazz.cast(container); - } - return null; - } - - @Override - public void reload(@NotNull ConfigurationSection config) { - ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); - if (placeholders != null) { - try { - Placeholders.load(placeholders); - } catch (Exception e) { - logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); - } - } - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java deleted file mode 100644 index 2f0b3d97..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenEnderChest.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container; - -import com.lishid.openinv.internal.ISpecialEnderChest; -import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenEnderChestMenu; -import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; -import net.minecraft.core.NonNullList; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedItemContents; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.StackedContentsCompatible; -import net.minecraft.world.item.ItemStack; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned, - ISpecialEnderChest { - - private CraftInventory inventory; - private @NotNull ServerPlayer owner; - private NonNullList items; - private int maxStack = 64; - private final List transaction = new ArrayList<>(); - - public OpenEnderChest(@NotNull org.bukkit.entity.Player player) { - this.owner = PlayerManager.getHandle(player); - this.items = owner.getEnderChestInventory().items; - } - - @Override - public @NotNull ServerPlayer getOwnerHandle() { - return owner; - } - - @Override - public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { - if (inventory == null) { - inventory = new CraftInventory(this) { - @Override - public @NotNull InventoryType getType() { - return InventoryType.ENDER_CHEST; - } - }; - } - return inventory; - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - owner = PlayerManager.getHandle(player); - NonNullList activeItems = owner.getEnderChestInventory().items; - - // Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally. - int max = Math.min(items.size(), activeItems.size()); - for (int index = 0; index < max; ++index) { - activeItems.set(index, items.get(index)); - } - - items = activeItems; - } - - @Override - public void setPlayerOffline() {} - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return owner.getBukkitEntity(); - } - - @Override - public int getContainerSize() { - return items.size(); - } - - @Override - public boolean isEmpty() { - return items.stream().allMatch(ItemStack::isEmpty); - } - - @Override - public ItemStack getItem(int index) { - return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY; - } - - @Override - public ItemStack removeItem(int index, int amount) { - ItemStack itemstack = ContainerHelper.removeItem(items, index, amount); - - if (!itemstack.isEmpty()) { - setChanged(); - } - - return itemstack; - } - - @Override - public ItemStack removeItemNoUpdate(int index) { - return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY; - } - - @Override - public void setItem(int index, ItemStack itemStack) { - if (index >= 0 && index < items.size()) { - items.set(index, itemStack); - } - } - - @Override - public int getMaxStackSize() { - return maxStack; - } - - @Override - public void setChanged() { - this.owner.getEnderChestInventory().setChanged(); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public List getContents() { - return items; - } - - @Override - public void onOpen(CraftHumanEntity craftHumanEntity) { - transaction.add(craftHumanEntity); - } - - @Override - public void onClose(CraftHumanEntity craftHumanEntity) { - transaction.remove(craftHumanEntity); - } - - @Override - public List getViewers() { - return transaction; - } - - @Override - public org.bukkit.entity.Player getOwner() { - return getPlayer(); - } - - @Override - public void setMaxStackSize(int size) { - maxStack = size; - } - - @Override - public @Nullable Location getLocation() { - return null; - } - - @Override - public void clearContent() { - items.clear(); - setChanged(); - } - - @Override - public void fillStackedContents(StackedItemContents stackedContents) { - for (ItemStack itemstack : items) { - stackedContents.accountStack(itemstack); - } - } - - public Component getTitle() { - return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName()) - .append(Component.translatable("container.enderchest")) - .append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName())); - } - - public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { - if (player instanceof ServerPlayer serverPlayer) { - return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly); - } - return null; - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java deleted file mode 100644 index ef3113ff..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/OpenInventory.java +++ /dev/null @@ -1,364 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container; - -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventory; -import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenInventoryMenu; -import com.lishid.openinv.internal.v1_21_R2.container.slot.Content; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCrafting; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCraftingResult; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCursor; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentList; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentOffHand; -import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentViewOnly; -import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly; -import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager; -import net.minecraft.ChatFormatting; -import net.minecraft.core.NonNullList; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -public class OpenInventory implements Container, InternalOwned, ISpecialPlayerInventory { - - private final List slots; - private final int size; - private ServerPlayer owner; - private int maxStackSize = 99; - private CraftInventory bukkitEntity; - public List transaction = new ArrayList<>(); - - public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { - owner = PlayerManager.getHandle(bukkitPlayer); - - // Get total size, rounding up to nearest 9 for client compatibility. - int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; - size = ((int) Math.ceil(rawSize / 9.0)) * 9; - - slots = NonNullList.withSize(size, new ContentViewOnly(owner)); - setupSlots(); - } - - private void setupSlots() { - // Top of inventory: Regular contents. - int nextIndex = addMainInventory(); - - // If inventory is expected size, we can arrange slots to be pretty. - Inventory ownerInv = owner.getInventory(); - if (ownerInv.items.size() == 36 - && ownerInv.armor.size() == 4 - && ownerInv.offhand.size() == 1 - && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) { - // Armor slots: Bottom left. - addArmor(36); - // Off-hand: Below chestplate. - addOffHand(46); - // Drop slot: Bottom right. - slots.set(53, new ContentDrop(owner)); - // Cursor slot: Above drop. - slots.set(44, new ContentCursor(owner)); - - // Crafting is displayed in the bottom right corner. - // As we're using the pretty view, this is a 3x2. - addCrafting(41, true); - return; - } - - // Otherwise we'll just add elements linearly. - nextIndex = addArmor(nextIndex); - nextIndex = addOffHand(nextIndex); - nextIndex = addCrafting(nextIndex, false); - slots.set(nextIndex, new ContentCursor(owner)); - // Drop slot last. - slots.set(slots.size() - 1, new ContentDrop(owner)); - } - - private int addMainInventory() { - int listSize = owner.getInventory().items.size(); - // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, - // so everything else needs to move up a row. - int hotbarDiff = listSize - 9; - for (int localIndex = 0; localIndex < listSize; ++localIndex) { - InventoryType.SlotType type; - int invIndex; - if (localIndex < hotbarDiff) { - invIndex = localIndex + 9; - type = InventoryType.SlotType.CONTAINER; - } else { - type = InventoryType.SlotType.QUICKBAR; - invIndex = localIndex - hotbarDiff; - } - - slots.set(localIndex, new ContentList(owner, invIndex, type) { - @Override - public void setHolder(@NotNull ServerPlayer holder) { - items = holder.getInventory().items; - } - }); - } - return listSize; - } - - private int addArmor(int startIndex) { - int listSize = owner.getInventory().armor.size(); - - for (int i = 0; i < listSize; ++i) { - // Armor slots go bottom to top; boots are slot 0, helmet is slot 3. - // Since we have to display horizontally due to space restrictions, - // making the left side the "top" is more user-friendly. - int armorIndex; - EquipmentSlot slot; - switch (i) { - case 3 -> { - armorIndex = 0; - slot = EquipmentSlot.FEET; - } - case 2 -> { - armorIndex = 1; - slot = EquipmentSlot.LEGS; - } - case 1 -> { - armorIndex = 2; - slot = EquipmentSlot.CHEST; - } - case 0 -> { - armorIndex = 3; - slot = EquipmentSlot.HEAD; - } - default -> { - // In the event that new armor slots are added, they can be placed at the end. - armorIndex = i; - slot = EquipmentSlot.MAINHAND; - } - } - - slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot)); - } - - return startIndex + listSize; - } - - private int addOffHand(int startIndex) { - int listSize = owner.getInventory().offhand.size(); - for (int localIndex = 0; localIndex < listSize; ++localIndex) { - slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex)); - } - return startIndex + listSize; - } - - private int addCrafting(int startIndex, boolean pretty) { - int listSize = owner.inventoryMenu.getCraftSlots().getContents().size(); - pretty &= listSize == 4; - - for (int localIndex = 0; localIndex < listSize; ++localIndex) { - // Pretty display is a 2x2 rather than linear. - // If index is in top row, grid is not 2x2, or pretty is disabled, just use current index. - // Otherwise, subtract 2 and add 9 to start in the same position on the next row. - int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); - - slots.set(modIndex, new ContentCrafting(owner, localIndex)); - } - - if (pretty) { - slots.set(startIndex + 2, new ContentViewOnly(owner) { - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotViewOnly(container, slot, x, y) { - @Override - public ItemStack getOrDefault() { - return Placeholders.craftingOutput; - } - }; - } - }); - slots.set(startIndex + 11, new ContentCraftingResult(owner)); - } - - return startIndex + listSize; - } - - public Slot getMenuSlot(int index, int x, int y) { - return slots.get(index).asSlot(this, index, x, y); - } - - public InventoryType.SlotType getSlotType(int index) { - return slots.get(index).getSlotType(); - } - - public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { - MutableComponent component = Component.empty(); - // Prefix for use with custom bitmap image fonts. - if (owner.equals(viewer)) { - component.append( - Component.translatableWithFallback("openinv.container.inventory.self", "") - .withStyle(style -> style - .withFont(ResourceLocation.parse("openinv:font/inventory")) - .withColor(ChatFormatting.WHITE))); - } else { - component.append( - Component.translatableWithFallback("openinv.container.inventory.other", "") - .withStyle(style -> style - .withFont(ResourceLocation.parse("openinv:font/inventory")) - .withColor(ChatFormatting.WHITE))); - } - // Normal title: "Inventory - OwnerName" - component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) - .append(Component.translatable("container.inventory")) - .append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName())); - return component; - } - - @Override - public ServerPlayer getOwnerHandle() { - return owner; - } - - @Override - public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { - if (bukkitEntity == null) { - bukkitEntity = new OpenPlayerInventory(this); - } - return bukkitEntity; - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - ServerPlayer newOwner = PlayerManager.getHandle(player); - // Only transfer regular inventory - crafting and cursor slots are transient. - newOwner.getInventory().replaceWith(owner.getInventory()); - owner = newOwner; - // Update slots to point to new inventory. - slots.forEach(slot -> slot.setHolder(newOwner)); - } - - @Override - public void setPlayerOffline() {} - - @Override - public boolean isInUse() { - return !transaction.isEmpty(); - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return getOwner(); - } - - @Override - public int getContainerSize() { - return size; - } - - @Override - public boolean isEmpty() { - return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty); - } - - @Override - public ItemStack getItem(int index) { - return slots.get(index).get(); - } - - @Override - public ItemStack removeItem(int index, int amount) { - return slots.get(index).removePartial(amount); - } - - @Override - public ItemStack removeItemNoUpdate(int index) { - return slots.get(index).remove(); - } - - @Override - public void setItem(int index, ItemStack itemStack) { - slots.get(index).set(itemStack); - } - - @Override - public int getMaxStackSize() { - return maxStackSize; - } - - @Override - public void setMaxStackSize(int maxStackSize) { - this.maxStackSize = maxStackSize; - } - - @Override - public void setChanged() {} - - @Override - public boolean stillValid(Player player) { - return true; - } - - @Override - public List getContents() { - NonNullList contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); - for (int i = 0; i < getContainerSize(); ++i) { - contents.set(i, getItem(i)); - } - return contents; - } - - @Override - public void onOpen(CraftHumanEntity viewer) { - transaction.add(viewer); - } - - @Override - public void onClose(CraftHumanEntity viewer) { - transaction.remove(viewer); - } - - @Override - public List getViewers() { - return transaction; - } - - @Override - public org.bukkit.entity.Player getOwner() { - return owner.getBukkitEntity(); - } - - @Override - public Location getLocation() { - return owner.getBukkitEntity().getLocation(); - } - - @Override - public void clearContent() { - owner.getInventory().clearContent(); - owner.inventoryMenu.getCraftSlots().clearContent(); - owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots()); - owner.containerMenu.setCarried(ItemStack.EMPTY); - } - - public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { - if (player instanceof ServerPlayer serverPlayer) { - return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); - } - return null; - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java deleted file mode 100644 index ec9422f5..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/Placeholders.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container; - -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Unit; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.component.CustomModelData; -import net.minecraft.world.item.component.DyedItemColor; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.block.entity.BannerPattern; -import net.minecraft.world.level.block.entity.BannerPatternLayers; -import net.minecraft.world.level.block.entity.BannerPatterns; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.craftbukkit.v1_21_R2.CraftRegistry; -import org.jetbrains.annotations.NotNull; - -import java.util.EnumMap; -import java.util.List; -import java.util.Optional; - -public final class Placeholders { - - private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); - public static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); - public static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); - public static @NotNull ItemStack cursor = defaultCursor(); - public static @NotNull ItemStack drop = defaultDrop(); - public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); - public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); - public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); - public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); - public static @NotNull ItemStack emptyOffHand = getEmptyShield(); - public static @NotNull ItemStack notSlot = defaultNotSlot(); - public static @NotNull ItemStack blockedOffline = defaultBlockedOffline(); - - static { - for (GameType type : GameType.values()) { - // Barrier: "Not available - Creative" etc. - ItemStack typeItem = new ItemStack(Items.BARRIER); - typeItem.set( - DataComponents.ITEM_NAME, - Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName())); - BLOCKED_GAME_TYPE.put(type, typeItem); - } - } - - public static void load(@NotNull ConfigurationSection section) throws Exception { - craftingOutput = parse(section, "crafting-output", craftingOutput); - cursor = parse(section, "cursor", cursor); - drop = parse(section, "drop", drop); - emptyHelmet = parse(section, "empty-helmet", emptyHelmet); - emptyChestplate = parse(section, "empty-chestplate", emptyChestplate); - emptyLeggings = parse(section, "empty-leggings", emptyLeggings); - emptyBoots = parse(section, "empty-boots", emptyBoots); - emptyOffHand = parse(section, "empty-off-hand", emptyOffHand); - notSlot = parse(section, "not-a-slot", notSlot); - blockedOffline = parse(section, "blocked.offline", blockedOffline); - BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE))); - BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); - } - - private static @NotNull ItemStack parse( - @NotNull ConfigurationSection section, - @NotNull String path, - @NotNull ItemStack defaultStack) throws Exception { - String itemText = section.getString(path); - - if (itemText == null) { - return defaultStack; - } - - CompoundTag compoundTag = TagParser.parseTag(itemText); - Optional parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag); - return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); - } - - public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { - if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) { - return blockedOffline; - } - - return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY); - } - - private static ItemStack defaultCraftingOutput() { - // Crafting table: "Crafting" - ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE); - itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting")); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); - return itemStack; - } - - private static ItemStack defaultCursor() { - // Cursor-like banner with no tooltip - ItemStack itemStack = new ItemStack(Items.WHITE_BANNER); - RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); - Registry bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN); - BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value(); - BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value(); - BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value(); - itemStack.set(DataComponents.BANNER_PATTERNS, - new BannerPatternLayers(List.of( - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - return itemStack; - } - - private static ItemStack defaultDrop() { - // Dropper: "Drop Selected Item" - ItemStack itemStack = new ItemStack(Items.DROPPER); - // Note: translatable component, not keybind component! We want the text identifying the keybind, not the key. - itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop")); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); - return itemStack; - } - - private static ItemStack getEmptyArmor(ItemLike item) { - // Inventory-background-grey-ish leather armor with no tooltip - ItemStack itemStack = new ItemStack(item); - DyedItemColor color = new DyedItemColor(0xC8C8C8, false); - itemStack.set(DataComponents.DYED_COLOR, color); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); - return itemStack; - } - - private static ItemStack getEmptyShield() { - ItemStack itemStack = new ItemStack(Items.SHIELD); - itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); - RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); - Registry bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN); - BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value(); - BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value(); - BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value(); - BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value(); - BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value(); - itemStack.set(DataComponents.BANNER_PATTERNS, - new BannerPatternLayers(List.of( - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); - return itemStack; - } - - private static ItemStack defaultNotSlot() { - // White pane with no tooltip - ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); - return itemStack; - } - - private static ItemStack defaultBlockedOffline() { - // Barrier: "Not available - Offline" - ItemStack itemStack = new ItemStack(Items.BARRIER); - itemStack.set(DataComponents.ITEM_NAME, - Component.translatable("options.narrator.notavailable") - .append(Component.literal(" - ")) - .append(Component.translatable("gui.socialInteractions.status_offline"))); - return itemStack; - } - - private Placeholders() { - throw new IllegalStateException("Cannot create instance of utility class."); - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java deleted file mode 100644 index 73babce1..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenChestMenu.java +++ /dev/null @@ -1,479 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container.menu; - -import com.google.common.base.Suppliers; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyInventory; -import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotPlaceholder; -import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.ClickType; -import net.minecraft.world.inventory.ContainerData; -import net.minecraft.world.inventory.ContainerListener; -import net.minecraft.world.inventory.ContainerSynchronizer; -import net.minecraft.world.inventory.DataSlot; -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.inventory.CraftInventoryView; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -/** - * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. - */ -public abstract class OpenChestMenu> - extends AbstractContainerMenu { - - protected static final int BOTTOM_INVENTORY_SIZE = 36; - - protected final T container; - protected final ServerPlayer viewer; - protected final boolean viewOnly; - protected final boolean ownContainer; - protected final int topSize; - private CraftInventoryView, Inventory> bukkitEntity; - // Syncher fields - private @Nullable ContainerSynchronizer synchronizer; - private final List dataSlots = new ArrayList<>(); - private final IntList remoteDataSlots = new IntArrayList(); - private final List containerListeners = new ArrayList<>(); - private ItemStack remoteCarried = ItemStack.EMPTY; - private boolean suppressRemoteUpdates; - - protected OpenChestMenu( - @NotNull MenuType type, - int containerCounter, - @NotNull T container, - @NotNull ServerPlayer viewer, - boolean viewOnly) { - super(type, containerCounter); - this.container = container; - this.viewer = viewer; - this.viewOnly = viewOnly; - ownContainer = container.getOwnerHandle().equals(viewer); - topSize = getTopSize(viewer); - - preSlotSetup(); - - int upperRows = topSize / 9; - // View's upper inventory - our container - for (int row = 0; row < upperRows; ++row) { - for (int col = 0; col < 9; ++col) { - // x and y for client purposes, but hey, we're thorough here. - // Adapted from net.minecraft.world.inventory.ChestMenu - int x = 8 + col * 18; - int y = 18 + row * 18; - int index = row * 9 + col; - - // Guard against weird inventory sizes. - if (index >= container.getContainerSize()) { - addSlot(new SlotViewOnly(container, index, x, y)); - continue; - } - - Slot slot = getUpperSlot(index, x, y); - - addSlot(slot); - } - } - - // View's lower inventory - viewer inventory - int playerInvPad = (upperRows - 4) * 18; - for (int row = 0; row < 3; ++row) { - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + row * 18 + 103; - addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); - } - } - // Hotbar - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + 161; - addSlot(new Slot(viewer.getInventory(), col, x, y)); - } - } - - public static @NotNull MenuType getChestMenuType(int inventorySize) { - inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9; - return switch (inventorySize) { - case 9 -> MenuType.GENERIC_9x1; - case 18 -> MenuType.GENERIC_9x2; - case 27 -> MenuType.GENERIC_9x3; - case 36 -> MenuType.GENERIC_9x4; - case 45 -> MenuType.GENERIC_9x5; - case 54 -> MenuType.GENERIC_9x6; - default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize); - }; - } - - protected void preSlotSetup() {} - - protected @NotNull Slot getUpperSlot(int index, int x, int y) { - Slot slot = new Slot(container, index, x, y); - if (viewOnly) { - return SlotViewOnly.wrap(slot); - } - return slot; - } - - - @Override - public final @NotNull CraftInventoryView, Inventory> getBukkitView() { - if (bukkitEntity == null) { - bukkitEntity = createBukkitEntity(); - } - - return bukkitEntity; - } - - protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { - Inventory top; - if (viewOnly) { - top = new OpenDummyInventory(container, container.getBukkitType()); - } else { - top = container.getBukkitInventory(); - } - return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) { - @Override - public @Nullable Inventory getInventory(int rawSlot) { - if (viewOnly) { - return null; - } - return super.getInventory(rawSlot); - } - - @Override - public int convertSlot(int rawSlot) { - if (viewOnly) { - return InventoryView.OUTSIDE; - } - return super.convertSlot(rawSlot); - } - - @Override - public @NotNull InventoryType.SlotType getSlotType(int slot) { - if (viewOnly) { - return InventoryType.SlotType.OUTSIDE; - } - return super.getSlotType(slot); - } - }; - } - - private int getTopSize(ServerPlayer viewer) { - MenuType menuType = getType(); - if (menuType == null) { - throw new IllegalStateException("MenuType cannot be null!"); - } else if (menuType == MenuType.GENERIC_9x1) { - return 9; - } else if (menuType == MenuType.GENERIC_9x2) { - return 18; - } else if (menuType == MenuType.GENERIC_9x3) { - return 27; - } else if (menuType == MenuType.GENERIC_9x4) { - return 36; - } else if (menuType == MenuType.GENERIC_9x5) { - return 45; - } else if (menuType == MenuType.GENERIC_9x6) { - return 54; - } - // This is a bit gross, but allows us a safe fallthrough. - return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE; - } - - /** - * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake - * slots and respects {@link Slot#hasItem()}. - * - * @param itemStack the stack to quick-move - * @param rangeLow the start of the range of slots that can be moved to, inclusive - * @param rangeHigh the end of the range of slots that can be moved to, exclusive - * @param topDown whether to start at the top of the range or bottom - * @return whether the stack was modified as a result of being quick-moved - */ - @Override - protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { - boolean modified = false; - boolean stackable = itemStack.isStackable(); - Slot firstEmpty = null; - - for (int index = topDown ? rangeHigh - 1 : rangeLow; - !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); - index += topDown ? -1 : 1 - ) { - Slot slot = slots.get(index); - // If the slot cannot be added to, check the next slot. - if (slot.isFake() || !slot.mayPlace(itemStack)) { - continue; - } - - if (slot.hasItem()) { - // If the item isn't stackable, check the next slot. - if (!stackable) { - continue; - } - // Otherwise, add as many as we can from our stack to the slot. - modified |= addToExistingStack(itemStack, slot); - } else { - // If this is the first empty slot, keep track of it for later use. - if (firstEmpty == null) { - firstEmpty = slot; - } - // If the item isn't stackable, we've located the slot we're adding it to, so we're done. - if (!stackable) { - break; - } - } - } - - // If the item hasn't been fully added yet, add as many as we can to the first open slot. - if (!itemStack.isEmpty() && firstEmpty != null) { - firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); - firstEmpty.setChanged(); - modified = true; - } - - return modified; - } - - private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { - ItemStack existing = slot.getItem(); - - // If the items aren't the same, we can't add our item. - if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { - return false; - } - - int max = slot.getMaxStackSize(existing); - int existingCount = existing.getCount(); - - // If the stack is already full, we can't add more. - if (existingCount >= max) { - return false; - } - - int total = existingCount + itemStack.getCount(); - - // If the existing item can accept the entirety of our item, we're done! - if (total <= max) { - itemStack.setCount(0); - existing.setCount(total); - slot.setChanged(); - return true; - } - - // Otherwise, add as many as we can. - itemStack.shrink(max - existingCount); - existing.setCount(max); - slot.setChanged(); - return true; - } - - @Override - public void clicked(int i, int j, ClickType clickType, Player player) { - if (viewOnly) { - if (clickType == ClickType.QUICK_CRAFT) { - sendAllDataToRemote(); - } - return; - } - super.clicked(i, j, clickType, player); - } - - @Override - public boolean stillValid(Player player) { - return true; - } - - // Overrides from here on are purely to modify the sync process to send placeholder items. - @Override - protected Slot addSlot(Slot slot) { - slot.index = this.slots.size(); - this.slots.add(slot); - this.lastSlots.add(ItemStack.EMPTY); - this.remoteSlots.add(ItemStack.EMPTY); - return slot; - } - - @Override - protected DataSlot addDataSlot(DataSlot dataSlot) { - this.dataSlots.add(dataSlot); - this.remoteDataSlots.add(0); - return dataSlot; - } - - @Override - protected void addDataSlots(ContainerData containerData) { - for (int i = 0; i < containerData.getCount(); i++) { - this.addDataSlot(DataSlot.forContainer(containerData, i)); - } - } - - @Override - public void addSlotListener(ContainerListener containerListener) { - if (!this.containerListeners.contains(containerListener)) { - this.containerListeners.add(containerListener); - this.broadcastChanges(); - } - } - - @Override - public void setSynchronizer(ContainerSynchronizer containerSynchronizer) { - this.synchronizer = containerSynchronizer; - this.sendAllDataToRemote(); - } - - @Override - public void sendAllDataToRemote() { - for (int index = 0; index < slots.size(); ++index) { - Slot slot = slots.get(index); - this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); - } - - remoteCarried = getCarried().copy(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); - } - - if (this.synchronizer != null) { - this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); - } - } - - @Override - public void broadcastCarriedItem() { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); - } - } - - @Override - public void removeSlotListener(ContainerListener containerListener) { - this.containerListeners.remove(containerListener); - } - - @Override - public void broadcastChanges() { - for (int index = 0; index < this.slots.size(); ++index) { - Slot slot = this.slots.get(index); - ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); - Supplier supplier = Suppliers.memoize(itemstack::copy); - this.triggerSlotListeners(index, itemstack, supplier); - this.synchronizeSlotToRemote(index, itemstack, supplier); - } - - this.synchronizeCarriedToRemote(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot dataSlot = this.dataSlots.get(index); - int j = dataSlot.get(); - if (dataSlot.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, j); - } - - this.synchronizeDataSlotToRemote(index, j); - } - } - - @Override - public void broadcastFullState() { - for (int index = 0; index < this.slots.size(); ++index) { - ItemStack itemstack = this.slots.get(index).getItem(); - this.triggerSlotListeners(index, itemstack, itemstack::copy); - } - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot containerproperty = this.dataSlots.get(index); - if (containerproperty.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, containerproperty.get()); - } - } - - this.sendAllDataToRemote(); - } - - private void updateDataSlotListeners(int i, int j) { - for (ContainerListener containerListener : this.containerListeners) { - containerListener.dataChanged(this, i, j); - } - } - - private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { - ItemStack itemStack1 = this.lastSlots.get(index); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemStack2 = supplier.get(); - this.lastSlots.set(index, itemStack2); - - for (ContainerListener containerListener : this.containerListeners) { - containerListener.slotChanged(this, index, itemStack2); - } - } - } - - private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { - if (!this.suppressRemoteUpdates) { - ItemStack itemStack1 = this.remoteSlots.get(i); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemstack2 = supplier.get(); - this.remoteSlots.set(i, itemstack2); - if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(this, i, itemstack2); - } - } - } - } - - private void synchronizeDataSlotToRemote(int index, int value) { - if (!this.suppressRemoteUpdates) { - int existing = this.remoteDataSlots.getInt(index); - if (existing != value) { - this.remoteDataSlots.set(index, value); - if (this.synchronizer != null) { - this.synchronizer.sendDataChange(this, index, value); - } - } - } - } - - private void synchronizeCarriedToRemote() { - if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); - } - } - } - - @Override - public void setRemoteCarried(ItemStack itemstack) { - this.remoteCarried = itemstack.copy(); - } - - @Override - public void suppressRemoteUpdates() { - this.suppressRemoteUpdates = true; - } - - @Override - public void resumeRemoteUpdates() { - this.suppressRemoteUpdates = false; - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java deleted file mode 100644 index 832efa73..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/menu/OpenEnderChestMenu.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container.menu; - -import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.NotNull; - -public class OpenEnderChestMenu extends OpenChestMenu { - - public OpenEnderChestMenu( - @NotNull OpenEnderChest enderChest, - @NotNull ServerPlayer viewer, - int containerId, - boolean viewOnly) { - super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly); - } - - @Override - public ItemStack quickMoveStack(Player player, int index) { - if (viewOnly) { - return ItemStack.EMPTY; - } - - // See ChestMenu - Slot slot = this.slots.get(index); - - if (slot.isFake() || !slot.hasItem()) { - return ItemStack.EMPTY; - } - - ItemStack itemStack = slot.getItem(); - ItemStack original = itemStack.copy(); - - if (index < topSize) { - if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { - return ItemStack.EMPTY; - } - } else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) { - return ItemStack.EMPTY; - } - - if (itemStack.isEmpty()) { - slot.setByPlayer(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - - return original; - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java deleted file mode 100644 index def3448b..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentEquipment.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; - -import com.lishid.openinv.internal.v1_21_R2.container.Placeholders; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -/** - * A slot for equipment that displays placeholders if empty. - */ -public class ContentEquipment extends ContentList { - - private final ItemStack placeholder; - private final EquipmentSlot equipmentSlot; - - public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { - super(holder, index, InventoryType.SlotType.ARMOR); - placeholder = switch (equipmentSlot) { - case HEAD -> Placeholders.emptyHelmet; - case CHEST -> Placeholders.emptyChestplate; - case LEGS -> Placeholders.emptyLeggings; - case FEET -> Placeholders.emptyBoots; - default -> Placeholders.emptyOffHand; - }; - this.equipmentSlot = equipmentSlot; - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.items = holder.getInventory().armor; - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotEquipment(container, slot, x, y); - } - - public class SlotEquipment extends SlotPlaceholder { - - private ServerPlayer viewer; - - SlotEquipment(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - @Override - public ItemStack getOrDefault() { - ItemStack itemStack = getItem(); - if (!itemStack.isEmpty()) { - return itemStack; - } - return placeholder; - } - - public EquipmentSlot getEquipmentSlot() { - return equipmentSlot; - } - - public void onlyEquipmentFor(ServerPlayer viewer) { - this.viewer = viewer; - } - - @Override - public boolean mayPlace(ItemStack var0) { - if (viewer == null) { - return true; - } - - return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot; - } - - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java deleted file mode 100644 index 27490a50..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentList.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; - -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; - -import java.util.List; - -/** - * A normal slot backed by an item list. - */ -public abstract class ContentList implements Content { - - private final int index; - private final InventoryType.SlotType slotType; - protected List items; - - public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) { - this.index = index; - this.slotType = slotType; - setHolder(holder); - } - - @Override - public ItemStack get() { - return items.get(index); - } - - @Override - public ItemStack remove() { - ItemStack removed = items.remove(index); - return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed; - } - - @Override - public ItemStack removePartial(int amount) { - return ContainerHelper.removeItem(items, index, amount); - } - - @Override - public void set(ItemStack itemStack) { - items.set(index, itemStack); - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new Slot(container, slot, x, y); - } - - @Override - public InventoryType.SlotType getSlotType() { - return slotType; - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java deleted file mode 100644 index dc97b4c6..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentOffHand.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; - -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.inventory.Slot; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -/** - * A slot for equipment that updates held items if necessary. - */ -public class ContentOffHand extends ContentEquipment { - - private ServerPlayer holder; - - public ContentOffHand(ServerPlayer holder, int localIndex) { - super(holder, localIndex, EquipmentSlot.OFFHAND); - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.items = holder.getInventory().offhand; - this.holder = holder; - } - - @Override - public InventoryType.SlotType getSlotType() { - return InventoryType.SlotType.QUICKBAR; - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotEquipment(container, slot, x, y) { - @Override - public void setChanged() { - if (holder.connection != null - && !holder.connection.isDisconnected() - && holder.containerMenu != holder.inventoryMenu) { - holder.connection.send( - new ClientboundContainerSetSlotPacket( - holder.inventoryMenu.containerId, - holder.inventoryMenu.incrementStateId(), - InventoryMenu.SHIELD_SLOT, - holder.getOffhandItem())); - } - } - }; - } - -} diff --git a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java b/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java deleted file mode 100644 index 37213db0..00000000 --- a/internal/v1_21_R2/src/main/java/com/lishid/openinv/internal/v1_21_R2/container/slot/ContentViewOnly.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R2.container.slot; - -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -/** - * A view-only slot that can't be interacted with. - */ -public class ContentViewOnly implements Content { - - @NotNull ServerPlayer holder; - - public ContentViewOnly(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public ItemStack get() { - return ItemStack.EMPTY; - } - - @Override - public ItemStack remove() { - return ItemStack.EMPTY; - } - - @Override - public ItemStack removePartial(int amount) { - return ItemStack.EMPTY; - } - - @Override - public void set(ItemStack itemStack) { - this.holder.drop(itemStack, false); - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotViewOnly(container, slot, x, y); - } - - @Override - public InventoryType.SlotType getSlotType() { - return InventoryType.SlotType.OUTSIDE; - } - -} diff --git a/internal/v1_21_R3/pom.xml b/internal/v1_21_R3/pom.xml deleted file mode 100644 index 733f3b4b..00000000 --- a/internal/v1_21_R3/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - 4.0.0 - - - openinvparent - com.lishid - ../../pom.xml - 5.1.7-SNAPSHOT - - - openinvadapter1_21_R3 - OpenInvAdapter1_21_R3 - - - 21 - 21 - 1.21.4-R0.1-SNAPSHOT - - - - - org.spigotmc - spigot-api - ${spigot.version} - - - spigot - org.spigotmc - provided - ${spigot.version} - remapped-mojang - - - openinvapi - com.lishid - - - openinvcommon - com.lishid - - - annotations - org.jetbrains - - - - - - - maven-compiler-plugin - - - net.md-5 - specialsource-maven-plugin - - - - - diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java deleted file mode 100644 index c2724bad..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/AnySilentContainer.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2011-2023 lishid. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lishid.openinv.internal.v1_21_R3.container; - -import com.lishid.openinv.internal.AnySilentContainerBase; -import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenChestMenu; -import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager; -import com.lishid.openinv.util.ReflectionHelper; -import com.lishid.openinv.util.lang.LanguageManager; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.PlayerEnderChestContainer; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.block.BarrelBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.ShulkerBoxBlock; -import net.minecraft.world.level.block.TrappedChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.EnderChestBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.logging.Logger; - -public class AnySilentContainer extends AnySilentContainerBase { - - private final @NotNull Logger logger; - private final @NotNull LanguageManager lang; - private @Nullable Field serverPlayerGameModeGameType; - - public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { - this.logger = logger; - this.lang = lang; - try { - try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); - this.serverPlayerGameModeGameType.setAccessible(true); - } catch (NoSuchFieldException e) { - logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!"); - logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); - logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); - // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. - this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); - } - } catch (SecurityException e) { - logger.warning("Unable to directly write player game mode! SilentContainer will fail."); - logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); - } - } - - @Override - public boolean activateContainer( - @NotNull final Player bukkitPlayer, - final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - - final net.minecraft.world.level.Level level = player.level(); - final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final BlockEntity blockEntity = level.getBlockEntity(blockPos); - - if (blockEntity == null) { - return false; - } - - if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock - PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); - enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); - int rows = enderChest.getContainerSize() / 9; - return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable("container.enderchest"))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(blockEntity instanceof MenuProvider menuProvider)) { - return false; - } - - BlockState blockState = level.getBlockState(blockPos); - Block block = blockState.getBlock(); - - if (block instanceof ChestBlock chestBlock) { - - // boolean flag: do not check if chest is blocked - menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); - - if (menuProvider == null) { - lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - if (block instanceof TrappedChestBlock) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof ShulkerBoxBlock) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BarrelBlock) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { - player.openMenu(menuProvider); - return true; - } - - // SilentChest requires access to setting players' game mode directly. - if (this.serverPlayerGameModeGameType == null) { - return false; - } - - if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { - if (lootable.lootTable != null) { - lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - player.openMenu(menuProvider); - this.forceGameType(player, gameType); - return true; - } - - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { - return; - } - - ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - - // Force game mode change without informing plugins or players. - // Regular game mode set calls GameModeChangeEvent and is cancellable. - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - - // ServerPlayer#closeContainer cannot be called without entering an - // infinite loop because this method is called during inventory close. - // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent - player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); - // From ServerPlayer#closeContainer - player.doCloseContainer(); - // Regular inventory close will handle the rest - packet sending, etc. - - // Revert forced game mode. - this.forceGameType(player, gameType); - } - - private void forceGameType(final ServerPlayer player, final GameType gameMode) { - if (this.serverPlayerGameModeGameType == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - this.serverPlayerGameModeGameType.setAccessible(true); - this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); - } - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java deleted file mode 100644 index d859d8db..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyInventory.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.bukkit; - -import com.lishid.openinv.internal.ViewOnly; -import net.minecraft.world.Container; -import org.bukkit.Material; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.HashMap; -import java.util.ListIterator; - -/** - * A locked down "empty" inventory that rejects plugin interaction. - */ -public class OpenDummyInventory extends CraftInventory implements ViewOnly { - - private final InventoryType type; - - public OpenDummyInventory(Container inventory, InventoryType type) { - super(inventory); - this.type = type; - } - - @Override - public @NotNull InventoryType getType() { - return type; - } - - @Override - public @Nullable ItemStack getItem(int index) { - return null; - } - - @Override - public void setItem(int index, @Nullable ItemStack item) { - - } - - @SuppressWarnings("NonApiType") - @Override - public @NotNull HashMap addItem(@NotNull ItemStack... items) throws IllegalArgumentException { - return arrayToHashMap(items); - } - - @SuppressWarnings("NonApiType") - @Override - public @NotNull HashMap removeItem(@NotNull ItemStack... items) throws IllegalArgumentException { - return arrayToHashMap(items); - } - - @SuppressWarnings("NonApiType") - private static @NotNull HashMap arrayToHashMap(@NotNull ItemStack[] items) { - HashMap ignored = new HashMap<>(); - for (int index = 0; index < items.length; ++index) { - ignored.put(index, items[index]); - } - return ignored; - } - - @Override - public ItemStack[] getContents() { - return new ItemStack[getSize()]; - } - - @Override - public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException { - - } - - @Override - public @NotNull ItemStack[] getStorageContents() { - return new ItemStack[getSize()]; - } - - @Override - public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException { - - } - - @Override - public boolean contains(@NotNull Material material) throws IllegalArgumentException { - return false; - } - - @Override - public boolean contains(@Nullable ItemStack item) { - return false; - } - - @Override - public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException { - return false; - } - - @Override - public boolean contains(@Nullable ItemStack item, int amount) { - return false; - } - - @Override - public boolean containsAtLeast(@Nullable ItemStack item, int amount) { - return false; - } - - @SuppressWarnings("NonApiType") - @Override - public @NotNull HashMap all( - @NotNull Material material) throws IllegalArgumentException { - return new HashMap<>(); - } - - @SuppressWarnings("NonApiType") - @Override - public @NotNull HashMap all(@Nullable ItemStack item) { - return new HashMap<>(); - } - - @Override - public int first(@NotNull Material material) throws IllegalArgumentException { - return -1; - } - - @Override - public int first(@NotNull ItemStack item) { - return -1; - } - - @Override - public int firstEmpty() { - return -1; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public void remove(@NotNull Material material) throws IllegalArgumentException { - - } - - @Override - public void remove(@NotNull ItemStack item) { - - } - - @Override - public void clear(int index) { - - } - - @Override - public void clear() { - - } - - @Override - public @NotNull ListIterator iterator() { - return Collections.emptyListIterator(); - } - - @Override - public @NotNull ListIterator iterator(int index) { - return Collections.emptyListIterator(); - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java deleted file mode 100644 index ced1128c..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenDummyPlayerInventory.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.bukkit; - -import net.minecraft.world.Container; -import org.bukkit.Material; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory { - - public OpenDummyPlayerInventory(Container inventory) { - super(inventory, InventoryType.PLAYER); - } - - @Override - public HumanEntity getHolder() { - return (HumanEntity) super.getHolder(); - } - - @Override - public @NotNull ItemStack[] getArmorContents() { - return new ItemStack[4]; - } - - @Override - public @NotNull ItemStack[] getExtraContents() { - return new ItemStack[4]; - } - - @Override - public @Nullable ItemStack getHelmet() { - return null; - } - - @Override - public @Nullable ItemStack getChestplate() { - return null; - } - - @Override - public @Nullable ItemStack getLeggings() { - return null; - } - - @Override - public @Nullable ItemStack getBoots() { - return null; - } - - @Override - public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) { - - } - - @Override - public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) { - return null; - } - - @Override - public void setArmorContents(@Nullable ItemStack[] items) { - - } - - @Override - public void setExtraContents(@Nullable ItemStack[] items) { - - } - - @Override - public void setHelmet(@Nullable ItemStack helmet) { - - } - - @Override - public void setChestplate(@Nullable ItemStack chestplate) { - - } - - @Override - public void setLeggings(@Nullable ItemStack leggings) { - - } - - @Override - public void setBoots(@Nullable ItemStack boots) { - - } - - @Override - public @NotNull ItemStack getItemInMainHand() { - return new ItemStack(Material.AIR); - } - - @Override - public void setItemInMainHand(@Nullable ItemStack item) { - - } - - @Override - public @NotNull ItemStack getItemInOffHand() { - return new ItemStack(Material.AIR); - } - - @Override - public void setItemInOffHand(@Nullable ItemStack item) { - - } - - @Override - public @NotNull ItemStack getItemInHand() { - return new ItemStack(Material.AIR); - } - - @Override - public void setItemInHand(@Nullable ItemStack stack) { - - } - - @Override - public int getHeldItemSlot() { - return 0; - } - - @Override - public void setHeldItemSlot(int slot) { - - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java deleted file mode 100644 index 7d612d77..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventory.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.bukkit; - -import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; -import net.minecraft.core.NonNullList; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { - - public OpenPlayerInventory(@NotNull OpenInventory inventory) { - super(inventory); - } - - @Override - public @NotNull OpenInventory getInventory() { - return (OpenInventory) super.getInventory(); - } - - @Override - public ItemStack[] getContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents()); - } - - @Override - public void setContents(ItemStack[] items) { - Inventory internal = getInventory().getOwnerHandle().getInventory(); - int size = internal.getContainerSize(); - Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); - - for (int index = 0; index < size; ++index) { - if (index < items.length) { - internal.setItem(index, CraftItemStack.asNMSCopy(items[index])); - } else { - internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY); - } - } - } - - @Override - public ItemStack[] getStorageContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().items); - } - - @Override - public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { - NonNullList list = getInventory().getOwnerHandle().getInventory().items; - int size = list.size(); - Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); - for (int index = 0; index < items.length; ++index) { - list.set(index, CraftItemStack.asNMSCopy(items[index])); - } - } - - @Override - public @NotNull InventoryType getType() { - return InventoryType.PLAYER; - } - - @Override - public @NotNull Player getHolder() { - return getInventory().getOwner(); - } - - @Override - public @NotNull ItemStack[] getArmorContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor); - } - - @Override - public void setArmorContents(@Nullable ItemStack[] items) { - NonNullList list = getInventory().getOwnerHandle().getInventory().armor; - int size = list.size(); - Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); - for (int index = 0; index < items.length; ++index) { - list.set(index, CraftItemStack.asNMSCopy(items[index])); - } - } - - @Override - public @NotNull ItemStack[] getExtraContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand); - } - - @Override - public void setExtraContents(@Nullable ItemStack[] items) { - NonNullList list = getInventory().getOwnerHandle().getInventory().offhand; - int size = list.size(); - Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); - for (int index = 0; index < items.length; ++index) { - list.set(index, CraftItemStack.asNMSCopy(items[index])); - } - } - - @Override - public @Nullable ItemStack getHelmet() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.HEAD.getIndex())); - } - - @Override - public void setHelmet(@Nullable ItemStack helmet) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet)); - } - - @Override - public @Nullable ItemStack getChestplate() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.HEAD.getIndex())); - } - - @Override - public void setChestplate(@Nullable ItemStack chestplate) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate)); - } - - @Override - public @Nullable ItemStack getLeggings() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.LEGS.getIndex())); - } - - @Override - public void setLeggings(@Nullable ItemStack leggings) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings)); - } - - @Override - public @Nullable ItemStack getBoots() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.FEET.getIndex())); - } - - @Override - public void setBoots(@Nullable ItemStack boots) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots)); - } - - @Override - public @NotNull ItemStack getItemInMainHand() { - Inventory internal = getInventory().getOwnerHandle().getInventory(); - return CraftItemStack.asCraftMirror(internal.getItem(internal.selected)); - } - - @Override - public void setItemInMainHand(@Nullable ItemStack item) { - Inventory internal = getInventory().getOwnerHandle().getInventory(); - internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item)); - } - - @Override - public @NotNull ItemStack getItemInOffHand() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst()); - } - - @Override - public void setItemInOffHand(@Nullable ItemStack item) { - getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); - } - - @Override - public @NotNull ItemStack getItemInHand() { - return getItemInMainHand(); - } - - @Override - public void setItemInHand(@Nullable ItemStack stack) { - setItemInMainHand(stack); - } - - @Override - public int getHeldItemSlot() { - Inventory internal = getInventory().getOwnerHandle().getInventory(); - return internal.items.size() - 9 + internal.selected; - } - - @Override - public void setHeldItemSlot(int slot) { - slot %= 9; - getInventory().getOwnerHandle().getInventory().selected = slot; - } - - @Override - public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { - return switch (slot) { - case HAND -> getItemInMainHand(); - case OFF_HAND -> getItemInOffHand(); - case FEET -> getBoots(); - case LEGS -> getLeggings(); - case CHEST -> getChestplate(); - case HEAD -> getHelmet(); - default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); - }; - } - - @Override - public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) { - switch (slot) { - case HAND -> setItemInMainHand(item); - case OFF_HAND -> setItemInOffHand(item); - case FEET -> setBoots(item); - case LEGS -> setLeggings(item); - case CHEST -> setChestplate(item); - case HEAD -> setHelmet(item); - default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); - } - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java deleted file mode 100644 index a26e75c8..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/bukkit/OpenPlayerInventorySelf.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.bukkit; - -import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -public class OpenPlayerInventorySelf extends OpenPlayerInventory { - - private final int offset; - - public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) { - super(inventory); - this.offset = offset; - } - - @Override - public ItemStack getItem(int index) { - return super.getItem(offset + index); - } - - @Override - public void setItem(int index, ItemStack item) { - super.setItem(offset + index, item); - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java deleted file mode 100644 index 2d26e091..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/menu/OpenInventoryMenu.java +++ /dev/null @@ -1,262 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.menu; - -import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory; -import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyPlayerInventory; -import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventorySelf; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop; -import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment; -import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly; -import com.lishid.openinv.util.Permissions; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView; -import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class OpenInventoryMenu extends OpenChestMenu { - - private int offset; - - public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) { - super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly); - } - - private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { - int size = inventory.getContainerSize(); - // Disallow duplicate access to own main inventory contents. - if (inventory.getOwnerHandle().equals(viewer)) { - size -= viewer.getInventory().items.size(); - size = ((int) Math.ceil(size / 9.0)) * 9; - } - - return getChestMenuType(size); - } - - @Override - protected void preSlotSetup() { - offset = ownContainer ? viewer.getInventory().items.size() : 0; - } - - @Override - protected @NotNull Slot getUpperSlot(int index, int x, int y) { - index += offset; - Slot slot = container.getMenuSlot(index, x, y); - - // If the slot cannot be interacted with there's nothing to configure. - if (slot.getClass().equals(SlotViewOnly.class)) { - return slot; - } - - // Remove drop slot if viewer is not allowed to use it. - if (slot instanceof ContentDrop.SlotDrop - && (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) { - return new SlotViewOnly(container, index, x, y); - } - - if (slot instanceof ContentEquipment.SlotEquipment equipment) { - if (viewOnly) { - return SlotViewOnly.wrap(slot); - } - - Permissions perm = switch (equipment.getEquipmentSlot()) { - case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY; - case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY; - case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY; - case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY; - // Off-hand can hold anything, not just equipment. - default -> null; - }; - - // If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot. - if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) { - equipment.onlyEquipmentFor(container.getOwnerHandle()); - } - - // Equipment slots are a core part of the inventory, so they will always be shown. - return slot; - } - - // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). - if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) { - return new SlotViewOnly(container, index, x, y); - } - - if (viewOnly) { - return SlotViewOnly.wrap(slot); - } - - return slot; - } - - @Override - protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { - org.bukkit.inventory.Inventory bukkitInventory; - if (viewOnly) { - bukkitInventory = new OpenDummyPlayerInventory(container); - } else if (ownContainer) { - bukkitInventory = new OpenPlayerInventorySelf(container, offset); - } else { - bukkitInventory = container.getBukkitInventory(); - } - - return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) { - @Override - public org.bukkit.inventory.ItemStack getItem(int index) { - if (viewOnly || index < 0) { - return null; - } - - Slot slot = slots.get(index); - return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY); - } - - @Override - public boolean isInTop(int rawSlot) { - return rawSlot < topSize; - } - - @Override - public @Nullable Inventory getInventory(int rawSlot) { - if (viewOnly) { - return null; - } - if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) { - return null; - } - Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, - "Slot %s outside of inventory", rawSlot); - if (rawSlot > topSize) { - return getBottomInventory(); - } - Slot slot = slots.get(rawSlot); - if (slot.isFake()) { - return null; - } - return getTopInventory(); - } - - @Override - public int convertSlot(int rawSlot) { - if (viewOnly) { - return InventoryView.OUTSIDE; - } - if (rawSlot < 0) { - return rawSlot; - } - if (rawSlot < topSize) { - Slot slot = slots.get(rawSlot); - if (slot.isFake()) { - return InventoryView.OUTSIDE; - } - return rawSlot; - } - - int slot = rawSlot - topSize; - - if (slot >= 27) { - slot -= 27; - } else { - slot += 9; - } - - return slot; - } - - @Override - public @NotNull InventoryType.SlotType getSlotType(int slot) { - if (viewOnly || slot < 0) { - return InventoryType.SlotType.OUTSIDE; - } - if (slot >= topSize) { - slot -= topSize; - if (slot >= 27) { - return InventoryType.SlotType.QUICKBAR; - } - return InventoryType.SlotType.CONTAINER; - } - return OpenInventoryMenu.this.container.getSlotType(offset + slot); - } - - @Override - public int countSlots() { - return topSize + BOTTOM_INVENTORY_SIZE; - } - }; - } - - @Override - public ItemStack quickMoveStack(Player player, int index) { - if (viewOnly) { - return ItemStack.EMPTY; - } - - // See ChestMenu and InventoryMenu - Slot slot = this.slots.get(index); - - if (!slot.hasItem() || slot.isFake()) { - return ItemStack.EMPTY; - } - - ItemStack itemStack = slot.getItem(); - ItemStack originalStack = itemStack.copy(); - - if (index < topSize) { - // If we're moving top to bottom, do a normal transfer. - if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { - return ItemStack.EMPTY; - } - } else { - EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack); - boolean movedGear = switch (equipmentSlot) { - // If this is gear, try to move it to the correct slot first. - case OFFHAND, FEET, LEGS, CHEST, HEAD -> { - // Locate the correct slot in the contents following the main inventory. - for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { - Slot extraSlot = getSlot(extra); - if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot - && equipSlot.getEquipmentSlot() == equipmentSlot) { - // If we've found a matching slot, try to move to it. - // If this succeeds, even partially, we will not attempt to move to other slots. - // Otherwise, armor is already occupied, so we'll fall through to main inventory. - yield this.moveItemStackTo(itemStack, extra, extra + 1, false); - } - } - yield false; - } - // Non-gear gets no special treatment. - default -> false; - }; - - // If main inventory is not available, there's nowhere else to move. - if (offset != 0) { - if (!movedGear) { - return ItemStack.EMPTY; - } - } else { - // If we didn't move to a gear slot, try to move to a main inventory slot. - if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) { - return ItemStack.EMPTY; - } - } - } - - if (itemStack.isEmpty()) { - slot.setByPlayer(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - - return originalStack; - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java deleted file mode 100644 index fb290de4..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/Content.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.NotNull; - -/** - * An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering. - */ -public interface Content { - - /** - * Update internal holder. - * - * @param holder the new holder - */ - void setHolder(@NotNull ServerPlayer holder); - - /** - * Get the current item. - * - * @return the current item - */ - ItemStack get(); - - /** - * Remove the current item. - * - * @return the current item - */ - ItemStack remove(); - - /** - * Remove some of the current item. - * - * @return the current item - */ - ItemStack removePartial(int amount); - - /** - * Set the current item. If slot is currently not usable, will drop item instead. - * - * @param itemStack the item to set - */ - void set(ItemStack itemStack); - - /** - * Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will - * impose any specific restrictions to insertion or removal. - * - * @param container the backing container - * @param slot the slot of the backing container represented - * @param x clientside x dimension from top left of inventory, not used - * @param y clientside y dimension from top left of inventory, not used - * @return a menu slot - */ - Slot asSlot(Container container, int slot, int x, int y); - - /** - * Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather - * than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}. - * - * @return the closes Bukkit slot type - */ - org.bukkit.event.inventory.InventoryType.SlotType getSlotType(); - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java deleted file mode 100644 index 0479ea8c..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCrafting.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * A slot in a survival crafting inventory. Unavailable when not online in a survival mode. - */ -public class ContentCrafting implements Content { - - private final int index; - private ServerPlayer holder; - private List items; - - public ContentCrafting(@NotNull ServerPlayer holder, int index) { - setHolder(holder); - this.index = index; - } - - private boolean isAvailable() { - return isAvailable(holder); - } - - static boolean isAvailable(@NotNull ServerPlayer holder) { - // Player must be online and not in creative - since the creative client is (semi-)authoritative, - // it ignores changes without extra help, and will delete the item as a result. - // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. - return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.holder = holder; - // Note: CraftingContainer#getItems is immutable! Be careful with updates. - this.items = holder.inventoryMenu.getCraftSlots().getContents(); - } - - @Override - public ItemStack get() { - return isAvailable() ? items.get(index) : ItemStack.EMPTY; - } - - @Override - public ItemStack remove() { - if (!this.isAvailable()) { - return ItemStack.EMPTY; - } - ItemStack removed = items.remove(index); - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); - return removed; - } - - @Override - public ItemStack removePartial(int amount) { - if (!this.isAvailable()) { - return ItemStack.EMPTY; - } - ItemStack removed = ContainerHelper.removeItem(items, index, amount); - if (removed.isEmpty()) { - return ItemStack.EMPTY; - } - holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); - return removed; - } - - @Override - public void set(ItemStack itemStack) { - if (isAvailable()) { - items.set(index, itemStack); - holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots()); - } else { - holder.drop(itemStack, false); - } - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotCrafting(container, slot, x, y); - } - - @Override - public InventoryType.SlotType getSlotType() { - return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE; - } - - public class SlotCrafting extends SlotPlaceholder { - - private SlotCrafting(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - @Override - public ItemStack getOrDefault() { - return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder); - } - - @Override - public boolean mayPickup(Player player) { - return isAvailable(); - } - - @Override - public boolean mayPlace(ItemStack itemStack) { - return isAvailable(); - } - - @Override - public boolean hasItem() { - return isAvailable() && super.hasItem(); - } - - @Override - public boolean isFake() { - return !isAvailable(); - } - - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java deleted file mode 100644 index 1fceaca0..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCraftingResult.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -/** - * A slot allowing viewing of the crafting result. - * - *

Unmodifiable because I said so. Use your own crafting grid.

- */ -public class ContentCraftingResult extends ContentViewOnly { - - public ContentCraftingResult(@NotNull ServerPlayer holder) { - super(holder); - } - - @Override - public ItemStack get() { - InventoryMenu inventoryMenu = holder.inventoryMenu; - return inventoryMenu.getResultSlot().getItem(); - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotViewOnly(container, slot, x, y) { - @Override - public ItemStack getOrDefault() { - if (!ContentCrafting.isAvailable(holder)) { - return Placeholders.survivalOnly(holder); - } - InventoryMenu inventoryMenu = holder.inventoryMenu; - return inventoryMenu.getResultSlot().getItem(); - } - }; - } - - @Override - public InventoryType.SlotType getSlotType() { - return InventoryType.SlotType.RESULT; - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java deleted file mode 100644 index 46ea737e..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentCursor.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -/** - * A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode. - */ -public class ContentCursor implements Content { - - private @NotNull ServerPlayer holder; - - public ContentCursor(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public ItemStack get() { - return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY; - } - - @Override - public ItemStack remove() { - ItemStack carried = holder.containerMenu.getCarried(); - holder.containerMenu.setCarried(ItemStack.EMPTY); - return carried; - } - - @Override - public ItemStack removePartial(int amount) { - ItemStack carried = holder.containerMenu.getCarried(); - if (!carried.isEmpty() && carried.getCount() >= amount) { - ItemStack value = carried.split(amount); - if (carried.isEmpty()) { - holder.containerMenu.setCarried(ItemStack.EMPTY); - } - return value; - } - return ItemStack.EMPTY; - } - - @Override - public void set(ItemStack itemStack) { - if (isAvailable()) { - holder.containerMenu.setCarried(itemStack); - } else { - holder.drop(itemStack, false); - } - } - - private boolean isAvailable() { - // Player must be online and not in creative - since the creative client is (semi-)authoritative, - // it ignores changes without extra help, and will delete the item as a result. - // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. - return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival(); - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotCursor(container, slot, x, y); - } - - @Override - public InventoryType.SlotType getSlotType() { - // As close as possible to "not real" - return InventoryType.SlotType.OUTSIDE; - } - - public class SlotCursor extends SlotPlaceholder { - - private SlotCursor(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - @Override - public ItemStack getOrDefault() { - if (!isAvailable()) { - return Placeholders.survivalOnly(holder); - } - ItemStack carried = holder.containerMenu.getCarried(); - return carried.isEmpty() ? Placeholders.cursor : carried; - } - - @Override - public boolean mayPickup(Player player) { - return isAvailable(); - } - - @Override - public boolean mayPlace(ItemStack itemStack) { - return isAvailable(); - } - - @Override - public boolean hasItem() { - return isAvailable() && super.hasItem(); - } - - @Override - public boolean isFake() { - return true; - } - - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java deleted file mode 100644 index 5e1c5168..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/ContentDrop.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.event.inventory.InventoryType; -import org.jetbrains.annotations.NotNull; - -/** - * A fake slot used to drop items. Unavailable offline. - */ -public class ContentDrop implements Content { - - private ServerPlayer holder; - - public ContentDrop(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public void setHolder(@NotNull ServerPlayer holder) { - this.holder = holder; - } - - @Override - public ItemStack get() { - return ItemStack.EMPTY; - } - - @Override - public ItemStack remove() { - return ItemStack.EMPTY; - } - - @Override - public ItemStack removePartial(int amount) { - return ItemStack.EMPTY; - } - - @Override - public void set(ItemStack itemStack) { - holder.drop(itemStack, true); - } - - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotDrop(container, slot, x, y); - } - - @Override - public InventoryType.SlotType getSlotType() { - // Behaves like dropping an item outside the screen, just by the target player. - return InventoryType.SlotType.OUTSIDE; - } - - public class SlotDrop extends SlotPlaceholder { - - private SlotDrop(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - @Override - public ItemStack getOrDefault() { - return holder.connection != null && !holder.connection.isDisconnected() - ? Placeholders.drop - : Placeholders.blockedOffline; - } - - @Override - public boolean mayPlace(ItemStack itemStack) { - return holder.connection != null && !holder.connection.isDisconnected(); - } - - @Override - public boolean hasItem() { - return false; - } - - @Override - public boolean isFake() { - return true; - } - - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java deleted file mode 100644 index 7ec22a84..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotPlaceholder.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import net.minecraft.world.Container; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; - -/** - * An implementation of a slot as used by a menu that may have fake placeholder items. - * - *

Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.

- */ -public abstract class SlotPlaceholder extends Slot { - - public SlotPlaceholder(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - public abstract ItemStack getOrDefault(); - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java deleted file mode 100644 index 76981cff..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/container/slot/SlotViewOnly.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.container.slot; - -import com.lishid.openinv.internal.v1_21_R3.container.Placeholders; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; - -/** - * A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content. - */ -public class SlotViewOnly extends SlotPlaceholder { - - public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) { - SlotViewOnly wrapper; - if (wrapped instanceof SlotPlaceholder placeholder) { - wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) { - @Override - public ItemStack getOrDefault() { - return placeholder.getOrDefault(); - } - }; - } else { - wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) { - @Override - public ItemStack getOrDefault() { - return wrapped.getItem(); - } - }; - } - wrapper.index = wrapped.index; - return wrapper; - } - - public SlotViewOnly(Container container, int index, int x, int y) { - super(container, index, x, y); - } - - @Override - public ItemStack getOrDefault() { - return Placeholders.notSlot; - } - - @Override - public void onQuickCraft(ItemStack var0, ItemStack var1) { - } - - @Override - public void onTake(Player var0, ItemStack var1) { - } - - @Override - public boolean mayPlace(ItemStack var0) { - return false; - } - - @Override - public ItemStack getItem() { - return ItemStack.EMPTY; - } - - @Override - public boolean hasItem() { - return false; - } - - @Override - public void setByPlayer(ItemStack newStack) { - } - - @Override - public void setByPlayer(ItemStack newStack, ItemStack oldStack) { - } - - @Override - public void set(ItemStack var0) { - } - - @Override - public void setChanged() { - } - - @Override - public int getMaxStackSize() { - return 0; - } - - @Override - public int getMaxStackSize(ItemStack itemStack) { - return 0; - } - - @Override - public ItemStack remove(int amount) { - return ItemStack.EMPTY; - } - - @Override - public boolean mayPickup(Player var0) { - return false; - } - - @Override - public boolean isActive() { - return false; - } - - @Override - public Optional tryRemove(int var0, int var1, Player var2) { - return Optional.empty(); - } - - @Override - public ItemStack safeTake(int var0, int var1, Player var2) { - return ItemStack.EMPTY; - } - - @Override - public ItemStack safeInsert(ItemStack itemStack) { - return itemStack; - } - - @Override - public ItemStack safeInsert(ItemStack itemStack, int amount) { - return itemStack; - } - - @Override - public boolean allowModification(Player var0) { - return false; - } - - @Override - public int getContainerSlot() { - return this.slot; - } - - @Override - public boolean isHighlightable() { - return false; - } - - @Override - public boolean isFake() { - return true; - } - -} diff --git a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java b/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java deleted file mode 100644 index 4fd1cb72..00000000 --- a/internal/v1_21_R3/src/main/java/com/lishid/openinv/internal/v1_21_R3/player/OpenPlayer.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.lishid.openinv.internal.v1_21_R3.player; - -import com.lishid.openinv.event.OpenEvents; -import com.mojang.logging.LogUtils; -import net.minecraft.Util; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.storage.PlayerDataStorage; -import org.bukkit.craftbukkit.v1_21_R3.CraftServer; -import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Set; - -public class OpenPlayer extends CraftPlayer { - - /** - * List of tags to always reset when saving. - * - * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) - * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - */ - private static final Set RESET_TAGS = Set.of( - // Entity#saveWithoutId(CompoundTag) - "CustomName", - "CustomNameVisible", - "Silent", - "NoGravity", - "Glowing", - "TicksFrozen", - "HasVisualFire", - "Tags", - "Passengers", - // ServerPlayer#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - "warden_spawn_tracker", - "enteredNetherPosition", - "SpawnX", - "SpawnY", - "SpawnZ", - "SpawnForced", - "SpawnAngle", - "SpawnDimension", - "raid_omen_position", - "ender_pearls", - // Player#addAdditionalSaveData(CompoundTag) - "ShoulderEntityLeft", - "ShoulderEntityRight", - "LastDeathLocation", - "current_explosion_impact_pos", - // LivingEntity#addAdditionalSaveData(CompoundTag) - "active_effects", - "SleepingX", - "SleepingY", - "SleepingZ", - "Brain" - ); - - private final PlayerManager manager; - - OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { - super(server, entity); - this.manager = manager; - } - - @Override - public void loadData() { - manager.loadData(getHandle()); - } - - @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); - // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); - CompoundTag playerData = getWritableTag(oldData); - playerData = player.saveWithoutId(playerData); - setExtraData(playerData); - - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, tempFile); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, tempFile, backupFile); - } catch (Exception e) { - LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); - } - } - - @Contract("null -> new") - private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { - if (oldData == null) { - return new CompoundTag(); - } - - // Copy old data. This is a deep clone, so operating on it should be safe. - oldData = oldData.copy(); - - // Remove vanilla/server data that is not written every time. - oldData.getAllKeys() - .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); - - return oldData; - } - - private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { - // Revert automatic updates to play timestamps. - copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); - } - - private void copyValue( - @NotNull CompoundTag source, - @NotNull CompoundTag target, - @NotNull String container, - @NotNull String key, - @SuppressWarnings("SameParameterValue") @NotNull Class tagType) { - CompoundTag oldContainer = getTag(source, container, CompoundTag.class); - CompoundTag newContainer = getTag(target, container, CompoundTag.class); - - // New container being null means the server implementation doesn't store this data. - if (newContainer == null) { - return; - } - - // If old tag exists, copy it to new location, removing otherwise. - setTag(newContainer, key, getTag(oldContainer, key, tagType)); - } - - private @Nullable T getTag( - @Nullable CompoundTag container, - @NotNull String key, - @NotNull Class dataType) { - if (container == null) { - return null; - } - Tag value = container.get(key); - if (value == null || !dataType.isAssignableFrom(value.getClass())) { - return null; - } - return dataType.cast(value); - } - - private void setTag( - @NotNull CompoundTag container, - @NotNull String key, - @Nullable T data) { - if (data == null) { - container.remove(key); - } else { - container.put(key, data); - } - } - -} diff --git a/jitpack.yml b/jitpack.yml index 6fd76428..bd3dacf9 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,4 +1,6 @@ before_install: - - sdk install java 21.0.4-tem - - sdk use java 21.0.4-tem - - mvn wrapper:wrapper -Dmaven=3.9.8 + - sdk update + - sdk install java 21-tem + - sdk use java 21-tem +install: + - ./gradlew :openinvapi:publishJitpackPublicationToMavenLocal diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts new file mode 100644 index 00000000..713660c4 --- /dev/null +++ b/plugin/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + `openinv-base` + alias(libs.plugins.shadow) +} + +repositories { + maven("https://jitpack.io") +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterspigot", configuration = "shadow")) + implementation(libs.planarwrappers) +} + +tasks.processResources { + expand("version" to version) +} + +tasks.jar { + manifest.attributes("paperweight-mappings-namespace" to "mojang") +} + +tasks.shadowJar { + dependsOn(":openinvadapterspigot:reobf") + minimize { + exclude(":openinv**") + } +} + +tasks.register("distributePlugin") { + into(rootProject.layout.projectDirectory.dir("dist")) + from(tasks.shadowJar) + rename("openinvplugin.*\\.jar", "OpenInv.jar") +} + +tasks.assemble { + dependsOn(tasks.shadowJar) + dependsOn(tasks.named("distributePlugin")) +} diff --git a/plugin/pom.xml b/plugin/pom.xml deleted file mode 100644 index 9657ea6b..00000000 --- a/plugin/pom.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - 4.0.0 - - - openinvparent - com.lishid - 5.1.7-SNAPSHOT - - - openinvplugin - OpenInvPlugin - - - - spigot-api - org.spigotmc - - - planarwrappers - com.github.jikoo - - - openinvapi - com.lishid - - - openinvcommon - com.lishid - - - com.lishid - openinvadapter1_21_R3 - 5.1.7-SNAPSHOT - compile - - - com.lishid - openinvadapter1_21_R2 - 5.1.7-SNAPSHOT - compile - - - annotations - org.jetbrains - - - - - OpenInv - - - src/main/resources - true - - - - - - maven-shade-plugin - - - package - - shade - - - - - - - - com.lishid:openinv* - - ** - - - - *:* - - - META-INF/MANIFEST.MF - - - - - - com.github.jikoo.planarwrappers - com.github.jikoo.openinv.planarwrappers - - - true - - - - maven-compiler-plugin - - - maven-resources-plugin - - - copy-final-jar - package - - copy-resources - - - ${project.parent.build.directory} - - - ${project.build.directory} - - ${project.build.finalName}.jar - - - - - - - - - - - diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 86f1ecff..c3ee3250 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -172,12 +172,6 @@ private void sendVersionError(@NotNull Consumer messageMethod) { if (!accessor.isSupported()) { messageMethod.accept("Your server version (" + accessor.getVersion() + ") is not supported."); messageMethod.accept("Please download the correct version of OpenInv here: " + accessor.getReleasesLink()); - - // We check this property late so users can use jars that were remapped by Paper already. - if (Boolean.getBoolean("paper.disable-plugin-rewriting")) { - messageMethod.accept("OpenInv uses Spigot-mapped internals, but you have disabled plugin rewriting in Paper!"); - messageMethod.accept("Please set system property 'paper.disable-plugin-rewriting' to false."); - } } if (!isSpigot) { messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread"); diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index a29b0f13..bee9ca87 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -38,12 +38,25 @@ public class InternalAccessor { private @Nullable Accessor internal; public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + boolean paper = false; + try { + Class.forName("io.papermc.paper.configuration.GlobalConfiguration"); + paper = true; + } catch (ClassNotFoundException ignored) { + // Expect remapped server. + } try { - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) { - internal = new com.lishid.openinv.internal.v1_21_R3.InternalAccessor(logger, lang); - } else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 3))) { - internal = new com.lishid.openinv.internal.v1_21_R2.InternalAccessor(logger, lang); + Version maxSupported = Version.of(1, 21, 4); + // Placeholders currently mandate 1.21.4 minimum. + Version minSupported = Version.of(1, 21, 4); + if (!paper) { + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { + internal = new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); + } + } else if (BukkitVersions.MINECRAFT.greaterThanOrEqual(minSupported) + && BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported)) { + internal = new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); } if (internal != null) { InventoryAccess.setProvider(internal::get); @@ -127,6 +140,16 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) { return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3"; } + boolean paper = false; + try { + Class.forName("io.papermc.paper.configuration.GlobalConfiguration"); + paper = true; + } catch (ClassNotFoundException ignored) { + // Expected on Spigot. + } + if (!paper && BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.6"; + } return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index dd8014a2..79a5c9e5 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -1,10 +1,10 @@ name: OpenInv main: com.lishid.openinv.OpenInv -version: ${project.version} +version: ${version} author: lishid authors: [Jikoo, ShadowRanger] description: Open a player's inventory as a chest and interact with it in real time. -api-version: "1.16" +api-version: "1.13" permissions: diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 7e8a9ed9..00000000 --- a/pom.xml +++ /dev/null @@ -1,221 +0,0 @@ - - - - 4.0.0 - - com.lishid - openinvparent - OpenInv - http://dev.bukkit.org/bukkit-plugins/openinv/ - - common - - 5.1.7-SNAPSHOT - - pom - - - UTF-8 - 17 - 17 - - unknown - - - - - all - - api - addon/togglepersist - common - internal/v1_21_R3 - internal/v1_21_R2 - plugin - - - - - api-only - - true - - - api - - - - - - - spigot-repo - https://hub.spigotmc.org/nexus/content/groups/public/ - - - jitpack.io - https://jitpack.io - - - - - - - annotations - org.jetbrains - provided - 26.0.2 - - - spigot-api - org.spigotmc - provided - 1.21.1-R0.1-SNAPSHOT - - - openinvapi - com.lishid - compile - ${project.version} - - - openinvcommon - com.lishid - compile - ${project.version} - - - planarwrappers - com.github.jikoo - compile - 3.3.0 - - - - - - - - - - maven-dependency-plugin - org.apache.maven.plugins - 3.8.1 - - - - maven-shade-plugin - org.apache.maven.plugins - 3.6.0 - - - - maven-resources-plugin - org.apache.maven.plugins - 3.3.1 - - - - maven-jar-plugin - org.apache.maven.plugins - 3.4.2 - - - - spigot - - - - - - - maven-compiler-plugin - - - - com.google.errorprone - error_prone_core - 2.36.0 - - - - -XDcompilePolicy=simple - --should-stop=ifError=FLOW - -Xplugin:ErrorProne - -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED - -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED - -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED - - true - - org.apache.maven.plugins - 3.13.0 - - - - maven-assembly-plugin - org.apache.maven.plugins - 3.7.1 - - - - specialsource-maven-plugin - - - package - - remap - - remap-obf - - org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang - true - org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang - true - remapped-obf - - - - package - - remap - - remap-spigot - - ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar - org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot - org.spigotmc:spigot:${spigot.version}:jar:remapped-obf - - - - net.md-5 - 2.0.3 - - - - - - diff --git a/resource-pack/build.gradle.kts b/resource-pack/build.gradle.kts new file mode 100644 index 00000000..a5e0996f --- /dev/null +++ b/resource-pack/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + `base` +} + +tasks.register("buildResourcePack") { + archiveFileName = "openinv-legibility-pack.zip" + destinationDirectory = rootProject.layout.projectDirectory.dir("dist") + + from("openinv-legibility-pack") + with (copySpec { + include("**/*.json", "**/*.png", "pack.mcmeta") + }) +} + +tasks.assemble { + dependsOn(tasks.named("buildResourcePack")) +} diff --git a/resource-pack/assets/openinv/models/item/crafting_output.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/crafting_output.json similarity index 100% rename from resource-pack/assets/openinv/models/item/crafting_output.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/crafting_output.json diff --git a/resource-pack/assets/openinv/models/item/cursor.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/cursor.json similarity index 100% rename from resource-pack/assets/openinv/models/item/cursor.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/cursor.json diff --git a/resource-pack/assets/openinv/models/item/drop.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/drop.json similarity index 100% rename from resource-pack/assets/openinv/models/item/drop.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/drop.json diff --git a/resource-pack/assets/openinv/models/item/empty_boots.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_boots.json similarity index 100% rename from resource-pack/assets/openinv/models/item/empty_boots.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_boots.json diff --git a/resource-pack/assets/openinv/models/item/empty_chestplate.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_chestplate.json similarity index 100% rename from resource-pack/assets/openinv/models/item/empty_chestplate.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_chestplate.json diff --git a/resource-pack/assets/openinv/models/item/empty_helmet.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_helmet.json similarity index 100% rename from resource-pack/assets/openinv/models/item/empty_helmet.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_helmet.json diff --git a/resource-pack/assets/openinv/models/item/empty_leggings.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_leggings.json similarity index 100% rename from resource-pack/assets/openinv/models/item/empty_leggings.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_leggings.json diff --git a/resource-pack/assets/openinv/models/item/empty_shield.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_shield.json similarity index 100% rename from resource-pack/assets/openinv/models/item/empty_shield.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/empty_shield.json diff --git a/resource-pack/assets/openinv/models/item/not_a_slot.json b/resource-pack/openinv-legibility-pack/assets/openinv/models/item/not_a_slot.json similarity index 100% rename from resource-pack/assets/openinv/models/item/not_a_slot.json rename to resource-pack/openinv-legibility-pack/assets/openinv/models/item/not_a_slot.json diff --git a/resource-pack/assets/openinv/textures/item/crafting_output.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/crafting_output.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/crafting_output.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/crafting_output.png diff --git a/resource-pack/assets/openinv/textures/item/cursor.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/cursor.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/cursor.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/cursor.png diff --git a/resource-pack/assets/openinv/textures/item/drop.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/drop.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/drop.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/drop.png diff --git a/resource-pack/assets/openinv/textures/item/empty_boots.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_boots.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/empty_boots.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_boots.png diff --git a/resource-pack/assets/openinv/textures/item/empty_chestplate.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_chestplate.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/empty_chestplate.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_chestplate.png diff --git a/resource-pack/assets/openinv/textures/item/empty_helmet.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_helmet.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/empty_helmet.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_helmet.png diff --git a/resource-pack/assets/openinv/textures/item/empty_leggings.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_leggings.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/empty_leggings.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_leggings.png diff --git a/resource-pack/assets/openinv/textures/item/empty_shield.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_shield.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/empty_shield.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/empty_shield.png diff --git a/resource-pack/assets/openinv/textures/item/not_a_slot.png b/resource-pack/openinv-legibility-pack/assets/openinv/textures/item/not_a_slot.png similarity index 100% rename from resource-pack/assets/openinv/textures/item/not_a_slot.png rename to resource-pack/openinv-legibility-pack/assets/openinv/textures/item/not_a_slot.png diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/crafting_table.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/crafting_table.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/crafting_table.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/crafting_table.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/dropper.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/dropper.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/dropper.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/dropper.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_boots.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_boots.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/leather_boots.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_boots.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_chestplate.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_helmet.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/leather_leggings.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/shield.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/shield.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/shield.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/shield.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/white_banner.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/white_banner.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/white_banner.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/white_banner.json diff --git a/resource-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json b/resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json similarity index 100% rename from resource-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json rename to resource-pack/openinv-legibility-pack/openinv_34/assets/minecraft/models/item/white_stained_glass_pane.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/crafting_table.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/crafting_table.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/crafting_table.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/crafting_table.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/dropper.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/dropper.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/dropper.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/dropper.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/leather_boots.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_boots.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/leather_boots.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_boots.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/leather_chestplate.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_chestplate.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/leather_chestplate.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_chestplate.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/leather_helmet.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_helmet.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/leather_helmet.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_helmet.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/leather_leggings.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_leggings.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/leather_leggings.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/leather_leggings.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/shield.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/shield.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/shield.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/shield.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/white_banner.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_banner.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/white_banner.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_banner.json diff --git a/resource-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json similarity index 100% rename from resource-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json rename to resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json diff --git a/resource-pack/pack.mcmeta b/resource-pack/openinv-legibility-pack/pack.mcmeta similarity index 100% rename from resource-pack/pack.mcmeta rename to resource-pack/openinv-legibility-pack/pack.mcmeta diff --git a/resource-pack/pack.png b/resource-pack/openinv-legibility-pack/pack.png similarity index 100% rename from resource-pack/pack.png rename to resource-pack/openinv-legibility-pack/pack.png diff --git a/scripts/generate_changelog.sh b/scripts/generate_changelog.sh deleted file mode 100644 index bc234b9d..00000000 --- a/scripts/generate_changelog.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2011-2023 lishid. All rights reserved. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -# Note that this script is designed for use in GitHub Actions, and is not -# particularly robust nor configurable. Run from project parent directory. - -# Query GitHub for the username of the given email address. -# Falls through to the given author name. -function lookup_email_username() { - lookup=$(curl -G --data-urlencode "q=$1 in:email" https://api.github.com/search/users -H 'Accept: application/vnd.github.v3+json' | grep '"login":' | sed -e 's/^.*": "//g' -e 's/",.*$//g') - - if [[ $lookup ]]; then - echo -n "@$lookup" - else - echo "$2" - fi -} - -# Get a pretty list of supported Minecraft versions -function get_minecraft_versions() { - readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" - - for version in "${versions[@]}"; do - # Append comma if variable is set, then append version - minecraft_versions="${minecraft_versions:+${minecraft_versions}, }${version%%-R*}" - done - - echo "${minecraft_versions}" -} - -previous_tag=$(git describe --tags --abbrev=0 @^) - -# Use formatted log to pull authors list -authors_raw=$(git log --pretty=format:"%ae|%an" "$previous_tag"..@) -readarray -t authors <<<"$authors_raw" - -# Use associative array to map email to author name -declare -A author_data - -for author in "${authors[@]}"; do - # Match author email - author_email=${author%|*} - # Convert to lower case - author_email=${author_email,,} - # Match author name - author_name=${author##*|} - if [[ -n ${author_data[$author_email]} ]]; then - # Skip emails we already have data for - continue - fi - - # Fetch and store author GitHub username by email - author_data[$author_email]=$(lookup_email_username "$author_email" "$author_name") -done - -# Fetch actual formatted changelog -changelog=$(git log --pretty=format:"* %s (%h) - %ae" "$previous_tag"..@) - -for author_email in "${!author_data[@]}"; do - # Ignore case when matching - shopt -s nocasematch - # Match and replace email - changelog=${changelog//$author_email/${author_data[$author_email]}} -done - -minecraft_versions=$(get_minecraft_versions) - -printf "## Supported Minecraft versions\n%s\n\n## Changelog\n%s" "${minecraft_versions}" "${changelog}" diff --git a/scripts/get_spigot_versions.sh b/scripts/get_spigot_versions.sh deleted file mode 100644 index c4e7b264..00000000 --- a/scripts/get_spigot_versions.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2011-2022 lishid. All rights reserved. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -# Note that this script is designed for use in GitHub Actions, and is not -# particularly robust nor configurable. Run from project parent directory. - -# Use a nameref as a cache - maven evaluation is pretty slow. -# Re-calling the script and relying on it to handle caching is way easier than passing around info. -declare -a spigot_versions - -# We don't care about concatenation - either it's not null and we handle entries or it's null and we instantiate. -# shellcheck disable=SC2199 -if [[ ${spigot_versions[@]} ]]; then - for spigot_version in "${spigot_versions[@]}"; do - echo "$spigot_version" - done - return -fi - -old_maven_opts=$MAVEN_OPTS -# Add JVM parameters to allow help plugin access to packages it needs. -export MAVEN_OPTS="$old_maven_opts --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED" - -# Pull Spigot dependency information from Maven. -# Since we only care about Spigot versions, only check modules in the folder internal. -readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all | grep -oP '(?<=)(internal/.*)(?=)')" - -declare -n versions="spigot_versions" - -for module in "${modules[@]}"; do - # Get Spigot version. - spigot_version=$(mvn help:evaluate -Dexpression=spigot.version -q -DforceStdout -P all -pl "$module") - versions+=("$spigot_version") - echo "$spigot_version" -done - -# Reset JVM parameters -export MAVEN_OPTS=$old_maven_opts \ No newline at end of file diff --git a/scripts/install_spigot_dependencies.sh b/scripts/install_spigot.sh old mode 100644 new mode 100755 similarity index 54% rename from scripts/install_spigot_dependencies.sh rename to scripts/install_spigot.sh index 61668d42..e9f98870 --- a/scripts/install_spigot_dependencies.sh +++ b/scripts/install_spigot.sh @@ -15,9 +15,6 @@ # along with this program. If not, see . # -# Note that this script is designed for use in GitHub Actions, and is not -# particularly robust nor configurable. Run from project parent directory. - buildtools_dir=~/buildtools buildtools=$buildtools_dir/BuildTools.jar @@ -30,22 +27,13 @@ get_buildtools () { wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools } -readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" -echo Found Spigot dependencies: "${versions[@]}" +if [[ ! $1 ]]; then + echo "Please specify Spigot version to install." + exit 1 +fi -# Install dependencies aside from Spigot prior to running in offline mode. -mvn dependency:go-offline -DexcludeArtifactIds=spigot +get_buildtools -for version in "${versions[@]}"; do - set -e - exit_code=0 - mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version":remapped-mojang -q -o || exit_code=$? - if [ $exit_code -ne 0 ]; then - echo Installing missing Spigot version "$version" - revision=${version%%-R*} - get_buildtools - java -jar $buildtools -rev "$revision" --remapped - else - echo Spigot "$version" is already installed - fi -done +pushd $buildtools_dir +java -jar $buildtools -rev $1 --remapped +popd diff --git a/scripts/set_curseforge_env.sh b/scripts/set_curseforge_env.sh old mode 100644 new mode 100755 index 09f08b45..e345163b --- a/scripts/set_curseforge_env.sh +++ b/scripts/set_curseforge_env.sh @@ -20,7 +20,8 @@ # Parse Spigot dependency information into major Minecraft versions function get_curseforge_minecraft_versions() { - readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)" + # TODO convert to parsing event body + readarray -t versions <<< "1.21.4" for version in "${versions[@]}"; do # Parse Minecraft major version @@ -38,18 +39,5 @@ function get_curseforge_minecraft_versions() { echo "${minecraft_versions}" } -# Modify provided changelog to not break when inserted into yaml file. -function get_yaml_safe_changelog() { - changelog=$1 - # Since we're using a flow scalar, newlines need to be doubled. - echo "${changelog// -/ - -}" -} - minecraft_versions=$(get_curseforge_minecraft_versions) echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV" - -changelog=$(get_yaml_safe_changelog "$1") -printf "CURSEFORGE_CHANGELOG<> "$GITHUB_ENV" \ No newline at end of file diff --git a/scripts/set_release_env.sh b/scripts/set_release_env.sh deleted file mode 100644 index 3d850dcc..00000000 --- a/scripts/set_release_env.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2011-2021 lishid. All rights reserved. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -# Note that this script is designed for use in GitHub Actions, and is not -# particularly robust nor configurable. Run from project parent directory. - -# Get a pretty string of the project's name and version -# Disable SC warning about variable expansion for this function - those are Maven variables. -# shellcheck disable=SC2016 -function get_versioned_name() { - mvn -q -Dexec.executable=echo -Dexec.args='${project.name} ${project.version}' --non-recursive exec:exec -} - -# Set GitHub environmental variables -echo "VERSIONED_NAME=$(get_versioned_name)" >> "$GITHUB_ENV" - -changelog="$(. ./scripts/generate_changelog.sh)" -printf "GENERATED_CHANGELOG<> "$GITHUB_ENV" diff --git a/scripts/tag_release.sh b/scripts/tag_release.sh old mode 100644 new mode 100755 diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..98321d8c --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,31 @@ +rootProject.name = "openinvparent" + +include(":openinvapi") +project(":openinvapi").projectDir = file("api") + +val addons = listOf( + "togglepersist" +) +for (addon in addons) { + include(":addon$addon") + val proj = project(":addon$addon") + proj.projectDir = file("addon/$addon") + proj.name = "openinv$addon" +} + +include(":openinvcommon") +project(":openinvcommon").projectDir = file("common") + +val internals = listOf( + "common", + "spigot" +) +for (internal in internals) { + include(":openinvadapter$internal") + project(":openinvadapter$internal").projectDir = file("internal/$internal") +} + +include(":resource-pack") + +include(":plugin") +project(":plugin").name = "openinvplugin" From 0df5e39a6da1d65c8c71353cc9f22dab61a89ec6 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 9 Feb 2025 22:37:37 -0500 Subject: [PATCH 257/340] Correct release notes Paper 1.21.3 requires changes to placeholders and is not currently supported. --- .../java/com/lishid/openinv/util/InternalAccessor.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index bee9ca87..3b29cfea 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -140,14 +140,7 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) { return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3"; } - boolean paper = false; - try { - Class.forName("io.papermc.paper.configuration.GlobalConfiguration"); - paper = true; - } catch (ClassNotFoundException ignored) { - // Expected on Spigot. - } - if (!paper && BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) { + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) { return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.6"; } return "https://github.com/Jikoo/OpenInv/releases"; From 49e408ee7a71db1febde9411f0e822a23e7c33a7 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 9 Feb 2025 22:38:13 -0500 Subject: [PATCH 258/340] Bump version to 5.1.7 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 72adefbb..7f86a236 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group = com.lishid.openinv -version = 5.1.7-SNAPSHOT +version = 5.1.7 description = A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 04ed652073ee4cb776847e6c4198cb2aba3c4c00 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 9 Feb 2025 22:38:34 -0500 Subject: [PATCH 259/340] Bump version to 5.1.8-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7f86a236..cdd4e893 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group = com.lishid.openinv -version = 5.1.7 +version = 5.1.8-SNAPSHOT description = A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 73e003bde40e8d99ecf8464e898fb6ffa33c72ee Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 9 Feb 2025 22:40:28 -0500 Subject: [PATCH 260/340] Update dependabot ecosystem --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ab4dca3..f24e4164 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,7 @@ updates: directory: "/" schedule: interval: "monthly" - - package-ecosystem: "maven" + - package-ecosystem: "gradle" directory: "/" schedule: interval: "monthly" From e9d63f31ed042ed5e8596612900d907d4f689513 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 03:45:09 +0000 Subject: [PATCH 261/340] Bump com.gradleup.shadow from 8.3.5 to 8.3.6 (#285) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5c5a9257..5bacf617 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ specialsource = "1.11.4" planarwrappers = "3.3.0" annotations = "26.0.2" paperweight = "2.0.0-beta.14" -shadow = "8.3.5" +shadow = "8.3.6" [libraries] spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } From 9f50de5dfee55067ac97a6ad20094b0563f65e96 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 10 Feb 2025 11:46:41 -0500 Subject: [PATCH 262/340] Unify Spigot dependency version information declaration May be worth adding an entry for latest MC version that is shared between common Paper and current Spigot modules. --- build.gradle.kts | 3 +++ gradle/libs.versions.toml | 2 -- internal/common/build.gradle.kts | 3 +-- internal/spigot/build.gradle.kts | 7 +++++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7b53201e..3c7f2fc0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,9 @@ plugins { alias(libs.plugins.shadow) apply false } +// Set by Spigot module, used by Paper module to convert to Spigot's version of Mojang mappings. +project.ext.set("craftbukkitPackage", "UNKNOWN") + repositories { maven("https://repo.papermc.io/repository/maven-public/") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5bacf617..e4409e54 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,5 @@ [versions] spigotapi = "1.21.4-R0.1-SNAPSHOT" -spigot = "1.21.4-R0.1-SNAPSHOT" specialsource = "1.11.4" planarwrappers = "3.3.0" annotations = "26.0.2" @@ -9,7 +8,6 @@ shadow = "8.3.6" [libraries] spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } -spigot = { module = "org.spigotmc:spigot", version.ref = "spigot" } specialsource = { module = "net.md-5:SpecialSource", version.ref = "specialsource" } planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" } annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 6ef2e512..936fe966 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -30,8 +30,7 @@ val spigot = tasks.register("spigotRelocations") { dependsOn(tasks.jar) from(sourceSets.main.get().output) relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf") - // TODO pass from Spigot adapter somehow. Can tasks be added by another project? Can we fetch a global config? - relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.v1_21_R3") + relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.${rootProject.extra["craftbukkitPackage"]}") archiveClassifier = "spigot" } diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index d4d58426..18e39ec3 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -8,6 +8,9 @@ repositories { mavenLocal() } +val spigotVer = "1.21.4-R0.1-SNAPSHOT" +rootProject.extra["craftbukkitPackage"] = "v1_21_R3" + configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val spigot = candidates.firstOrNull { @@ -24,7 +27,7 @@ configurations.all { dependencies { compileOnly(libs.spigotapi) - compileOnly(variantOf(libs.spigot) { classifier("remapped-mojang") }) + compileOnly(create("org.spigotmc", "spigot", spigotVer, classifier = "remapped-mojang")) compileOnly(project(":openinvapi")) compileOnly(project(":openinvcommon")) @@ -43,5 +46,5 @@ tasks.register("reobf") { notCompatibleWithConfigurationCache("gradle is hard") dependsOn(tasks.shadowJar) inputs.files(tasks.shadowJar.get().outputs.files.files) - spigotVersion = libs.spigot.get().version.toString() + spigotVersion = spigotVer } From e4fdd613aed75f7beb2047f7d98d8d56bede5196 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 12 Feb 2025 08:01:53 -0500 Subject: [PATCH 263/340] Expand support back to Paper 1.21.1 (#287) --- .../internal/common/InternalAccessor.java | 15 ++- .../common/container/OpenInventory.java | 7 +- .../container/slot/ContentCrafting.java | 4 +- .../container/slot/ContentCraftingResult.java | 2 +- .../common/container/slot/ContentCursor.java | 2 +- .../common/container/slot/ContentDrop.java | 2 +- .../container/slot/ContentEquipment.java | 2 +- .../container/slot/ContentViewOnly.java | 2 +- .../common/container/slot/SlotViewOnly.java | 2 +- .../slot/placeholder/PlaceholderLoader.java | 18 +++ .../placeholder/PlaceholderLoaderBase.java} | 109 ++++++++---------- .../slot/placeholder/Placeholders.java | 37 ++++++ .../internal/common/player/PlayerManager.java | 9 +- internal/paper1_21_1/build.gradle.kts | 27 +++++ .../paper1_21_1/InternalAccessor.java | 80 +++++++++++++ .../paper1_21_1/container/OpenInventory.java | 20 ++++ .../container/slot/ContentCraftingResult.java | 46 ++++++++ .../slot/placeholder/PlaceholderLoader.java | 65 +++++++++++ .../paper1_21_1/player/PlayerManager.java | 54 +++++++++ internal/paper1_21_3/build.gradle.kts | 26 +++++ .../paper1_21_3/InternalAccessor.java | 28 +++++ .../NumericDataPlaceholderLoader.java | 31 +++++ plugin/build.gradle.kts | 2 + .../lishid/openinv/util/InternalAccessor.java | 57 ++++++--- settings.gradle.kts | 2 + 25 files changed, 555 insertions(+), 94 deletions(-) create mode 100644 internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java rename internal/common/src/main/java/com/lishid/openinv/internal/common/container/{Placeholders.java => slot/placeholder/PlaceholderLoaderBase.java} (61%) create mode 100644 internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java create mode 100644 internal/paper1_21_1/build.gradle.kts create mode 100644 internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java create mode 100644 internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java create mode 100644 internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/ContentCraftingResult.java create mode 100644 internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java create mode 100644 internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java create mode 100644 internal/paper1_21_3/build.gradle.kts create mode 100644 internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java create mode 100644 internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java index c9aea82d..16218663 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/InternalAccessor.java @@ -8,7 +8,7 @@ import com.lishid.openinv.internal.common.container.AnySilentContainer; import com.lishid.openinv.internal.common.container.OpenEnderChest; import com.lishid.openinv.internal.common.container.OpenInventory; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoader; import com.lishid.openinv.internal.common.player.PlayerManager; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; @@ -24,7 +24,7 @@ public class InternalAccessor implements Accessor { - private final @NotNull Logger logger; + protected final @NotNull Logger logger; private final @NotNull PlayerManager manager; private final @NotNull AnySilentContainer anySilentContainer; @@ -69,12 +69,11 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { @Override public void reload(@NotNull ConfigurationSection config) { ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); - if (placeholders != null) { - try { - Placeholders.load(placeholders); - } catch (Exception e) { - logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); - } + try { + // Reset placeholders to defaults and try to load configuration. + new PlaceholderLoader().load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); } } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 899be70f..ae7bdcf2 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -14,6 +14,7 @@ import com.lishid.openinv.internal.common.container.slot.ContentOffHand; import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import com.lishid.openinv.internal.common.player.PlayerManager; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; @@ -191,12 +192,16 @@ public ItemStack getOrDefault() { }; } }); - slots.set(startIndex + 11, new ContentCraftingResult(owner)); + slots.set(startIndex + 11, getCraftingResult(owner)); } return startIndex + listSize; } + protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) { + return new ContentCraftingResult(serverPlayer); + } + public Slot getMenuSlot(int index, int x, int y) { return slots.get(index).asSlot(this, index, x, y); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java index 4621af79..c2eb9cc6 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -31,7 +31,7 @@ private boolean isAvailable() { return isAvailable(holder); } - static boolean isAvailable(@NotNull ServerPlayer holder) { + public static boolean isAvailable(@NotNull ServerPlayer holder) { // Player must be online and not in creative - since the creative client is (semi-)authoritative, // it ignores changes without extra help, and will delete the item as a result. // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java index 57b612e5..e930c8e8 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCraftingResult.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.InventoryMenu; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java index d0b16f17..38e7033d 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java index 1d3d8b0e..97d53718 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java index 60731e4a..71d9f826 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.EquipmentSlot; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java index 46076658..319ef0e5 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentViewOnly.java @@ -12,7 +12,7 @@ */ public class ContentViewOnly implements Content { - @NotNull ServerPlayer holder; + protected @NotNull ServerPlayer holder; public ContentViewOnly(@NotNull ServerPlayer holder) { this.holder = holder; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java index 7fb43763..3fe82fd9 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/SlotViewOnly.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.container.Placeholders; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java new file mode 100644 index 00000000..1fdafd73 --- /dev/null +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java @@ -0,0 +1,18 @@ +package com.lishid.openinv.internal.common.container.slot.placeholder; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomModelData; + +import java.util.List; + +public class PlaceholderLoader extends PlaceholderLoaderBase { + + private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of()); + + @Override + protected void addModelData(ItemStack itemStack) { + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + } + +} diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/Placeholders.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java similarity index 61% rename from internal/common/src/main/java/com/lishid/openinv/internal/common/container/Placeholders.java rename to internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java index 75a0834e..c32a88ce 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/Placeholders.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java @@ -1,6 +1,5 @@ -package com.lishid.openinv.internal.common.container; +package com.lishid.openinv.internal.common.container.slot.placeholder; -import com.lishid.openinv.internal.common.player.OpenPlayer; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; @@ -8,12 +7,10 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.TagParser; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Unit; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import net.minecraft.world.item.component.CustomModelData; import net.minecraft.world.item.component.DyedItemColor; import net.minecraft.world.level.GameType; import net.minecraft.world.level.ItemLike; @@ -23,56 +20,57 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.craftbukkit.CraftRegistry; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.EnumMap; import java.util.List; import java.util.Optional; -public final class Placeholders { - - private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of()); - public static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); - public static @NotNull ItemStack craftingOutput = defaultCraftingOutput(); - public static @NotNull ItemStack cursor = defaultCursor(); - public static @NotNull ItemStack drop = defaultDrop(); - public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); - public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); - public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); - public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); - public static @NotNull ItemStack emptyOffHand = getEmptyShield(); - public static @NotNull ItemStack notSlot = defaultNotSlot(); - public static @NotNull ItemStack blockedOffline = defaultBlockedOffline(); - - static { +public abstract class PlaceholderLoaderBase { + + protected PlaceholderLoaderBase() { for (GameType type : GameType.values()) { // Barrier: "Not available - Creative" etc. ItemStack typeItem = new ItemStack(Items.BARRIER); typeItem.set( DataComponents.ITEM_NAME, Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName())); - BLOCKED_GAME_TYPE.put(type, typeItem); + Placeholders.BLOCKED_GAME_TYPE.put(type, typeItem); } + Placeholders.craftingOutput = defaultCraftingOutput(); + Placeholders.cursor = defaultCursor(); + Placeholders.drop = defaultDrop(); + Placeholders.emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); + Placeholders.emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); + Placeholders.emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); + Placeholders.emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); + Placeholders.emptyOffHand = defaultShield(); + Placeholders.notSlot = defaultNotSlot(); + Placeholders.blockedOffline = defaultBlockedOffline(); } - public static void load(@NotNull ConfigurationSection section) throws Exception { - craftingOutput = parse(section, "crafting-output", craftingOutput); - cursor = parse(section, "cursor", cursor); - drop = parse(section, "drop", drop); - emptyHelmet = parse(section, "empty-helmet", emptyHelmet); - emptyChestplate = parse(section, "empty-chestplate", emptyChestplate); - emptyLeggings = parse(section, "empty-leggings", emptyLeggings); - emptyBoots = parse(section, "empty-boots", emptyBoots); - emptyOffHand = parse(section, "empty-off-hand", emptyOffHand); - notSlot = parse(section, "not-a-slot", notSlot); - blockedOffline = parse(section, "blocked.offline", blockedOffline); - BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE))); - BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); + public void load(@Nullable ConfigurationSection section) throws Exception { + Placeholders.craftingOutput = parse(section, "crafting-output", Placeholders.craftingOutput); + Placeholders.cursor = parse(section, "cursor", Placeholders.cursor); + Placeholders.drop = parse(section, "drop", Placeholders.drop); + Placeholders.emptyHelmet = parse(section, "empty-helmet", Placeholders.emptyHelmet); + Placeholders.emptyChestplate = parse(section, "empty-chestplate", Placeholders.emptyChestplate); + Placeholders.emptyLeggings = parse(section, "empty-leggings", Placeholders.emptyLeggings); + Placeholders.emptyBoots = parse(section, "empty-boots", Placeholders.emptyBoots); + Placeholders.emptyOffHand = parse(section, "empty-off-hand", Placeholders.emptyOffHand); + Placeholders.notSlot = parse(section, "not-a-slot", Placeholders.notSlot); + Placeholders.blockedOffline = parse(section, "blocked.offline", Placeholders.blockedOffline); + Placeholders.BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", Placeholders.BLOCKED_GAME_TYPE.get(GameType.CREATIVE))); + Placeholders.BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", Placeholders.BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); } private static @NotNull ItemStack parse( - @NotNull ConfigurationSection section, + @Nullable ConfigurationSection section, @NotNull String path, @NotNull ItemStack defaultStack) throws Exception { + if (section == null) { + return defaultStack; + } + String itemText = section.getString(path); if (itemText == null) { @@ -84,23 +82,17 @@ public static void load(@NotNull ConfigurationSection section) throws Exception return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); } - public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { - if (!OpenPlayer.isConnected(serverPlayer.connection)) { - return blockedOffline; - } + protected abstract void addModelData(ItemStack itemStack); - return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY); - } - - private static ItemStack defaultCraftingOutput() { + protected @NotNull ItemStack defaultCraftingOutput() { // Crafting table: "Crafting" ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE); itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting")); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + addModelData(itemStack); return itemStack; } - private static ItemStack defaultCursor() { + protected @NotNull ItemStack defaultCursor() { // Cursor-like banner with no tooltip ItemStack itemStack = new ItemStack(Items.WHITE_BANNER); RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); @@ -113,31 +105,32 @@ private static ItemStack defaultCursor() { new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + addModelData(itemStack); itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); return itemStack; } - private static ItemStack defaultDrop() { + protected @NotNull ItemStack defaultDrop() { // Dropper: "Drop Selected Item" ItemStack itemStack = new ItemStack(Items.DROPPER); // Note: translatable component, not keybind component! We want the text identifying the keybind, not the key. itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop")); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + addModelData(itemStack); return itemStack; } - private static ItemStack getEmptyArmor(ItemLike item) { + protected @NotNull ItemStack getEmptyArmor(@NotNull ItemLike item) { // Inventory-background-grey-ish leather armor with no tooltip ItemStack itemStack = new ItemStack(item); DyedItemColor color = new DyedItemColor(0xC8C8C8, false); itemStack.set(DataComponents.DYED_COLOR, color); itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + addModelData(itemStack); return itemStack; } - private static ItemStack getEmptyShield() { + protected @NotNull ItemStack defaultShield() { + // Shield with "missing texture" pattern, magenta and black squares. ItemStack itemStack = new ItemStack(Items.SHIELD); itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); @@ -155,19 +148,19 @@ private static ItemStack getEmptyShield() { new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + addModelData(itemStack); return itemStack; } - private static ItemStack defaultNotSlot() { + protected @NotNull ItemStack defaultNotSlot() { // White pane with no tooltip ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + addModelData(itemStack); return itemStack; } - private static ItemStack defaultBlockedOffline() { + protected @NotNull ItemStack defaultBlockedOffline() { // Barrier: "Not available - Offline" ItemStack itemStack = new ItemStack(Items.BARRIER); itemStack.set(DataComponents.ITEM_NAME, @@ -177,8 +170,4 @@ private static ItemStack defaultBlockedOffline() { return itemStack; } - private Placeholders() { - throw new IllegalStateException("Cannot create instance of utility class."); - } - } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java new file mode 100644 index 00000000..451ff3ec --- /dev/null +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java @@ -0,0 +1,37 @@ +package com.lishid.openinv.internal.common.container.slot.placeholder; + +import com.lishid.openinv.internal.common.player.OpenPlayer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.GameType; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; + +public final class Placeholders { + + static final @NotNull EnumMap BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class); + public static @NotNull ItemStack craftingOutput = ItemStack.EMPTY; + public static @NotNull ItemStack cursor = ItemStack.EMPTY; + public static @NotNull ItemStack drop = ItemStack.EMPTY; + public static @NotNull ItemStack emptyHelmet = ItemStack.EMPTY; + public static @NotNull ItemStack emptyChestplate = ItemStack.EMPTY; + public static @NotNull ItemStack emptyLeggings = ItemStack.EMPTY; + public static @NotNull ItemStack emptyBoots = ItemStack.EMPTY; + public static @NotNull ItemStack emptyOffHand = ItemStack.EMPTY; + public static @NotNull ItemStack notSlot = ItemStack.EMPTY; + public static @NotNull ItemStack blockedOffline = ItemStack.EMPTY; + + public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { + if (!OpenPlayer.isConnected(serverPlayer.connection)) { + return blockedOffline; + } + + return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY); + } + + private Placeholders() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + +} diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index c1e31815..fbaddb4a 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -49,7 +49,7 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager } } - private final @NotNull Logger logger; + protected final @NotNull Logger logger; private @Nullable Field bukkitEntity; public PlayerManager(@NotNull Logger logger) { @@ -107,7 +107,7 @@ public PlayerManager(@NotNull Logger logger) { return null; } - private @NotNull ServerPlayer createNewPlayer( + protected @NotNull ServerPlayer createNewPlayer( @NotNull MinecraftServer server, @NotNull ServerLevel worldServer, @NotNull final OfflinePlayer offline) { @@ -200,7 +200,7 @@ private void spawnInDefaultWorld(ServerPlayer player) { } } - private void injectPlayer(ServerPlayer player) throws IllegalAccessException { + protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; } @@ -259,7 +259,8 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { // resulting in a title change but no other state modifications (like cursor position). menu.setTitle(title); - menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); + var pair = CraftEventFactory.callInventoryOpenEventWithTitle(player, menu); + menu = pair.getSecond(); // Menu is null if event is cancelled. if (menu == null) { diff --git a/internal/paper1_21_1/build.gradle.kts b/internal/paper1_21_1/build.gradle.kts new file mode 100644 index 00000000..6a61a347 --- /dev/null +++ b/internal/paper1_21_1/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { + id -> id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_3")) + + paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") +} diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java new file mode 100644 index 00000000..078f50fc --- /dev/null +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java @@ -0,0 +1,80 @@ +package com.lishid.openinv.internal.paper1_21_1; + +import com.lishid.openinv.internal.Accessor; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.common.container.AnySilentContainer; +import com.lishid.openinv.internal.common.container.OpenEnderChest; +import com.lishid.openinv.internal.paper1_21_1.container.OpenInventory; +import com.lishid.openinv.internal.paper1_21_1.container.slot.placeholder.PlaceholderLoader; +import com.lishid.openinv.internal.paper1_21_1.player.PlayerManager; +import com.lishid.openinv.util.lang.LanguageManager; +import net.minecraft.world.Container; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InternalAccessor implements Accessor { + + private final @NotNull Logger logger; + private final @NotNull PlayerManager manager; + private final @NotNull AnySilentContainer anySilentContainer; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.manager = new PlayerManager(logger); + this.anySilentContainer = new AnySilentContainer(logger, lang); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { + return anySilentContainer; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new OpenInventory(player); + } + + @Override + public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { + return new OpenEnderChest(player); + } + + @Override + public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { + if (!(bukkitInventory instanceof CraftInventory craftInventory)) { + return null; + } + Container container = craftInventory.getInventory(); + if (clazz.isInstance(container)) { + return clazz.cast(container); + } + return null; + } + + @Override + public void reload(@NotNull ConfigurationSection config) { + ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); + try { + // Reset placeholders to defaults and try to load configuration. + new PlaceholderLoader().load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + +} diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java new file mode 100644 index 00000000..c1fff7d6 --- /dev/null +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java @@ -0,0 +1,20 @@ +package com.lishid.openinv.internal.paper1_21_1.container; + +import com.lishid.openinv.internal.common.container.slot.Content; +import com.lishid.openinv.internal.paper1_21_1.container.slot.ContentCraftingResult; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory { + + public OpenInventory(@NotNull Player bukkitPlayer) { + super(bukkitPlayer); + } + + @Override + protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) { + return new ContentCraftingResult(serverPlayer); + } + +} diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/ContentCraftingResult.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/ContentCraftingResult.java new file mode 100644 index 00000000..d0d74f74 --- /dev/null +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/ContentCraftingResult.java @@ -0,0 +1,46 @@ +package com.lishid.openinv.internal.paper1_21_1.container.slot; + +import com.lishid.openinv.internal.common.container.slot.ContentCrafting; +import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +public class ContentCraftingResult extends ContentViewOnly { + + public ContentCraftingResult(@NotNull ServerPlayer holder) { + super(holder); + } + + @Override + public ItemStack get() { + InventoryMenu inventoryMenu = holder.inventoryMenu; + return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + if (!ContentCrafting.isAvailable(holder)) { + return Placeholders.survivalOnly(holder); + } + InventoryMenu inventoryMenu = holder.inventoryMenu; + return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem(); + } + }; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.RESULT; + } + +} diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java new file mode 100644 index 00000000..604a358b --- /dev/null +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java @@ -0,0 +1,65 @@ +package com.lishid.openinv.internal.paper1_21_1.container.slot.placeholder; + +import com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder.NumericDataPlaceholderLoader; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.util.Unit; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.entity.BannerPattern; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.entity.BannerPatterns; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class PlaceholderLoader extends NumericDataPlaceholderLoader { + + @Override + protected @NotNull ItemStack defaultCursor() { + // Cursor-like banner with no tooltip + ItemStack itemStack = new ItemStack(Items.WHITE_BANNER); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT); + BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT); + BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER); + itemStack.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); + addModelData(itemStack); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + return itemStack; + } + + @Override + protected @NotNull ItemStack defaultShield() { + // Shield with "missing texture" pattern, magenta and black squares. + ItemStack itemStack = new ItemStack(Items.SHIELD); + itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA); + RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry(); + Registry bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN); + BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL); + BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT); + BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT); + BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT); + BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT); + itemStack.set(DataComponents.BANNER_PATTERNS, + new BannerPatternLayers(List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + addModelData(itemStack); + return itemStack; + } + +} diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java new file mode 100644 index 00000000..3ad69f3b --- /dev/null +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java @@ -0,0 +1,54 @@ +package com.lishid.openinv.internal.paper1_21_1.player; + +import com.mojang.authlib.GameProfile; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.ChatVisiblity; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Logger; + +public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { + public PlayerManager(@NotNull Logger logger) { + super(logger); + } + + @Override + protected @NotNull ServerPlayer createNewPlayer( + @NotNull MinecraftServer server, + @NotNull ServerLevel worldServer, + @NotNull final OfflinePlayer offline) { + // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) + // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) + GameProfile profile = new GameProfile(offline.getUniqueId(), + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + + ClientInformation dummyInfo = new ClientInformation( + "en_us", + 1, // Reduce distance just in case. + ChatVisiblity.HIDDEN, // Don't accept chat. + false, + ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION, + ServerPlayer.DEFAULT_MAIN_HAND, + true, + false // Don't list in player list (not that this player is in the list anyway). + ); + + ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); + + try { + injectPlayer(entity); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + } + + return entity; + } + +} diff --git a/internal/paper1_21_3/build.gradle.kts b/internal/paper1_21_3/build.gradle.kts new file mode 100644 index 00000000..b1a13522 --- /dev/null +++ b/internal/paper1_21_3/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { + id -> id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + + paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT") +} diff --git a/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java new file mode 100644 index 00000000..6ec24db0 --- /dev/null +++ b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java @@ -0,0 +1,28 @@ +package com.lishid.openinv.internal.paper1_21_3; + +import com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder.NumericDataPlaceholderLoader; +import com.lishid.openinv.util.lang.LanguageManager; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor { + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + super(logger, lang); + } + + @Override + public void reload(@NotNull ConfigurationSection config) { + ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); + try { + // Reset placeholders to defaults and try to load configuration. + new NumericDataPlaceholderLoader().load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + +} diff --git a/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java new file mode 100644 index 00000000..c77fd676 --- /dev/null +++ b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java @@ -0,0 +1,31 @@ +package com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder; + +import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoaderBase; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.util.Unit; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.CustomModelData; +import net.minecraft.world.level.block.entity.BannerPattern; +import net.minecraft.world.level.block.entity.BannerPatternLayers; +import net.minecraft.world.level.block.entity.BannerPatterns; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class NumericDataPlaceholderLoader extends PlaceholderLoaderBase { + + private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); + + @Override + protected void addModelData(ItemStack itemStack) { + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + } + +} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 713660c4..46a72d48 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -11,6 +11,8 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_3")) + implementation(project(":openinvadapterpaper1_21_1")) implementation(project(":openinvadapterspigot", configuration = "shadow")) implementation(libs.planarwrappers) } diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 3b29cfea..68c42e0b 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -35,9 +35,9 @@ public class InternalAccessor { - private @Nullable Accessor internal; + private static final boolean PAPER; - public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + static { boolean paper = false; try { Class.forName("io.papermc.paper.configuration.GlobalConfiguration"); @@ -45,19 +45,15 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } catch (ClassNotFoundException ignored) { // Expect remapped server. } + PAPER = paper; + } + + private @Nullable Accessor internal; + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { try { - Version maxSupported = Version.of(1, 21, 4); - // Placeholders currently mandate 1.21.4 minimum. - Version minSupported = Version.of(1, 21, 4); - if (!paper) { - if (BukkitVersions.MINECRAFT.equals(maxSupported)) { - internal = new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); - } - } else if (BukkitVersions.MINECRAFT.greaterThanOrEqual(minSupported) - && BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported)) { - internal = new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); - } + internal = getAccessor(logger, lang); + if (internal != null) { InventoryAccess.setProvider(internal::get); } @@ -67,6 +63,38 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } } + private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + Version maxSupported = Version.of(1, 21, 4); + Version minSupported = Version.of(1, 21, 1); + + // Ensure version is in supported range. + if (BukkitVersions.MINECRAFT.greaterThan(maxSupported) || BukkitVersions.MINECRAFT.lessThan(minSupported)) { + return null; + } + + // Load Spigot accessor. + if (!PAPER) { + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { + // Current Spigot, remapped internals are available. + return new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); + } else { + // Older Spigot; unsupported. + return null; + } + } + + // Paper or a Paper fork, can use Mojang-mapped internals. + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.4 + return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); + } else if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21, 3))) { + // 1.21.1-1.21.2 placeholder format + return new com.lishid.openinv.internal.paper1_21_1.InternalAccessor(logger, lang); + } + + // 1.21.3 + return new com.lishid.openinv.internal.paper1_21_3.InternalAccessor(logger, lang); + } + public String getReleasesLink() { if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck. return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated"; @@ -134,6 +162,9 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 return "Unsupported; upgrade to 1.20.6: https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; } + if (PAPER && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 1))) { // Paper 1.21.1-1.21.3 + return "https://github.com/Jikoo/OpenInv/releases"; + } if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21 return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; } diff --git a/settings.gradle.kts b/settings.gradle.kts index 98321d8c..d6fdd75a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,6 +18,8 @@ project(":openinvcommon").projectDir = file("common") val internals = listOf( "common", + "paper1_21_3", + "paper1_21_1", "spigot" ) for (internal in internals) { From 4de2c2fe6d981d13055facba657b6ad6f4c6c9ec Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 13 Feb 2025 11:08:59 -0500 Subject: [PATCH 264/340] Code health (#288) * Fix some missing nullity/deprecation annotations * Remove unnecessary Paper checks * Respect title changes from InventoryOpenEvent --- internal/common/build.gradle.kts | 10 ++++ .../container/bukkit/OpenDummyInventory.java | 4 +- .../bukkit/OpenDummyPlayerInventory.java | 2 + .../container/bukkit/OpenPlayerInventory.java | 2 + .../internal/common/player/PlayerManager.java | 25 +++------ .../internal/reobf/player/PlayerManager.java | 56 ------------------- 6 files changed, 25 insertions(+), 74 deletions(-) diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 936fe966..565c251d 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -5,6 +5,16 @@ plugins { alias(libs.plugins.paperweight) } +//tasks { +// withType { +// // OpenPlayer unchecked warning is due to superclass' messy inheritance and legacy methods. +// options.compilerArgs.add("-Xlint:unchecked") +// // PlayerManager uses "deprecated" method matching vanilla to support legacy save data. +// // While vanilla still feels that it is appropriate to use in the load process, we will too. +// options.compilerArgs.add("-Xlint:deprecation") +// } +//} + configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val paper = candidates.firstOrNull { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java index 840bbaae..11275830 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java @@ -62,7 +62,7 @@ public void setItem(int index, @Nullable ItemStack item) { } @Override - public ItemStack[] getContents() { + public ItemStack @NotNull [] getContents() { return new ItemStack[getSize()]; } @@ -72,7 +72,7 @@ public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentExcept } @Override - public @NotNull ItemStack[] getStorageContents() { + public @NotNull ItemStack @NotNull [] getStorageContents() { return new ItemStack[getSize()]; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java index 0347e135..a98aca1f 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java @@ -111,11 +111,13 @@ public void setItemInOffHand(@Nullable ItemStack item) { } + @Deprecated @Override public @NotNull ItemStack getItemInHand() { return new ItemStack(Material.AIR); } + @Deprecated @Override public void setItemInHand(@Nullable ItemStack stack) { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java index a5a6339d..434e0ca3 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java @@ -170,11 +170,13 @@ public void setItemInOffHand(@Nullable ItemStack item) { getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); } + @Deprecated @Override public @NotNull ItemStack getItemInHand() { return getItemInMainHand(); } + @Deprecated @Override public void setItemInHand(@Nullable ItemStack stack) { setItemInMainHand(stack); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index fbaddb4a..9ea2063c 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -5,6 +5,7 @@ import com.lishid.openinv.internal.common.container.OpenInventory; import com.mojang.authlib.GameProfile; import com.mojang.serialization.Dynamic; +import io.papermc.paper.adventure.PaperAdventure; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; @@ -38,17 +39,6 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { - private static boolean paper; - - static { - try { - Class.forName("io.papermc.paper.configuration.Configuration"); - paper = true; - } catch (ClassNotFoundException ignored) { - paper = false; - } - } - protected final @NotNull Logger logger; private @Nullable Field bukkitEntity; @@ -158,10 +148,8 @@ boolean loadData(@NotNull ServerPlayer player) { // Game type settings are also loaded separately. player.loadGameTypes(loadedData); - if (paper) { - // Paper: world is not loaded by ServerPlayer#load(CompoundTag). - parseWorld(player, loadedData); - } + // World is not loaded by ServerPlayer#load(CompoundTag) on Paper. + parseWorld(player, loadedData); return true; } @@ -267,8 +255,13 @@ protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { return null; } + var newTitle = pair.getFirst(); + if (newTitle != null) { + title = PaperAdventure.asVanilla(newTitle); + } + player.containerMenu = menu; - player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); + player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), title)); player.initMenu(menu); return menu.getBukkitView(); diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index ea3504fc..38deed4a 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -4,9 +4,7 @@ import com.lishid.openinv.internal.reobf.container.OpenEnderChest; import com.lishid.openinv.internal.reobf.container.OpenInventory; import com.mojang.authlib.GameProfile; -import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -18,13 +16,10 @@ import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R3.CraftServer; -import org.bukkit.craftbukkit.v1_21_R3.CraftWorld; import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_21_R3.event.CraftEventFactory; import org.bukkit.entity.Player; @@ -33,22 +28,10 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; -import java.util.UUID; import java.util.logging.Logger; public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { - private static boolean paper; - - static { - try { - Class.forName("io.papermc.paper.configuration.Configuration"); - paper = true; - } catch (ClassNotFoundException ignored) { - paper = false; - } - } - private final @NotNull Logger logger; private @Nullable Field bukkitEntity; @@ -158,48 +141,9 @@ boolean loadData(@NotNull ServerPlayer player) { // Game type settings are also loaded separately. player.loadGameTypes(loadedData); - if (paper) { - // Paper: world is not loaded by ServerPlayer#load(CompoundTag). - parseWorld(player, loadedData); - } - return true; } - private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { - // See PlayerList#placeNewPlayer - World bukkitWorld; - if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { - // Modern Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); - } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { - // Legacy Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); - } else { - // Vanilla player data. - DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(logger::warning) - .map(player.server::getLevel) - // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); - return; - } - if (bukkitWorld == null) { - spawnInDefaultWorld(player); - return; - } - player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); - } - - private void spawnInDefaultWorld(ServerPlayer player) { - ServerLevel level = player.server.getLevel(Level.OVERWORLD); - if (level != null) { - player.spawnIn(level); - } else { - logger.warning("Tried to load player with invalid world when no fallback was available!"); - } - } - private void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; From 48a61e6043fc4c554ca2f9aa963cd73a1170971f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 13 Feb 2025 11:48:47 -0500 Subject: [PATCH 265/340] Update release script Now I can do things like have unstaged changes on the master branch when I cut a release! Not good! --- gradle.properties | 6 +++--- scripts/tag_release.sh | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle.properties b/gradle.properties index cdd4e893..2863c3a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Project meta -group = com.lishid.openinv -version = 5.1.8-SNAPSHOT -description = A Bukkit plugin for opening normally-inaccessible inventories. +group=com.lishid.openinv +version=5.1.8-SNAPSHOT +description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration org.gradle.parallel=true diff --git a/scripts/tag_release.sh b/scripts/tag_release.sh index 2e4d022a..341d3e2a 100755 --- a/scripts/tag_release.sh +++ b/scripts/tag_release.sh @@ -23,15 +23,15 @@ fi version="$1" snapshot="${version%.*}.$((${version##*.} + 1))-SNAPSHOT" -mvn versions:set -DnewVersion="$version" +sed -i s/version=.*/version="$version"/ gradle.properties -git add . +git add gradle.properties git commit -S -m "Bump version to $version for release" git tag -s "$version" -m "Release $version" -mvn clean package -am -P all +./gradlew build -mvn versions:set -DnewVersion="$snapshot" +sed -i s/version=.*/version="$snapshot"/ gradle.properties -git add . +git add gradle.properties git commit -S -m "Bump version to $snapshot for development" From 1a76e6fd5761ec53dee767e1eff5706d701a61ad Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 13 Feb 2025 11:48:59 -0500 Subject: [PATCH 266/340] Bump version to 5.1.8 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2863c3a6..6a18bb74 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.8-SNAPSHOT +version=5.1.8 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 1c1dc9505b32878d11090ff0f781a6fb9bfaf754 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 13 Feb 2025 11:49:04 -0500 Subject: [PATCH 267/340] Bump version to 5.1.9-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6a18bb74..b14c47a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.8 +version=5.1.9-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From cd73430bb207f63b18e73859ac28b0dfa58eb75e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 13 Feb 2025 12:12:25 -0500 Subject: [PATCH 268/340] Try to resolve remote cache issues The weird part is that I don't have any of these problems locally. Remote is executing tasks in a really weird order, common adapter is building after Spigot adapter despite Spigot adapter depending on an output from it. May just have to disable caching, but would like to avoid that if possible. --- .github/workflows/ci.yml | 2 +- internal/spigot/build.gradle.kts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7310eed..ac3547ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - uses: gradle/actions/setup-gradle@v4 - name: Build with Gradle - run: ./gradlew build + run: ./gradlew clean build # Upload artifacts - name: Upload Distributable Jar diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 18e39ec3..6a88b17e 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { } tasks.shadowJar { + notCompatibleWithConfigurationCache("reobf task replaces output artifact") // TODO use an output of reobf relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf") } From e834e940e21677d6cd79f421404e63e8659911e5 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 14 Feb 2025 01:02:24 -0500 Subject: [PATCH 269/340] Convert to precompiled task (#289) --- buildSrc/src/main/kotlin/ReobfTask.kt | 60 +++++++++++++++++++ .../src/main/kotlin/remap-spigot.gradle.kts | 55 ----------------- internal/spigot/build.gradle.kts | 29 ++++++--- plugin/build.gradle.kts | 3 +- 4 files changed, 83 insertions(+), 64 deletions(-) create mode 100644 buildSrc/src/main/kotlin/ReobfTask.kt delete mode 100644 buildSrc/src/main/kotlin/remap-spigot.gradle.kts diff --git a/buildSrc/src/main/kotlin/ReobfTask.kt b/buildSrc/src/main/kotlin/ReobfTask.kt new file mode 100644 index 00000000..f168915f --- /dev/null +++ b/buildSrc/src/main/kotlin/ReobfTask.kt @@ -0,0 +1,60 @@ +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.Jar +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths + +abstract class ReobfTask: Jar() { + + @get:Input + open val spigotVersion: Property = objectFactory.property(String::class.java) + + @get:InputFile + open val inputFile: Property = objectFactory.fileProperty() + + @get:Input + open val intermediaryClassifier: Property = objectFactory.property(String::class.java).convention("mojang-mapped") + + init { + archiveClassifier.convention("reobf") + } + + @TaskAction + override fun copy() { + val spigotVer = spigotVersion.get() + val inFile = inputFile.get().asFile + val obfPath = inFile.resolveSibling(inFile.name.replace(".jar", "-${intermediaryClassifier.get()}.jar")) + + // https://www.spigotmc.org/threads/510208/#post-4184317 + val specialsource = project.configurations.named("spigotRemap").get().incoming.artifacts.artifacts + .first { it.id.componentIdentifier.toString() == "net.md-5:SpecialSource:1.11.4" }.file.path + val repo = Paths.get(project.repositories.mavenLocal().url) + val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVer/") + val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVer/") + + // Remap original Mojang-mapped jar to obfuscated intermediary + val mojangServer = spigotDir.resolve("spigot-$spigotVer-remapped-mojang.jar") + val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-mojang.txt") + remapPartial(specialsource, mojangServer, mojangMappings, inFile, obfPath, true) + + // Remap obfuscated intermediary jar to Spigot and replace original + val obfServer = spigotDir.resolve("spigot-$spigotVer-remapped-obf.jar") + val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-spigot.csrg") + remapPartial(specialsource, obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false) + } + + private fun remapPartial(specialSource: String, serverJar: Path, mapping: Path, input: File, output: File, reverse: Boolean) { + project.providers.exec { + commandLine("java", "-cp", "$specialSource${File.pathSeparator}$serverJar", + "net.md_5.specialsource.SpecialSource", "--live", + "-i", input.path, "-o", output.path, + "-m", "$mapping", + if (reverse) "--reverse" else "") + }.result.get() + } + +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/remap-spigot.gradle.kts b/buildSrc/src/main/kotlin/remap-spigot.gradle.kts deleted file mode 100644 index a1a6fab9..00000000 --- a/buildSrc/src/main/kotlin/remap-spigot.gradle.kts +++ /dev/null @@ -1,55 +0,0 @@ -import java.nio.file.Path -import java.nio.file.Paths - -val spigotRemap = configurations.create("spigotRemap") - -repositories { - mavenCentral() -} - -dependencies { - spigotRemap("net.md-5:SpecialSource:1.11.4:shaded") -} - -abstract class RemapTask - @Inject constructor(private val project: Project): DefaultTask() { - - @get:Input - abstract var spigotVersion: String - - @TaskAction - fun remapInputs() { - inputs.files.forEach { - remap(spigotVersion, it.toPath(), it.toPath().resolveSibling(it.name.replace(".jar", "-obf.jar"))) - } - } - - private fun remap(spigotVersion: String, jarPath: Path, obfPath: Path) { - // https://www.spigotmc.org/threads/510208/#post-4184317 - val specialsource = project.configurations.named("spigotRemap").get().incoming.artifacts.artifacts - .first { it.id.componentIdentifier.toString() == "net.md-5:SpecialSource:1.11.4" }.file.path - val repo = Paths.get(project.repositories.mavenLocal().url) - val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVersion/") - val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVersion/") - - // Remap original Mojang-mapped jar to obfuscated intermediary - val mojangServer = spigotDir.resolve("spigot-$spigotVersion-remapped-mojang.jar") - val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVersion-maps-mojang.txt") - remapPartial(specialsource, mojangServer, mojangMappings, jarPath, obfPath, true) - - // Remap obfuscated intermediary jar to Spigot and replace original - val obfServer = spigotDir.resolve("spigot-$spigotVersion-remapped-obf.jar") - val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVersion-maps-spigot.csrg") - remapPartial(specialsource, obfServer, spigotMappings, obfPath, jarPath, false) - } - - private fun remapPartial(specialSource: String, serverJar: Path, mapping: Path, input: Path, output: Path, reverse: Boolean) { - project.providers.exec { - commandLine("java", "-cp", "$specialSource${File.pathSeparator}$serverJar", - "net.md_5.specialsource.SpecialSource", "--live", - "-i", "$input", "-o", "$output", - "-m", "$mapping", - if (reverse) "--reverse" else "") - }.result.get() - } -} diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 6a88b17e..1d7dc411 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - `remap-spigot` `openinv-base` alias(libs.plugins.shadow) } @@ -9,6 +8,7 @@ repositories { } val spigotVer = "1.21.4-R0.1-SNAPSHOT" +rootProject.extra["spigotVersion"] = spigotVer rootProject.extra["craftbukkitPackage"] = "v1_21_R3" configurations.all { @@ -25,7 +25,10 @@ configurations.all { } } +val spigotRemap = configurations.create("spigotRemap") + dependencies { + spigotRemap("net.md-5:SpecialSource:1.11.4:shaded") compileOnly(libs.spigotapi) compileOnly(create("org.spigotmc", "spigot", spigotVer, classifier = "remapped-mojang")) @@ -37,15 +40,27 @@ dependencies { } tasks.shadowJar { - notCompatibleWithConfigurationCache("reobf task replaces output artifact") // TODO use an output of reobf relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf") } -// TODO this appears to be a deprecated way to do things -// may want to just move all helper methods here. -tasks.register("reobf") { +val reobfTask = tasks.register("reobfTask") { notCompatibleWithConfigurationCache("gradle is hard") dependsOn(tasks.shadowJar) - inputs.files(tasks.shadowJar.get().outputs.files.files) - spigotVersion = spigotVer + inputFile.value(tasks.shadowJar.get().archiveFile.get()) + spigotVersion.value(spigotVer) +} + +configurations { + consumable("reobf") { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) + } + } +} + +artifacts { + add("reobf", reobfTask) } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 46a72d48..8269af66 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -13,7 +13,7 @@ dependencies { implementation(project(":openinvadaptercommon")) implementation(project(":openinvadapterpaper1_21_3")) implementation(project(":openinvadapterpaper1_21_1")) - implementation(project(":openinvadapterspigot", configuration = "shadow")) + implementation(project(":openinvadapterspigot", configuration = "reobf")) implementation(libs.planarwrappers) } @@ -26,7 +26,6 @@ tasks.jar { } tasks.shadowJar { - dependsOn(":openinvadapterspigot:reobf") minimize { exclude(":openinv**") } From d564773be62fd988028a52a2f9fdbd24fe4d1f71 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 14 Feb 2025 11:33:31 -0500 Subject: [PATCH 270/340] Bump version to 5.1.9 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b14c47a4..1a0a00fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.9-SNAPSHOT +version=5.1.9 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 3b907e1b3738ebd2038e814c72e23122e93cc0ac Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 14 Feb 2025 11:33:47 -0500 Subject: [PATCH 271/340] Bump version to 5.1.10-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1a0a00fe..39725644 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.9 +version=5.1.10-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 0db487c6eb677202ee1e1e05495c2e2f92224573 Mon Sep 17 00:00:00 2001 From: "Elmar (Cody) Blume" <48647037+xCodiq@users.noreply.github.com> Date: Wed, 19 Feb 2025 04:26:34 +0100 Subject: [PATCH 272/340] Add Folia support (#292) * feat(folia): added folia support * chore(folia): apply folia-supported: true label --------- Co-authored-by: R00tB33rMan --- gradle/libs.versions.toml | 2 ++ plugin/build.gradle.kts | 3 +++ plugin/src/main/java/com/lishid/openinv/OpenInv.java | 3 ++- .../com/lishid/openinv/command/OpenInvCommand.java | 6 +++--- .../com/lishid/openinv/util/InventoryManager.java | 7 ++++--- .../java/com/lishid/openinv/util/PlayerLoader.java | 12 +++++++----- plugin/src/main/resources/plugin.yml | 1 + 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e4409e54..ab1c5dd3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,12 +5,14 @@ planarwrappers = "3.3.0" annotations = "26.0.2" paperweight = "2.0.0-beta.14" shadow = "8.3.6" +folia = "v0.0.3" [libraries] spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } specialsource = { module = "net.md-5:SpecialSource", version.ref = "specialsource" } planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" } annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } +folia = { module = "com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper", version.ref = "folia" } [plugins] paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 8269af66..3279b096 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(project(":openinvadapterpaper1_21_1")) implementation(project(":openinvadapterspigot", configuration = "reobf")) implementation(libs.planarwrappers) + implementation(libs.folia) } tasks.processResources { @@ -26,8 +27,10 @@ tasks.jar { } tasks.shadowJar { + relocate("me.nahu.scheduler.wrapper", "com.lishid.openinv.internal.folia.scheduler") minimize { exclude(":openinv**") + exclude(dependency("com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper:.*")) } } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index c3ee3250..a06b2492 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -38,6 +38,7 @@ import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.setting.PlayerToggle; import com.lishid.openinv.util.setting.PlayerToggles; +import me.nahu.scheduler.wrapper.FoliaWrappedJavaPlugin; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -59,7 +60,7 @@ /** * The main class for OpenInv. */ -public class OpenInv extends JavaPlugin implements IOpenInv { +public class OpenInv extends FoliaWrappedJavaPlugin implements IOpenInv { private InternalAccessor accessor; private Config config; diff --git a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index b45b9717..b5f4e4b0 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -26,13 +26,13 @@ import com.lishid.openinv.util.config.Config; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import me.nahu.scheduler.wrapper.runnable.WrappedRunnable; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -99,7 +99,7 @@ public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Com name = args[0]; } - new BukkitRunnable() { + new WrappedRunnable() { @Override public void run() { final OfflinePlayer offlinePlayer = playerLoader.match(name); @@ -109,7 +109,7 @@ public void run() { return; } - new BukkitRunnable() { + new WrappedRunnable() { @Override public void run() { if (!player.isOnline()) { diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index 9cbc82c0..c3b0c050 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -3,6 +3,7 @@ import com.github.jikoo.planarwrappers.util.version.BukkitVersions; import com.github.jikoo.planarwrappers.util.version.Version; import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.event.OpenEvents; import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; @@ -44,11 +45,11 @@ public class InventoryManager implements Listener { private final Map inventories = new ConcurrentHashMap<>(); private final Map enderChests = new ConcurrentHashMap<>(); private final Set expectedCloses = new HashSet<>(); - private final @NotNull Plugin plugin; + private final @NotNull OpenInv plugin; private final @NotNull Config config; private final @NotNull InternalAccessor accessor; - public InventoryManager(@NotNull Plugin plugin, @NotNull Config config, @NotNull InternalAccessor accessor) { + public InventoryManager(@NotNull OpenInv plugin, @NotNull Config config, @NotNull InternalAccessor accessor) { this.plugin = plugin; this.config = config; this.accessor = accessor; @@ -164,7 +165,7 @@ private void onInventoryClose(@NotNull InventoryCloseEvent event) { } // Schedule task to check in use status later this tick. Closing user is still in viewer list. - plugin.getServer().getScheduler().runTask(plugin, () -> { + plugin.getScheduler().runTask(() -> { if (loaded.isInUse()) { return; } diff --git a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java index 4ecf788d..0d30e200 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java +++ b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java @@ -3,6 +3,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.errorprone.annotations.Keep; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.config.Config; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -18,6 +19,7 @@ import java.util.Iterator; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; @@ -28,7 +30,7 @@ */ public class PlayerLoader implements Listener { - private final @NotNull Plugin plugin; + private final @NotNull OpenInv plugin; private final @NotNull Config config; private final @NotNull InventoryManager inventoryManager; private final @NotNull InternalAccessor internalAccessor; @@ -36,7 +38,7 @@ public class PlayerLoader implements Listener { private final @NotNull Cache lookupCache; public PlayerLoader( - @NotNull Plugin plugin, + @NotNull OpenInv plugin, @NotNull Config config, @NotNull InventoryManager inventoryManager, @NotNull InternalAccessor internalAccessor, @@ -78,8 +80,8 @@ public PlayerLoader( return internalAccessor.getPlayerDataManager().loadPlayer(offline); } - Future future = Bukkit.getScheduler().callSyncMethod(plugin, - () -> internalAccessor.getPlayerDataManager().loadPlayer(offline)); + CompletableFuture future = new CompletableFuture<>(); + plugin.getScheduler().runTask(() -> future.complete(internalAccessor.getPlayerDataManager().loadPlayer(offline))); try { player = future.get(); @@ -195,7 +197,7 @@ private void updateMatches(@NotNull PlayerJoinEvent event) { return; } - plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, () -> { + plugin.getScheduler().runTaskLaterAsynchronously(() -> { Iterator> iterator = lookupCache.asMap().entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 79a5c9e5..dbc8ba69 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -5,6 +5,7 @@ author: lishid authors: [Jikoo, ShadowRanger] description: Open a player's inventory as a chest and interact with it in real time. api-version: "1.13" +folia-supported: true permissions: From 414b141b37652ea58d9a42b08ccfee50d093c0ea Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 2 Mar 2025 10:55:00 -0500 Subject: [PATCH 273/340] Improve Gradle build (#290) * Fix warnings about task not being cacheable * Add plugin to install Spigot * Explicitly cache BuildTools products * Move reobfuscation to a plugin * Update compilation instructions --- .github/workflows/ci.yml | 14 ++- .github/workflows/draft_release.yml | 8 +- README.MD | 9 +- buildSrc/src/main/kotlin/ReobfTask.kt | 60 ----------- .../jikoo/openinv/BuildToolsValueSource.kt | 102 ++++++++++++++++++ .../openinv/SpigotDependencyExtension.kt | 21 ++++ .../com/github/jikoo/openinv/SpigotReobf.kt | 66 ++++++++++++ .../github/jikoo/openinv/SpigotReobfTask.kt | 82 ++++++++++++++ .../com/github/jikoo/openinv/SpigotSetup.kt | 59 ++++++++++ internal/spigot/build.gradle.kts | 38 ++----- plugin/build.gradle.kts | 4 +- 11 files changed, 358 insertions(+), 105 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/ReobfTask.kt create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotDependencyExtension.kt create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac3547ce..ad9331d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,9 +22,17 @@ jobs: distribution: 'temurin' java-version: '21' - # TODO convert to a Gradle plugin later for portability - - name: Install Spigot dependency - run: ./scripts/install_spigot.sh 1.21.4 + # We can't use 'maven' prebuilt cache setup because it requires that the project have a pom file. + # BuildTools installs to Maven local if available, so it's easier to just rely on that. + - name: Cache Spigot dependency + uses: actions/cache@v4 + with: + path: | + ~/.m2/repository/org/spigotmc/spigot + ~/.m2/repository/org/spigotmc/minecraft-server + key: ${{ runner.os }}-buildtools-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-buildtools- - uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 5e982467..410bcff6 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -24,17 +24,15 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - name: ${{ env.VERSIONED_NAME }} + name: OpenInv ${{ env.VERSIONED_NAME }} # May actually want to manually specify Paper versions all the time; they won't all # need to be specified in code thanks to Mojang-mapped server internals. # A bit annoying, but not terribly so. # Instead, fetch min and max (which will also be Spigot version) and manually list intermediate versions? body: |- ## Supported server versions - ### Paper - TODO COMMA-SEPARATED VERSIONS - ### Spigot - TODO VERSION + **Paper:** TODO COMMA-SEPARATED VERSIONS + **Spigot:** TODO VERSION TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE. draft: true diff --git a/README.MD b/README.MD index 3126f273..ab3e2a0c 100644 --- a/README.MD +++ b/README.MD @@ -45,10 +45,7 @@ The OpenInv API is available via [JitPack](https://jitpack.io/). Note that since JitPack only builds the API now, the "full" OpenInv jar on JitPack is actually the openinvapi artifact. This is a change from previous dependency declaration that I hope to revert. ### Compilation -To compile, the relevant Spigot jar must be installed in the local repository. -As OpenInv is compiled against Mojang's mappings, you must run BuildTools with the `--remapped` argument: -`java -jar BuildTools.jar --remapped --rev $serverVersion` -`$serverVersion` is the version of the server, i.e. `1.21.4` - -Once Spigot is installed, execute the gradle wrapper: +Execute the gradle wrapper: `./gradlew build` + +If you encounter issues with building the Spigot module, try running BuildTools manually. diff --git a/buildSrc/src/main/kotlin/ReobfTask.kt b/buildSrc/src/main/kotlin/ReobfTask.kt deleted file mode 100644 index f168915f..00000000 --- a/buildSrc/src/main/kotlin/ReobfTask.kt +++ /dev/null @@ -1,60 +0,0 @@ -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.bundling.Jar -import java.io.File -import java.nio.file.Path -import java.nio.file.Paths - -abstract class ReobfTask: Jar() { - - @get:Input - open val spigotVersion: Property = objectFactory.property(String::class.java) - - @get:InputFile - open val inputFile: Property = objectFactory.fileProperty() - - @get:Input - open val intermediaryClassifier: Property = objectFactory.property(String::class.java).convention("mojang-mapped") - - init { - archiveClassifier.convention("reobf") - } - - @TaskAction - override fun copy() { - val spigotVer = spigotVersion.get() - val inFile = inputFile.get().asFile - val obfPath = inFile.resolveSibling(inFile.name.replace(".jar", "-${intermediaryClassifier.get()}.jar")) - - // https://www.spigotmc.org/threads/510208/#post-4184317 - val specialsource = project.configurations.named("spigotRemap").get().incoming.artifacts.artifacts - .first { it.id.componentIdentifier.toString() == "net.md-5:SpecialSource:1.11.4" }.file.path - val repo = Paths.get(project.repositories.mavenLocal().url) - val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVer/") - val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVer/") - - // Remap original Mojang-mapped jar to obfuscated intermediary - val mojangServer = spigotDir.resolve("spigot-$spigotVer-remapped-mojang.jar") - val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-mojang.txt") - remapPartial(specialsource, mojangServer, mojangMappings, inFile, obfPath, true) - - // Remap obfuscated intermediary jar to Spigot and replace original - val obfServer = spigotDir.resolve("spigot-$spigotVer-remapped-obf.jar") - val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-spigot.csrg") - remapPartial(specialsource, obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false) - } - - private fun remapPartial(specialSource: String, serverJar: Path, mapping: Path, input: File, output: File, reverse: Boolean) { - project.providers.exec { - commandLine("java", "-cp", "$specialSource${File.pathSeparator}$serverJar", - "net.md_5.specialsource.SpecialSource", "--live", - "-i", input.path, "-o", output.path, - "-m", "$mapping", - if (reverse) "--reverse" else "") - }.result.get() - } - -} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt new file mode 100644 index 00000000..bb73f8fb --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt @@ -0,0 +1,102 @@ +package com.github.jikoo.openinv + +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ValueSource +import org.gradle.api.provider.ValueSourceParameters +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.process.ExecOperations +import java.io.File +import java.net.URI +import java.nio.file.Files +import javax.inject.Inject + +abstract class BuildToolsValueSource: ValueSource { + + @get:Inject + abstract val exec: ExecOperations + + @get:Inject + abstract val javaToolchainService: JavaToolchainService + + interface Parameters: ValueSourceParameters { + val mavenLocal: Property + val workingDir: DirectoryProperty + + val spigotVersion: Property + val spigotRevision: Property + + val ignoreCached: Property + + val javaHome: DirectoryProperty + val javaExecutable: Property + } + + override fun obtain(): File { + val version = parameters.spigotVersion.get() + val revision = parameters.spigotRevision.get() + val installLocation = getInstallLocation(version) + // If Spigot is already installed, don't reinstall. + if (!parameters.ignoreCached.get() && installLocation.exists()) { + println("Skipping Spigot installation, $version is present") + return installLocation + } + + val buildTools = installBuildTools(parameters.workingDir.get().asFile) + + println("Installing Spigot $version (rev $revision)") + + exec.javaexec { + environment["JAVA_HOME"] = parameters.javaHome.get() + executable = parameters.javaExecutable.get() + workingDir = buildTools.parentFile + classpath(buildTools) + args = listOf("--nogui", "--rev", revision, "--remapped") + }.rethrowFailure() + + // Mark work for delete. + cleanUp(buildTools.parentFile) + + if (!installLocation.exists()) { + throw IllegalStateException( + "Failed to install Spigot $version from $revision. Does the revision point to a different version?") + } + return installLocation + } + + private fun getInstallLocation(version: String): File { + return parameters.mavenLocal.get().resolve("org/spigotmc/spigot/$version/spigot-$version-remapped-mojang.jar") + } + + private fun installBuildTools(workingDir: File): File { + val buildTools = workingDir.resolve("BuildTools.jar") + if (buildTools.exists()) { + return buildTools + } + + workingDir.mkdirs() + + val buildToolsUrl = "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" + println("Downloading $buildToolsUrl") + val stream = URI.create(buildToolsUrl).toURL().openStream() + Files.copy(stream, buildTools.toPath()) + stream.close() + + return buildTools + } + + private fun cleanUp(dir: File) { + dir.deleteOnExit() + if (!dir.isDirectory) { + return + } + + dir.listFiles()?.forEach { + if (it.isDirectory) { + cleanUp(it) + } else { + it.deleteOnExit() + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotDependencyExtension.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotDependencyExtension.kt new file mode 100644 index 00000000..cf0eb203 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotDependencyExtension.kt @@ -0,0 +1,21 @@ +package com.github.jikoo.openinv + +import org.gradle.api.model.ObjectFactory +import org.gradle.jvm.toolchain.JavaToolchainSpec + +abstract class SpigotDependencyExtension ( + objects: ObjectFactory +) { + + val version = objects.property(String::class.java) + val revision = objects.property(String::class.java) + .convention(version.map { + it.replace("-R\\d+\\.\\d+-SNAPSHOT".toRegex(), "") + }) + val configuration = objects.property(String::class.java) + val classifier = objects.property(String::class.java).convention("remapped-mojang") + val ext = objects.property(String::class.java) + val java = objects.property(JavaToolchainSpec::class.java) + val ignoreCached = objects.property(Boolean::class.java).convention(false) + +} diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt new file mode 100644 index 00000000..459423d8 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt @@ -0,0 +1,66 @@ +package com.github.jikoo.openinv + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.attributes.Bundling +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.Usage +import org.gradle.jvm.tasks.Jar +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register +import java.nio.file.Paths + +class SpigotReobf: Plugin { + + companion object { + internal const val DEP_CONFIG = "spigotReobfDep" + const val ARTIFACT_CONFIG = "reobf" + } + + override fun apply(target: Project) { + // Re-use extension from Spigot dependency declaration if available to reduce configuration requirements. + val spigotExt = target.dependencies.extensions.findByType(SpigotDependencyExtension::class.java) + ?: target.dependencies.extensions.create( + "spigot", + SpigotDependencyExtension::class.java, + target.objects + ) + + val mvnLocal = target.repositories.mavenLocal() + + val reobfTask = target.tasks.register("reobfTask") { + dependsOn(target.tasks.named("shadowJar")) + // ShadowJar extends Jar, so this should be a safe way to get the result without having + // to jump through hoops and shift around shadow declarations in the rest of the project. + inputFile.convention(target.tasks.named("shadowJar").get().archiveFile) + spigotVersion.convention(spigotExt.version) + getMavenLocal().set(Paths.get(mvnLocal.url).toFile()) + } + + // Create a separate configuration for SpecialSource to make it easier to locate. + val reobfDeps = target.configurations.create(DEP_CONFIG) + target.dependencies { + val libs = target.extensions.getByType(VersionCatalogsExtension::class.java).named("libs") + reobfDeps(variantOf(libs.findLibrary("specialsource").orElseThrow()) { classifier("shaded") }) + } + + // Set up configuration for producing reobf jar. + target.configurations.consumable(ARTIFACT_CONFIG) { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, target.objects.named(Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, target.objects.named(Usage.JAVA_RUNTIME)) + attribute(Bundling.BUNDLING_ATTRIBUTE, target.objects.named(Bundling.EXTERNAL)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, target.objects.named(LibraryElements.JAR)) + } + } + + // Add artifact from reobf task. + target.artifacts { + add(ARTIFACT_CONFIG, reobfTask) + } + } + +} diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt new file mode 100644 index 00000000..44166ce7 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt @@ -0,0 +1,82 @@ +package com.github.jikoo.openinv + +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.Jar +import org.gradle.process.internal.ExecActionFactory +import java.io.File +import javax.inject.Inject + +abstract class SpigotReobfTask @Inject constructor( + private var execActionFactory: ExecActionFactory +) : Jar() { + + @get:Input + val spigotVersion: Property = objectFactory.property(String::class.java) + + @get:InputFile + val inputFile: RegularFileProperty = objectFactory.fileProperty() + + @get:Input + val intermediaryClassifier: Property = objectFactory.property(String::class.java).convention("mojang-mapped") + + private val specialSource: Property = objectFactory.property(File::class.java).convention(project.provider { + // Grab SpecialSource location from dependency declaration. + project.configurations.named(SpigotReobf.DEP_CONFIG).get().incoming.artifacts.artifacts + .first { it.id.componentIdentifier.toString().startsWith("net.md-5:SpecialSource:") }.file + }) + + private val mavenLocal: Property = objectFactory.property(File::class.java) + + init { + archiveClassifier.convention(SpigotReobf.ARTIFACT_CONFIG) + } + + @TaskAction + override fun copy() { + val spigotVer = spigotVersion.get() + val inFile = inputFile.get().asFile + val obfPath = inFile.resolveSibling(inFile.name.replace(".jar", "-${intermediaryClassifier.get()}.jar")) + + // https://www.spigotmc.org/threads/510208/#post-4184317 + val specialSourceFile = specialSource.get() + val repo = mavenLocal.get() + val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVer/") + val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVer/") + + // Remap original Mojang-mapped jar to obfuscated intermediary + val mojangServer = spigotDir.resolve("spigot-$spigotVer-remapped-mojang.jar") + val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-mojang.txt") + remapPartial(specialSourceFile, mojangServer, mojangMappings, inFile, obfPath, true) + + // Remap obfuscated intermediary jar to Spigot and replace original + val obfServer = spigotDir.resolve("spigot-$spigotVer-remapped-obf.jar") + val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-spigot.csrg") + remapPartial(specialSourceFile, obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false) + } + + private fun remapPartial(specialSourceFile: File, serverJar: File, mapping: File, input: File, output: File, reverse: Boolean) { + // May need a direct dependency on SpecialSource later to customize behavior. + val exec = execActionFactory.newJavaExecAction() + exec.classpath(specialSourceFile, serverJar) + exec.mainClass.value("net.md_5.specialsource.SpecialSource") + exec.args( + "--live", + "-i", input.path, + "-o", output.path, + "-m", "$mapping", + if (reverse) "--reverse" else "" + ) + exec.execute().rethrowFailure() + } + + @Internal + internal fun getMavenLocal(): Property { + return mavenLocal + } + +} diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt new file mode 100644 index 00000000..30c81d3b --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt @@ -0,0 +1,59 @@ +package com.github.jikoo.openinv + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.kotlin.dsl.create +import java.nio.file.Paths +import javax.inject.Inject + +abstract class SpigotSetup: Plugin { + + @get:Inject + abstract val javaToolchainService: JavaToolchainService + + override fun apply(target: Project) { + target.plugins.apply("java") + + // Set up extension for configuring Spigot dependency. + val spigotExt = target.dependencies.extensions.findByType(SpigotDependencyExtension::class.java) + ?: target.dependencies.extensions.create( + "spigot", + SpigotDependencyExtension::class.java, + target.objects + ) + + val mvnLocal = target.repositories.mavenLocal() + + target.afterEvaluate { + // Get Java requirements, defaulting to version used for compilation. + spigotExt.java.convention(target.extensions.getByType(JavaPluginExtension::class.java).toolchain) + val launcher = javaToolchainService.launcherFor(spigotExt.java.get()).get() + + // Install Spigot with BuildTools. + target.providers.of(BuildToolsValueSource::class.java) { + parameters { + mavenLocal.set(Paths.get(mvnLocal.url).toFile()) + workingDir.set(target.layout.buildDirectory.dir("tmp/buildtools")) + spigotVersion.set(spigotExt.version) + spigotRevision.set(spigotExt.revision) + ignoreCached.set(spigotExt.ignoreCached) + javaHome.set(launcher.metadata.installationPath) + javaExecutable.set(launcher.executablePath.asFile.path) + } + }.get() + + // Add Spigot dependency. + val dependency = target.dependencies.create( + "org.spigotmc", + "spigot", + spigotExt.version.get(), + spigotExt.configuration.orNull, + spigotExt.classifier.orNull, + spigotExt.ext.orNull) + target.dependencies.add("compileOnly", dependency) + } + } + +} diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 1d7dc411..b531ee4b 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -1,14 +1,17 @@ +import com.github.jikoo.openinv.SpigotDependencyExtension +import com.github.jikoo.openinv.SpigotReobf +import com.github.jikoo.openinv.SpigotSetup + plugins { `openinv-base` alias(libs.plugins.shadow) } -repositories { - mavenLocal() -} +apply() +apply() val spigotVer = "1.21.4-R0.1-SNAPSHOT" -rootProject.extra["spigotVersion"] = spigotVer +// Used by common adapter to relocate Craftbukkit classes to a versioned package. rootProject.extra["craftbukkitPackage"] = "v1_21_R3" configurations.all { @@ -25,12 +28,9 @@ configurations.all { } } -val spigotRemap = configurations.create("spigotRemap") - dependencies { - spigotRemap("net.md-5:SpecialSource:1.11.4:shaded") compileOnly(libs.spigotapi) - compileOnly(create("org.spigotmc", "spigot", spigotVer, classifier = "remapped-mojang")) + extensions.getByType(SpigotDependencyExtension::class.java).version = spigotVer compileOnly(project(":openinvapi")) compileOnly(project(":openinvcommon")) @@ -42,25 +42,3 @@ dependencies { tasks.shadowJar { relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf") } - -val reobfTask = tasks.register("reobfTask") { - notCompatibleWithConfigurationCache("gradle is hard") - dependsOn(tasks.shadowJar) - inputFile.value(tasks.shadowJar.get().archiveFile.get()) - spigotVersion.value(spigotVer) -} - -configurations { - consumable("reobf") { - attributes { - attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) - attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) - attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) - attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) - } - } -} - -artifacts { - add("reobf", reobfTask) -} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 3279b096..73beed82 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,3 +1,5 @@ +import com.github.jikoo.openinv.SpigotReobf + plugins { `openinv-base` alias(libs.plugins.shadow) @@ -13,7 +15,7 @@ dependencies { implementation(project(":openinvadaptercommon")) implementation(project(":openinvadapterpaper1_21_3")) implementation(project(":openinvadapterpaper1_21_1")) - implementation(project(":openinvadapterspigot", configuration = "reobf")) + implementation(project(":openinvadapterspigot", configuration = SpigotReobf.ARTIFACT_CONFIG)) implementation(libs.planarwrappers) implementation(libs.folia) } From e44697f856dbca42ae8acc3de50bea8ef3910f86 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 7 Mar 2025 15:04:17 -0500 Subject: [PATCH 274/340] Parse CurseForge MC versions from release body (#293) --- .github/workflows/ci.yml | 3 +- .github/workflows/draft_release.yml | 6 +--- scripts/install_spigot.sh | 39 ---------------------- scripts/set_curseforge_env.sh | 51 +++++++++++++++++++---------- 4 files changed, 35 insertions(+), 64 deletions(-) delete mode 100755 scripts/install_spigot.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad9331d3..002311a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,7 @@ jobs: uses: actions/cache@v4 with: path: | - ~/.m2/repository/org/spigotmc/spigot - ~/.m2/repository/org/spigotmc/minecraft-server + ~/.m2/repository/org/spigotmc/ key: ${{ runner.os }}-buildtools-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-buildtools- diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 410bcff6..f8693b6b 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -25,13 +25,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: name: OpenInv ${{ env.VERSIONED_NAME }} - # May actually want to manually specify Paper versions all the time; they won't all - # need to be specified in code thanks to Mojang-mapped server internals. - # A bit annoying, but not terribly so. - # Instead, fetch min and max (which will also be Spigot version) and manually list intermediate versions? body: |- ## Supported server versions - **Paper:** TODO COMMA-SEPARATED VERSIONS + **Paper:** TODO VERSION, 1.21.4, 1.21.3, 1.21.1 **Spigot:** TODO VERSION TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE. diff --git a/scripts/install_spigot.sh b/scripts/install_spigot.sh deleted file mode 100755 index e9f98870..00000000 --- a/scripts/install_spigot.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2011-2022 lishid. All rights reserved. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -buildtools_dir=~/buildtools -buildtools=$buildtools_dir/BuildTools.jar - -get_buildtools () { - if [[ -d $buildtools_dir && -f $buildtools ]]; then - return - fi - - mkdir $buildtools_dir - wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools -} - -if [[ ! $1 ]]; then - echo "Please specify Spigot version to install." - exit 1 -fi - -get_buildtools - -pushd $buildtools_dir -java -jar $buildtools -rev $1 --remapped -popd diff --git a/scripts/set_curseforge_env.sh b/scripts/set_curseforge_env.sh index e345163b..c75d5202 100755 --- a/scripts/set_curseforge_env.sh +++ b/scripts/set_curseforge_env.sh @@ -18,26 +18,41 @@ # Note that this script is designed for use in GitHub Actions, and is not # particularly robust nor configurable. Run from project parent directory. -# Parse Spigot dependency information into major Minecraft versions -function get_curseforge_minecraft_versions() { - # TODO convert to parsing event body - readarray -t versions <<< "1.21.4" +if [[ ! $1 ]]; then + echo "No changelog, no Minecraft versions." + exit 0 +fi - for version in "${versions[@]}"; do - # Parse Minecraft major version - version="${version%[.-]"${version#*.*[.-]}"}" +# Find line declaring Paper versions. +raw=$(grep "**Paper:**" <<< "$1") - # Skip already listed versions - if [[ "$minecraft_versions" =~ "$version"($|,) ]]; then - continue - fi +# Enable extended glob pattern to match 0 or more whitespace characters. +shopt -s extglob +# Trim Paper versions identifier prefix. +raw=${raw##*([[:space:]])'**'Paper:'**'*([[:space:]])} +# Replace commas and optional spaces with a newline. +raw=${raw//,*([[:space:]])/$'\n'} +# Turn extglob back off. +shopt -u extglob - # Append comma if variable is set, then append version - minecraft_versions="${minecraft_versions:+${minecraft_versions},}Minecraft ${version}" - done +# Split into an array on newlines. +readarray -td $'\n' versions <<< "${raw}" - echo "${minecraft_versions}" -} +for version in "${versions[@]}"; do + # Parse Minecraft minor version by dropping everything from the second period onward. + # CurseForge doesn't usually add patch versions for Bukkit, so we're more likely to + # hit a supported identifier this way. + version="${version%[.-]"${version#*.*[.-]}"}" -minecraft_versions=$(get_curseforge_minecraft_versions) -echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV" + # Skip already listed versions + if [[ "$minecraft_versions" =~ "$version"($|,) ]]; then + continue + fi + + # Append comma if variable is set, then append version. + # Note that Minecraft versions on CurseForge are declared "Minecraft x.y.z" + minecraft_versions="${minecraft_versions:+${minecraft_versions},}Minecraft ${version}" +done + +printf "$minecraft_versions\n" +#echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV" From 51649bf2abadcb0ebd563b6565d0d901c3fc89e4 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 8 Mar 2025 17:53:41 -0500 Subject: [PATCH 275/340] Remap reflection (#294) * Hack around SpecialSource limitations with ProGuard mappings * Relocate PlanarWrappers * Use libs for shadow exclusion --- buildSrc/build.gradle.kts | 6 + buildSrc/settings.gradle.kts | 7 + .../com/github/jikoo/openinv/SpigotReobf.kt | 10 - .../github/jikoo/openinv/SpigotReobfTask.kt | 59 ++- .../specialsource/ReflectionJarMapping.kt | 479 ++++++++++++++++++ .../specialsource/ReflectionPreprocessor.kt | 105 ++++ gradle/libs.versions.toml | 4 +- .../reobf/container/AnySilentContainer.java | 4 +- plugin/build.gradle.kts | 7 +- 9 files changed, 636 insertions(+), 45 deletions(-) create mode 100644 buildSrc/settings.gradle.kts create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt create mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 52b9cc0a..753a6634 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -4,4 +4,10 @@ plugins { repositories { gradlePluginPortal() + mavenCentral() +} + +dependencies { + val libs = project.extensions.getByType(VersionCatalogsExtension::class.java).named("libs") + implementation(variantOf(libs.findLibrary("specialsource").orElseThrow()) { classifier("shaded") }) } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..215a5d58 --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,7 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt index 459423d8..7622058f 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobf.kt @@ -2,13 +2,11 @@ package com.github.jikoo.openinv import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Category import org.gradle.api.attributes.LibraryElements import org.gradle.api.attributes.Usage import org.gradle.jvm.tasks.Jar -import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.register import java.nio.file.Paths @@ -16,7 +14,6 @@ import java.nio.file.Paths class SpigotReobf: Plugin { companion object { - internal const val DEP_CONFIG = "spigotReobfDep" const val ARTIFACT_CONFIG = "reobf" } @@ -40,13 +37,6 @@ class SpigotReobf: Plugin { getMavenLocal().set(Paths.get(mvnLocal.url).toFile()) } - // Create a separate configuration for SpecialSource to make it easier to locate. - val reobfDeps = target.configurations.create(DEP_CONFIG) - target.dependencies { - val libs = target.extensions.getByType(VersionCatalogsExtension::class.java).named("libs") - reobfDeps(variantOf(libs.findLibrary("specialsource").orElseThrow()) { classifier("shaded") }) - } - // Set up configuration for producing reobf jar. target.configurations.consumable(ARTIFACT_CONFIG) { attributes { diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt index 44166ce7..33d3c3a5 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt @@ -1,19 +1,20 @@ package com.github.jikoo.openinv +import com.github.jikoo.openinv.specialsource.ReflectionJarMapping +import com.github.jikoo.openinv.specialsource.ReflectionPreprocessor +import net.md_5.specialsource.Jar +import net.md_5.specialsource.JarRemapper +import net.md_5.specialsource.provider.JarProvider +import net.md_5.specialsource.provider.JointProvider import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.bundling.Jar -import org.gradle.process.internal.ExecActionFactory import java.io.File -import javax.inject.Inject -abstract class SpigotReobfTask @Inject constructor( - private var execActionFactory: ExecActionFactory -) : Jar() { +abstract class SpigotReobfTask: org.gradle.api.tasks.bundling.Jar() { @get:Input val spigotVersion: Property = objectFactory.property(String::class.java) @@ -24,12 +25,6 @@ abstract class SpigotReobfTask @Inject constructor( @get:Input val intermediaryClassifier: Property = objectFactory.property(String::class.java).convention("mojang-mapped") - private val specialSource: Property = objectFactory.property(File::class.java).convention(project.provider { - // Grab SpecialSource location from dependency declaration. - project.configurations.named(SpigotReobf.DEP_CONFIG).get().incoming.artifacts.artifacts - .first { it.id.componentIdentifier.toString().startsWith("net.md-5:SpecialSource:") }.file - }) - private val mavenLocal: Property = objectFactory.property(File::class.java) init { @@ -43,7 +38,6 @@ abstract class SpigotReobfTask @Inject constructor( val obfPath = inFile.resolveSibling(inFile.name.replace(".jar", "-${intermediaryClassifier.get()}.jar")) // https://www.spigotmc.org/threads/510208/#post-4184317 - val specialSourceFile = specialSource.get() val repo = mavenLocal.get() val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVer/") val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVer/") @@ -51,27 +45,36 @@ abstract class SpigotReobfTask @Inject constructor( // Remap original Mojang-mapped jar to obfuscated intermediary val mojangServer = spigotDir.resolve("spigot-$spigotVer-remapped-mojang.jar") val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-mojang.txt") - remapPartial(specialSourceFile, mojangServer, mojangMappings, inFile, obfPath, true) + remapPartial(mojangServer, mojangMappings, inFile, obfPath, true) // Remap obfuscated intermediary jar to Spigot and replace original val obfServer = spigotDir.resolve("spigot-$spigotVer-remapped-obf.jar") val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-spigot.csrg") - remapPartial(specialSourceFile, obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false) + remapPartial(obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false) } - private fun remapPartial(specialSourceFile: File, serverJar: File, mapping: File, input: File, output: File, reverse: Boolean) { - // May need a direct dependency on SpecialSource later to customize behavior. - val exec = execActionFactory.newJavaExecAction() - exec.classpath(specialSourceFile, serverJar) - exec.mainClass.value("net.md_5.specialsource.SpecialSource") - exec.args( - "--live", - "-i", input.path, - "-o", output.path, - "-m", "$mapping", - if (reverse) "--reverse" else "" - ) - exec.execute().rethrowFailure() + private fun remapPartial(server: File, mapping: File, input: File, output: File, reverse: Boolean) { + val jarMapping = ReflectionJarMapping() + jarMapping.loadMappings(mapping.path, reverse, false, null, null) + + val inheritance = JointProvider() + jarMapping.setFallbackInheritanceProvider(inheritance) + + // Equivalent of --live with server jar on classpath. + val serverJar = Jar.init(server) + inheritance.add(JarProvider(serverJar)) + + val inputJar = Jar.init(input) + inheritance.add(JarProvider(inputJar)) + + // Remap reflective access. + val preprocessor = ReflectionPreprocessor(jarMapping) + + val remapper = JarRemapper(preprocessor, jarMapping, null) + remapper.remapJar(inputJar, output) + + serverJar.close() + inputJar.close() } @Internal diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt new file mode 100644 index 00000000..98aa862d --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt @@ -0,0 +1,479 @@ +package com.github.jikoo.openinv.specialsource + +import net.md_5.specialsource.JarMapping +import net.md_5.specialsource.ProgressMeter +import net.md_5.specialsource.transformer.MappingTransformer +import net.md_5.specialsource.transformer.MavenShade +import org.objectweb.asm.commons.Remapper +import java.io.BufferedReader +import java.io.IOException +import java.util.regex.Matcher +import java.util.regex.Pattern + + +open class ReflectionJarMapping : JarMapping() { + + val reflectableFields: MutableMap = HashMap() + protected var currentClass: String? = null + + override fun loadMappings( + reader: BufferedReader, + inputTransformer: MappingTransformer?, + outputTransformer: MappingTransformer?, + reverse: Boolean + ) { + val inTransformer = inputTransformer ?: MavenShade.IDENTITY + val outTransformer = outputTransformer ?: MavenShade.IDENTITY + + val lines = reader.lines() + .map { + val comment = it.indexOf('#') + if (comment == -1) { + return@map it + } + return@map it.substring(0, comment) + } + .filter(String::isNotBlank) + .toList() + + val meter = ProgressMeter(lines.size * 2, "Loading mappings... %2.0f%%") + val clsMap: MutableMap = HashMap() + val prgMap: MutableMap = HashMap() + + val proguard = " -> ".toRegex() + for (l in lines) { + if (l.endsWith(":")) { + val parts = l.split(proguard).dropLastWhile(String::isEmpty) + val orig = parts[0].replace('.', '/') + val obf = parts[1].substring(0, parts[1].length - 1).replace('.', '/') + clsMap[obf] = orig + prgMap[orig] = obf + } else if (l.contains(":")) { + if (!l.startsWith("CL:")) { + continue + } + + val tokens = l.split(" ".toRegex()).dropLastWhile(String::isEmpty) + clsMap[tokens[0]] = tokens[1] + } else { + if (l.startsWith("\t")) { + continue + } + + val tokens = l.split(" ".toRegex()).dropLastWhile(String::isEmpty) + if (tokens.size == 2) { + clsMap[tokens[0]] = tokens[1] + } + } + + meter.makeProgress() + } + + val reverseMapper: Remapper = object : Remapper() { + override fun map(cls: String): String { + return clsMap.getOrDefault(cls, cls) + } + } + + for (l in lines) { + if (!l.startsWith("tsrg2")) { + if (l.contains(" -> ")) { + this.parseProguardLine(l, inTransformer, outTransformer, reverse, reverseMapper, prgMap) + } else if (l.contains(":")) { + this.parseSrgLine(l, inTransformer, outTransformer, reverse) + } else { + this.parseCsrgLine(l, inTransformer, outTransformer, reverse, reverseMapper) + } + + meter.makeProgress() + } + } + + this.currentClass = null + } + + class ProguardUtil { + companion object { + val MEMBER_PATTERN: Pattern = Pattern.compile("(?:\\d+:\\d+:)?(.*?) (.*?) -> (.*)") + + fun csrgDesc(data: Map, args: String, ret: String): String { + val parts = args.substring(1, args.length - 1).split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val desc = StringBuilder("(") + for (part in parts) { + if (part.isEmpty()) { + continue + } + desc.append(toJvmType(data, part)) + } + desc.append(")") + desc.append(toJvmType(data, ret)) + return desc.toString() + } + + fun toJvmType(data: Map, type: String): String { + when (type) { + "byte" -> return "B" + "char" -> return "C" + "double" -> return "D" + "float" -> return "F" + "int" -> return "I" + "long" -> return "J" + "short" -> return "S" + "boolean" -> return "Z" + "void" -> return "V" + else -> { + if (type.endsWith("[]")) { + return "[" + toJvmType(data, type.substring(0, type.length - 2)) + } + val clazzType = type.replace('.', '/') + val mappedType = data[clazzType] + + return "L" + (mappedType ?: clazzType) + ";" + } + } + } + } + + } + + @Throws(IOException::class) + open fun parseProguardLine( + originalLine: String, + inputTransformer: MappingTransformer, + outputTransformer: MappingTransformer, + reverse: Boolean, + reverseMap: Remapper, + prgMap: Map + ) { + // Tsrg format, identical to Csrg, except the field and method lines should use the last class the was parsed. + var line = originalLine + if (line.startsWith(" ")) { + if (this.currentClass == null) { + throw IOException("Invalid proguard file, tsrg field/method line before class line: $line") + } + line = line.trim { it <= ' ' } + } + + if (line.endsWith(":")) { + val parts = line.split(" -> ".toRegex()).dropLastWhile { it.isEmpty() } + val orig = parts[0].replace('.', '/') + val obf = parts[1].substring(0, parts[1].length - 1).replace('.', '/') + + val oldClassName = inputTransformer.transformClassName(obf) + val newClassName = outputTransformer.transformClassName(orig) + + if (oldClassName.endsWith("/")) { + // Special case: mapping an entire hierarchy of classes + if (reverse) { + packages[newClassName] = oldClassName.substring(0, oldClassName.length - 1) + } else { + packages[oldClassName.substring(0, oldClassName.length - 1)] = newClassName + } + } else { + if (reverse) { + classes[newClassName] = oldClassName + } else { + classes[oldClassName] = newClassName + } + currentClass = obf + } + } else { + val matcher: Matcher = ProguardUtil.MEMBER_PATTERN.matcher(line) + matcher.find() + + val obfName: String = matcher.group(3) + val nameDesc: String = matcher.group(2) + if (nameDesc.contains("(")) { + val desc = ProguardUtil.csrgDesc(prgMap, nameDesc.substring(nameDesc.indexOf('(')), matcher.group(1)) + val newName = nameDesc.substring(0, nameDesc.indexOf('(')) + + var oldClassName = inputTransformer.transformClassName(currentClass) + var oldMethodName = inputTransformer.transformMethodName(currentClass, obfName, desc) + var oldMethodDescriptor = inputTransformer.transformMethodDescriptor(desc) + var newMethodName = outputTransformer.transformMethodName(currentClass, newName, desc) + + if (reverse) { + val newClassName = reverseMap.map(oldClassName) + oldClassName = newClassName + oldMethodDescriptor = reverseMap.mapMethodDesc(oldMethodDescriptor) + + val temp = newMethodName + newMethodName = oldMethodName + oldMethodName = temp + } + + methods["$oldClassName/$oldMethodName $oldMethodDescriptor"] = newMethodName + } else { + val desc = ProguardUtil.toJvmType(prgMap, matcher.group(1)) + + var oldClassName = inputTransformer.transformClassName(currentClass) + var oldFieldName = inputTransformer.transformFieldName(currentClass, obfName) + var oldFieldDesc = inputTransformer.transformMethodDescriptor(desc) + var newFieldName = outputTransformer.transformFieldName(currentClass, nameDesc) + + if (reverse) { + val newClassName = reverseMap.map(oldClassName) + oldClassName = newClassName + oldFieldDesc = reverseMap.mapDesc(oldFieldDesc) + + val temp = newFieldName + newFieldName = oldFieldName + oldFieldName = temp + } + + reflectableFields["$oldClassName/$oldFieldName"] = newFieldName + fields["$oldClassName/$oldFieldName/$oldFieldDesc"] = newFieldName + } + } + } + + /** + * Parse a 'csrg' mapping format line and populate the data structures + */ + @Throws(IOException::class) + open fun parseCsrgLine( + originalLine: String, + inputTransformer: MappingTransformer, + outputTransformer: MappingTransformer, + reverse: Boolean, + reverseMap: Remapper + ) { + //Tsrg format, identical to Csrg, except the field and method lines start with \t and should use the last class the was parsed. + var line = originalLine + if (line.startsWith("\t\t")) { + // Two tabs means the format is Tsrgv2 with parameters and extra data that isn't needed. + return + } + if (line.startsWith("\t")) { + if (this.currentClass == null) { + throw IOException("Invalid tsrg file, tsrg field/method line before class line: $line") + } + line = currentClass + " " + line.substring(1) + } + + val tokens = line.split(" ".toRegex()).dropLastWhile { it.isEmpty() } + + if (tokens.size == 2) { + var oldClassName = inputTransformer.transformClassName(tokens[0]) + var newClassName = outputTransformer.transformClassName(tokens[1]) + + if (oldClassName.endsWith("/")) { + // package names always either 1) suffixed with '/', or 2) equal to '.' to signify default package + + if (newClassName != "." && !newClassName.endsWith("/")) { + newClassName += "/" + } + + if (oldClassName != "." && !oldClassName.endsWith("/")) { + oldClassName += "/" + } + + // Special case: mapping an entire hierarchy of classes + if (reverse) { + packages[newClassName] = oldClassName + } else { + packages[oldClassName] = newClassName + } + } else { + if (reverse) { + classes[newClassName] = oldClassName + } else { + classes[oldClassName] = newClassName + } + currentClass = tokens[0] + } + } else if (tokens.size == 3) { + var oldClassName = inputTransformer.transformClassName(tokens[0]) + var oldFieldName = inputTransformer.transformFieldName(tokens[0], tokens[1]) + var newFieldName = outputTransformer.transformFieldName(tokens[0], tokens[2]) + + if (reverse) { + val newClassName = reverseMap.map(oldClassName) + oldClassName = newClassName + + val temp = newFieldName + newFieldName = oldFieldName + oldFieldName = temp + } + + reflectableFields["$oldClassName/$oldFieldName"] = newFieldName + fields["$oldClassName/$oldFieldName"] = newFieldName + } else if (tokens.size == 4) { + var oldClassName = inputTransformer.transformClassName(tokens[0]) + var oldMethodName = inputTransformer.transformMethodName(tokens[0], tokens[1], tokens[2]) + var oldMethodDescriptor = inputTransformer.transformMethodDescriptor(tokens[2]) + var newMethodName = outputTransformer.transformMethodName(tokens[0], tokens[3], tokens[2]) + + if (reverse) { + val newClassName = reverseMap.map(oldClassName) + oldClassName = newClassName + oldMethodDescriptor = reverseMap.mapMethodDesc(oldMethodDescriptor) + + val temp = newMethodName + newMethodName = oldMethodName + oldMethodName = temp + } + + methods["$oldClassName/$oldMethodName $oldMethodDescriptor"] = newMethodName + } else { + throw IOException("Invalid csrg file line, token count " + tokens.size + " unexpected in " + line) + } + } + + /** + * Parse a standard 'srg' mapping format line and populate the data + * structures + */ + @Throws(IOException::class) + open fun parseSrgLine( + line: String, + inputTransformer: MappingTransformer, + outputTransformer: MappingTransformer, + reverse: Boolean + ) { + val tokens = line.split(" ".toRegex()).dropLastWhile { it.isEmpty() } + val kind = tokens[0] + + if (kind == "CL:") { + var oldClassName = inputTransformer.transformClassName(tokens[1]) + var newClassName = outputTransformer.transformClassName(tokens[2]) + + if (reverse) { + val temp = newClassName + newClassName = oldClassName + oldClassName = temp + } + + require(!(classes.containsKey(oldClassName) && newClassName != classes[oldClassName])) { + ("Duplicate class mapping: " + oldClassName + " -> " + newClassName + + " but already mapped to " + classes[oldClassName] + " in line=" + line) + } + + if (oldClassName.endsWith("/*") && newClassName.endsWith("/*")) { + // extension for remapping class name prefixes + oldClassName = oldClassName.substring(0, oldClassName.length - 1) + newClassName = newClassName.substring(0, newClassName.length - 1) + + packages[oldClassName] = newClassName + } else { + classes[oldClassName] = newClassName + currentClass = tokens[0] + } + } else if (kind == "PK:") { + var oldPackageName = inputTransformer.transformClassName(tokens[1]) + var newPackageName = outputTransformer.transformClassName(tokens[2]) + + if (reverse) { + val temp = newPackageName + newPackageName = oldPackageName + oldPackageName = temp + } + + // package names always either 1) suffixed with '/', or 2) equal to '.' to signify default package + if (newPackageName != "." && !newPackageName.endsWith("/")) { + newPackageName += "/" + } + + if (oldPackageName != "." && !oldPackageName.endsWith("/")) { + oldPackageName += "/" + } + + require(!(packages.containsKey(oldPackageName) && newPackageName != packages[oldPackageName])) { + ("Duplicate package mapping: " + oldPackageName + " ->" + newPackageName + + " but already mapped to " + packages[oldPackageName] + " in line=" + line) + } + + packages[oldPackageName] = newPackageName + } else if (kind == "FD:") { + val oldFull = tokens[1] + val newFull = tokens[2] + + // Split the qualified field names into their classes and actual names + val splitOld = oldFull.lastIndexOf('/') + val splitNew = newFull.lastIndexOf('/') + require(!(splitOld == -1 || splitNew == -1)) { + ("Field name is invalid, not fully-qualified: " + oldFull + + " -> " + newFull + " in line=" + line) + } + + var oldClassName = inputTransformer.transformClassName(oldFull.substring(0, splitOld)) + var oldFieldName = + inputTransformer.transformFieldName(oldFull.substring(0, splitOld), oldFull.substring(splitOld + 1)) + val newClassName = outputTransformer.transformClassName( + newFull.substring( + 0, + splitNew + ) + ) + var newFieldName = + outputTransformer.transformFieldName(oldFull.substring(0, splitOld), newFull.substring(splitNew + 1)) + + if (reverse) { + oldClassName = newClassName + + val temp = newFieldName + newFieldName = oldFieldName + oldFieldName = temp + } + + val oldEntry = "$oldClassName/$oldFieldName" + require(!(fields.containsKey(oldEntry) && newFieldName != fields[oldEntry])) { + ("Duplicate field mapping: " + oldEntry + " ->" + newFieldName + + " but already mapped to " + fields[oldEntry] + " in line=" + line) + } + + reflectableFields[oldEntry] = newFieldName + fields[oldEntry] = newFieldName + } else if (kind == "MD:") { + val oldFull = tokens[1] + val newFull = tokens[3] + + // Split the qualified field names into their classes and actual names + val splitOld = oldFull.lastIndexOf('/') + val splitNew = newFull.lastIndexOf('/') + require(!(splitOld == -1 || splitNew == -1)) { + ("Field name is invalid, not fully-qualified: " + oldFull + + " -> " + newFull + " in line=" + line) + } + + var oldClassName = inputTransformer.transformClassName(oldFull.substring(0, splitOld)) + var oldMethodName = inputTransformer.transformMethodName( + oldFull.substring(0, splitOld), oldFull.substring(splitOld + 1), + tokens[2] + ) + var oldMethodDescriptor = inputTransformer.transformMethodDescriptor(tokens[2]) + val newClassName = outputTransformer.transformClassName( + newFull.substring( + 0, + splitNew + ) + ) + var newMethodName = outputTransformer.transformMethodName( + oldFull.substring(0, splitOld), newFull.substring(splitNew + 1), + tokens[2] + ) + val newMethodDescriptor = + outputTransformer.transformMethodDescriptor(tokens[4]) + + if (reverse) { + oldClassName = newClassName + oldMethodDescriptor = newMethodDescriptor + + val temp = newMethodName + newMethodName = oldMethodName + oldMethodName = temp + } + + val oldEntry = "$oldClassName/$oldMethodName $oldMethodDescriptor" + require(!(methods.containsKey(oldEntry) && newMethodName != methods[oldEntry])) { + ("Duplicate method mapping: " + oldEntry + " ->" + newMethodName + + " but already mapped to " + methods[oldEntry] + " in line=" + line) + } + + methods[oldEntry] = newMethodName + } else { + throw IllegalArgumentException("Unable to parse srg file, unrecognized mapping type in line=$line") + } + } + +} diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt new file mode 100644 index 00000000..ad3fa3ae --- /dev/null +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt @@ -0,0 +1,105 @@ +package com.github.jikoo.openinv.specialsource + +import net.md_5.specialsource.NodeType +import net.md_5.specialsource.RemapperProcessor +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.tree.AbstractInsnNode +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.LdcInsnNode +import org.objectweb.asm.tree.MethodInsnNode + +open class ReflectionPreprocessor( + private val jarMapping: ReflectionJarMapping +) : RemapperProcessor(null, jarMapping, null) { + + override fun process(classReader: ClassReader): ByteArray { + val classNode = ClassNode() + classReader.accept(classNode, 0) + + for (methodNode in classNode.methods) { + var insn = methodNode.instructions.first + while (insn != null) { + when (insn.opcode) { + Opcodes.INVOKEVIRTUAL -> this.remapGetDeclaredField(insn) + Opcodes.INVOKESTATIC -> this.remapClassForName(insn) + } + insn = insn.next + } + } + + val classWriter = ClassWriter(0) + classNode.accept(classWriter) + return classWriter.toByteArray() + } + + open fun remapGetDeclaredField(insn: AbstractInsnNode) { + val mi = insn as MethodInsnNode + + if (mi.owner != "java/lang/Class" || mi.name != "getDeclaredField" || mi.desc != "(Ljava/lang/String;)Ljava/lang/reflect/Field;") { + return + } + + this.logR("Found getDeclaredField!") + if (insn.previous == null || insn.previous.opcode != Opcodes.LDC) { + this.logR("- not constant field; skipping, prev=" + insn.getPrevious()) + return + } + val ldcField = insn.getPrevious() as LdcInsnNode + if (ldcField.cst !is String) { + this.logR("- not field string; skipping: ${ldcField.cst}") + return + } + val fieldName = ldcField.cst as String + if (ldcField.previous == null || ldcField.previous.opcode != Opcodes.LDC) { + this.logR("- not constant class; skipping: field=$fieldName") + return + } + val ldcClass = ldcField.previous as LdcInsnNode + if (ldcClass.cst !is Type) { + this.logR("- not class type; skipping: field=${ldcClass.cst}, class=${ldcClass.cst}") + return + } + + val className = (ldcClass.cst as Type).internalName + val newName = jarMapping.tryClimb(jarMapping.reflectableFields, NodeType.FIELD, className, fieldName, null, 0) + this.logR("Remapping $className/$fieldName -> $newName") + if (newName != null) { + ldcField.cst = newName + } + } + + open fun remapClassForName(insn: AbstractInsnNode) { + val mi = insn as MethodInsnNode + + if (mi.owner != "java/lang/Class" || mi.name != "forName" || mi.desc != "(Ljava/lang/String;)Ljava/lang/Class;") { + return + } + this.logR("Found Class forName!") + if (insn.getPrevious() == null || insn.getPrevious().opcode != Opcodes.LDC) { + this.logR("- not constant field; skipping, prev=${insn.previous}") + return + } + val ldcClassName = insn.getPrevious() as LdcInsnNode + if (ldcClassName.cst !is String) { + this.logR("- not field string; skipping: " + ldcClassName.cst) + return + } + + val className = ldcClassName.cst as String + val newName = jarMapping.classes[className.replace('.', '/')] + this.logR("Remapping $className -> $newName") + if (newName != null) { + ldcClassName.cst = newName.replace('/', '.') + } + } + + protected fun logR(message: String) { + if (this.debug) { + println("[ReflectionRemapper] $message") + } + } + +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab1c5dd3..18861484 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,14 +5,14 @@ planarwrappers = "3.3.0" annotations = "26.0.2" paperweight = "2.0.0-beta.14" shadow = "8.3.6" -folia = "v0.0.3" +folia-scheduler-wrapper = "v0.0.3" [libraries] spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } specialsource = { module = "net.md-5:SpecialSource", version.ref = "specialsource" } planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" } annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } -folia = { module = "com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper", version.ref = "folia" } +folia-scheduler-wrapper = { module = "com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper", version.ref = "folia-scheduler-wrapper" } [plugins] paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" } diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java index fe1ab4fa..f19091f2 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java @@ -45,10 +45,10 @@ public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) this.lang = lang; try { try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b"); + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("gameModeForPlayer"); this.serverPlayerGameModeGameType.setAccessible(true); } catch (NoSuchFieldException e) { - logger.warning("The mapping of ServerPlayerGameMode#gameModeForPlayer has changed!"); + logger.warning("The field ServerPlayerGameMode#gameModeForPlayer is no longer present!"); logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 73beed82..8ca6ce9a 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { implementation(project(":openinvadapterpaper1_21_1")) implementation(project(":openinvadapterspigot", configuration = SpigotReobf.ARTIFACT_CONFIG)) implementation(libs.planarwrappers) - implementation(libs.folia) + implementation(libs.folia.scheduler.wrapper) } tasks.processResources { @@ -29,10 +29,11 @@ tasks.jar { } tasks.shadowJar { - relocate("me.nahu.scheduler.wrapper", "com.lishid.openinv.internal.folia.scheduler") + relocate("me.nahu.scheduler.wrapper", "com.github.jikoo.openinv.lib.nahu.scheduler-wrapper") + relocate("com.github.jikoo.planarwrappers", "com.github.jikoo.openinv.lib.planarwrappers") minimize { exclude(":openinv**") - exclude(dependency("com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper:.*")) + exclude(dependency(libs.folia.scheduler.wrapper.get())) } } From 456909966fea92d8f62e07947e4d5c68020a92d7 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 8 Mar 2025 20:58:49 -0500 Subject: [PATCH 276/340] Reduce remapping maintenance burden I don't think SpecialSource is likely to make major changes to JarMapping, but RemapperProcessor hasn't been changed in 4 years. This is less efficient in terms of lookup speed, but does not require the hacky JarMapping extension and doesn't require a second copy of the field mappings to be kept in memory. --- .../github/jikoo/openinv/SpigotReobfTask.kt | 4 +- .../specialsource/ReflectionJarMapping.kt | 479 ------------------ .../specialsource/ReflectionPreprocessor.kt | 70 ++- 3 files changed, 65 insertions(+), 488 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt index 33d3c3a5..79bc6813 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt @@ -1,8 +1,8 @@ package com.github.jikoo.openinv -import com.github.jikoo.openinv.specialsource.ReflectionJarMapping import com.github.jikoo.openinv.specialsource.ReflectionPreprocessor import net.md_5.specialsource.Jar +import net.md_5.specialsource.JarMapping import net.md_5.specialsource.JarRemapper import net.md_5.specialsource.provider.JarProvider import net.md_5.specialsource.provider.JointProvider @@ -54,7 +54,7 @@ abstract class SpigotReobfTask: org.gradle.api.tasks.bundling.Jar() { } private fun remapPartial(server: File, mapping: File, input: File, output: File, reverse: Boolean) { - val jarMapping = ReflectionJarMapping() + val jarMapping = JarMapping() jarMapping.loadMappings(mapping.path, reverse, false, null, null) val inheritance = JointProvider() diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt deleted file mode 100644 index 98aa862d..00000000 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionJarMapping.kt +++ /dev/null @@ -1,479 +0,0 @@ -package com.github.jikoo.openinv.specialsource - -import net.md_5.specialsource.JarMapping -import net.md_5.specialsource.ProgressMeter -import net.md_5.specialsource.transformer.MappingTransformer -import net.md_5.specialsource.transformer.MavenShade -import org.objectweb.asm.commons.Remapper -import java.io.BufferedReader -import java.io.IOException -import java.util.regex.Matcher -import java.util.regex.Pattern - - -open class ReflectionJarMapping : JarMapping() { - - val reflectableFields: MutableMap = HashMap() - protected var currentClass: String? = null - - override fun loadMappings( - reader: BufferedReader, - inputTransformer: MappingTransformer?, - outputTransformer: MappingTransformer?, - reverse: Boolean - ) { - val inTransformer = inputTransformer ?: MavenShade.IDENTITY - val outTransformer = outputTransformer ?: MavenShade.IDENTITY - - val lines = reader.lines() - .map { - val comment = it.indexOf('#') - if (comment == -1) { - return@map it - } - return@map it.substring(0, comment) - } - .filter(String::isNotBlank) - .toList() - - val meter = ProgressMeter(lines.size * 2, "Loading mappings... %2.0f%%") - val clsMap: MutableMap = HashMap() - val prgMap: MutableMap = HashMap() - - val proguard = " -> ".toRegex() - for (l in lines) { - if (l.endsWith(":")) { - val parts = l.split(proguard).dropLastWhile(String::isEmpty) - val orig = parts[0].replace('.', '/') - val obf = parts[1].substring(0, parts[1].length - 1).replace('.', '/') - clsMap[obf] = orig - prgMap[orig] = obf - } else if (l.contains(":")) { - if (!l.startsWith("CL:")) { - continue - } - - val tokens = l.split(" ".toRegex()).dropLastWhile(String::isEmpty) - clsMap[tokens[0]] = tokens[1] - } else { - if (l.startsWith("\t")) { - continue - } - - val tokens = l.split(" ".toRegex()).dropLastWhile(String::isEmpty) - if (tokens.size == 2) { - clsMap[tokens[0]] = tokens[1] - } - } - - meter.makeProgress() - } - - val reverseMapper: Remapper = object : Remapper() { - override fun map(cls: String): String { - return clsMap.getOrDefault(cls, cls) - } - } - - for (l in lines) { - if (!l.startsWith("tsrg2")) { - if (l.contains(" -> ")) { - this.parseProguardLine(l, inTransformer, outTransformer, reverse, reverseMapper, prgMap) - } else if (l.contains(":")) { - this.parseSrgLine(l, inTransformer, outTransformer, reverse) - } else { - this.parseCsrgLine(l, inTransformer, outTransformer, reverse, reverseMapper) - } - - meter.makeProgress() - } - } - - this.currentClass = null - } - - class ProguardUtil { - companion object { - val MEMBER_PATTERN: Pattern = Pattern.compile("(?:\\d+:\\d+:)?(.*?) (.*?) -> (.*)") - - fun csrgDesc(data: Map, args: String, ret: String): String { - val parts = args.substring(1, args.length - 1).split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - val desc = StringBuilder("(") - for (part in parts) { - if (part.isEmpty()) { - continue - } - desc.append(toJvmType(data, part)) - } - desc.append(")") - desc.append(toJvmType(data, ret)) - return desc.toString() - } - - fun toJvmType(data: Map, type: String): String { - when (type) { - "byte" -> return "B" - "char" -> return "C" - "double" -> return "D" - "float" -> return "F" - "int" -> return "I" - "long" -> return "J" - "short" -> return "S" - "boolean" -> return "Z" - "void" -> return "V" - else -> { - if (type.endsWith("[]")) { - return "[" + toJvmType(data, type.substring(0, type.length - 2)) - } - val clazzType = type.replace('.', '/') - val mappedType = data[clazzType] - - return "L" + (mappedType ?: clazzType) + ";" - } - } - } - } - - } - - @Throws(IOException::class) - open fun parseProguardLine( - originalLine: String, - inputTransformer: MappingTransformer, - outputTransformer: MappingTransformer, - reverse: Boolean, - reverseMap: Remapper, - prgMap: Map - ) { - // Tsrg format, identical to Csrg, except the field and method lines should use the last class the was parsed. - var line = originalLine - if (line.startsWith(" ")) { - if (this.currentClass == null) { - throw IOException("Invalid proguard file, tsrg field/method line before class line: $line") - } - line = line.trim { it <= ' ' } - } - - if (line.endsWith(":")) { - val parts = line.split(" -> ".toRegex()).dropLastWhile { it.isEmpty() } - val orig = parts[0].replace('.', '/') - val obf = parts[1].substring(0, parts[1].length - 1).replace('.', '/') - - val oldClassName = inputTransformer.transformClassName(obf) - val newClassName = outputTransformer.transformClassName(orig) - - if (oldClassName.endsWith("/")) { - // Special case: mapping an entire hierarchy of classes - if (reverse) { - packages[newClassName] = oldClassName.substring(0, oldClassName.length - 1) - } else { - packages[oldClassName.substring(0, oldClassName.length - 1)] = newClassName - } - } else { - if (reverse) { - classes[newClassName] = oldClassName - } else { - classes[oldClassName] = newClassName - } - currentClass = obf - } - } else { - val matcher: Matcher = ProguardUtil.MEMBER_PATTERN.matcher(line) - matcher.find() - - val obfName: String = matcher.group(3) - val nameDesc: String = matcher.group(2) - if (nameDesc.contains("(")) { - val desc = ProguardUtil.csrgDesc(prgMap, nameDesc.substring(nameDesc.indexOf('(')), matcher.group(1)) - val newName = nameDesc.substring(0, nameDesc.indexOf('(')) - - var oldClassName = inputTransformer.transformClassName(currentClass) - var oldMethodName = inputTransformer.transformMethodName(currentClass, obfName, desc) - var oldMethodDescriptor = inputTransformer.transformMethodDescriptor(desc) - var newMethodName = outputTransformer.transformMethodName(currentClass, newName, desc) - - if (reverse) { - val newClassName = reverseMap.map(oldClassName) - oldClassName = newClassName - oldMethodDescriptor = reverseMap.mapMethodDesc(oldMethodDescriptor) - - val temp = newMethodName - newMethodName = oldMethodName - oldMethodName = temp - } - - methods["$oldClassName/$oldMethodName $oldMethodDescriptor"] = newMethodName - } else { - val desc = ProguardUtil.toJvmType(prgMap, matcher.group(1)) - - var oldClassName = inputTransformer.transformClassName(currentClass) - var oldFieldName = inputTransformer.transformFieldName(currentClass, obfName) - var oldFieldDesc = inputTransformer.transformMethodDescriptor(desc) - var newFieldName = outputTransformer.transformFieldName(currentClass, nameDesc) - - if (reverse) { - val newClassName = reverseMap.map(oldClassName) - oldClassName = newClassName - oldFieldDesc = reverseMap.mapDesc(oldFieldDesc) - - val temp = newFieldName - newFieldName = oldFieldName - oldFieldName = temp - } - - reflectableFields["$oldClassName/$oldFieldName"] = newFieldName - fields["$oldClassName/$oldFieldName/$oldFieldDesc"] = newFieldName - } - } - } - - /** - * Parse a 'csrg' mapping format line and populate the data structures - */ - @Throws(IOException::class) - open fun parseCsrgLine( - originalLine: String, - inputTransformer: MappingTransformer, - outputTransformer: MappingTransformer, - reverse: Boolean, - reverseMap: Remapper - ) { - //Tsrg format, identical to Csrg, except the field and method lines start with \t and should use the last class the was parsed. - var line = originalLine - if (line.startsWith("\t\t")) { - // Two tabs means the format is Tsrgv2 with parameters and extra data that isn't needed. - return - } - if (line.startsWith("\t")) { - if (this.currentClass == null) { - throw IOException("Invalid tsrg file, tsrg field/method line before class line: $line") - } - line = currentClass + " " + line.substring(1) - } - - val tokens = line.split(" ".toRegex()).dropLastWhile { it.isEmpty() } - - if (tokens.size == 2) { - var oldClassName = inputTransformer.transformClassName(tokens[0]) - var newClassName = outputTransformer.transformClassName(tokens[1]) - - if (oldClassName.endsWith("/")) { - // package names always either 1) suffixed with '/', or 2) equal to '.' to signify default package - - if (newClassName != "." && !newClassName.endsWith("/")) { - newClassName += "/" - } - - if (oldClassName != "." && !oldClassName.endsWith("/")) { - oldClassName += "/" - } - - // Special case: mapping an entire hierarchy of classes - if (reverse) { - packages[newClassName] = oldClassName - } else { - packages[oldClassName] = newClassName - } - } else { - if (reverse) { - classes[newClassName] = oldClassName - } else { - classes[oldClassName] = newClassName - } - currentClass = tokens[0] - } - } else if (tokens.size == 3) { - var oldClassName = inputTransformer.transformClassName(tokens[0]) - var oldFieldName = inputTransformer.transformFieldName(tokens[0], tokens[1]) - var newFieldName = outputTransformer.transformFieldName(tokens[0], tokens[2]) - - if (reverse) { - val newClassName = reverseMap.map(oldClassName) - oldClassName = newClassName - - val temp = newFieldName - newFieldName = oldFieldName - oldFieldName = temp - } - - reflectableFields["$oldClassName/$oldFieldName"] = newFieldName - fields["$oldClassName/$oldFieldName"] = newFieldName - } else if (tokens.size == 4) { - var oldClassName = inputTransformer.transformClassName(tokens[0]) - var oldMethodName = inputTransformer.transformMethodName(tokens[0], tokens[1], tokens[2]) - var oldMethodDescriptor = inputTransformer.transformMethodDescriptor(tokens[2]) - var newMethodName = outputTransformer.transformMethodName(tokens[0], tokens[3], tokens[2]) - - if (reverse) { - val newClassName = reverseMap.map(oldClassName) - oldClassName = newClassName - oldMethodDescriptor = reverseMap.mapMethodDesc(oldMethodDescriptor) - - val temp = newMethodName - newMethodName = oldMethodName - oldMethodName = temp - } - - methods["$oldClassName/$oldMethodName $oldMethodDescriptor"] = newMethodName - } else { - throw IOException("Invalid csrg file line, token count " + tokens.size + " unexpected in " + line) - } - } - - /** - * Parse a standard 'srg' mapping format line and populate the data - * structures - */ - @Throws(IOException::class) - open fun parseSrgLine( - line: String, - inputTransformer: MappingTransformer, - outputTransformer: MappingTransformer, - reverse: Boolean - ) { - val tokens = line.split(" ".toRegex()).dropLastWhile { it.isEmpty() } - val kind = tokens[0] - - if (kind == "CL:") { - var oldClassName = inputTransformer.transformClassName(tokens[1]) - var newClassName = outputTransformer.transformClassName(tokens[2]) - - if (reverse) { - val temp = newClassName - newClassName = oldClassName - oldClassName = temp - } - - require(!(classes.containsKey(oldClassName) && newClassName != classes[oldClassName])) { - ("Duplicate class mapping: " + oldClassName + " -> " + newClassName - + " but already mapped to " + classes[oldClassName] + " in line=" + line) - } - - if (oldClassName.endsWith("/*") && newClassName.endsWith("/*")) { - // extension for remapping class name prefixes - oldClassName = oldClassName.substring(0, oldClassName.length - 1) - newClassName = newClassName.substring(0, newClassName.length - 1) - - packages[oldClassName] = newClassName - } else { - classes[oldClassName] = newClassName - currentClass = tokens[0] - } - } else if (kind == "PK:") { - var oldPackageName = inputTransformer.transformClassName(tokens[1]) - var newPackageName = outputTransformer.transformClassName(tokens[2]) - - if (reverse) { - val temp = newPackageName - newPackageName = oldPackageName - oldPackageName = temp - } - - // package names always either 1) suffixed with '/', or 2) equal to '.' to signify default package - if (newPackageName != "." && !newPackageName.endsWith("/")) { - newPackageName += "/" - } - - if (oldPackageName != "." && !oldPackageName.endsWith("/")) { - oldPackageName += "/" - } - - require(!(packages.containsKey(oldPackageName) && newPackageName != packages[oldPackageName])) { - ("Duplicate package mapping: " + oldPackageName + " ->" + newPackageName - + " but already mapped to " + packages[oldPackageName] + " in line=" + line) - } - - packages[oldPackageName] = newPackageName - } else if (kind == "FD:") { - val oldFull = tokens[1] - val newFull = tokens[2] - - // Split the qualified field names into their classes and actual names - val splitOld = oldFull.lastIndexOf('/') - val splitNew = newFull.lastIndexOf('/') - require(!(splitOld == -1 || splitNew == -1)) { - ("Field name is invalid, not fully-qualified: " + oldFull - + " -> " + newFull + " in line=" + line) - } - - var oldClassName = inputTransformer.transformClassName(oldFull.substring(0, splitOld)) - var oldFieldName = - inputTransformer.transformFieldName(oldFull.substring(0, splitOld), oldFull.substring(splitOld + 1)) - val newClassName = outputTransformer.transformClassName( - newFull.substring( - 0, - splitNew - ) - ) - var newFieldName = - outputTransformer.transformFieldName(oldFull.substring(0, splitOld), newFull.substring(splitNew + 1)) - - if (reverse) { - oldClassName = newClassName - - val temp = newFieldName - newFieldName = oldFieldName - oldFieldName = temp - } - - val oldEntry = "$oldClassName/$oldFieldName" - require(!(fields.containsKey(oldEntry) && newFieldName != fields[oldEntry])) { - ("Duplicate field mapping: " + oldEntry + " ->" + newFieldName - + " but already mapped to " + fields[oldEntry] + " in line=" + line) - } - - reflectableFields[oldEntry] = newFieldName - fields[oldEntry] = newFieldName - } else if (kind == "MD:") { - val oldFull = tokens[1] - val newFull = tokens[3] - - // Split the qualified field names into their classes and actual names - val splitOld = oldFull.lastIndexOf('/') - val splitNew = newFull.lastIndexOf('/') - require(!(splitOld == -1 || splitNew == -1)) { - ("Field name is invalid, not fully-qualified: " + oldFull - + " -> " + newFull + " in line=" + line) - } - - var oldClassName = inputTransformer.transformClassName(oldFull.substring(0, splitOld)) - var oldMethodName = inputTransformer.transformMethodName( - oldFull.substring(0, splitOld), oldFull.substring(splitOld + 1), - tokens[2] - ) - var oldMethodDescriptor = inputTransformer.transformMethodDescriptor(tokens[2]) - val newClassName = outputTransformer.transformClassName( - newFull.substring( - 0, - splitNew - ) - ) - var newMethodName = outputTransformer.transformMethodName( - oldFull.substring(0, splitOld), newFull.substring(splitNew + 1), - tokens[2] - ) - val newMethodDescriptor = - outputTransformer.transformMethodDescriptor(tokens[4]) - - if (reverse) { - oldClassName = newClassName - oldMethodDescriptor = newMethodDescriptor - - val temp = newMethodName - newMethodName = oldMethodName - oldMethodName = temp - } - - val oldEntry = "$oldClassName/$oldMethodName $oldMethodDescriptor" - require(!(methods.containsKey(oldEntry) && newMethodName != methods[oldEntry])) { - ("Duplicate method mapping: " + oldEntry + " ->" + newMethodName - + " but already mapped to " + methods[oldEntry] + " in line=" + line) - } - - methods[oldEntry] = newMethodName - } else { - throw IllegalArgumentException("Unable to parse srg file, unrecognized mapping type in line=$line") - } - } - -} diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt index ad3fa3ae..2ce0a283 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt @@ -1,6 +1,6 @@ package com.github.jikoo.openinv.specialsource -import net.md_5.specialsource.NodeType +import net.md_5.specialsource.JarMapping import net.md_5.specialsource.RemapperProcessor import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter @@ -11,8 +11,8 @@ import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.LdcInsnNode import org.objectweb.asm.tree.MethodInsnNode -open class ReflectionPreprocessor( - private val jarMapping: ReflectionJarMapping +class ReflectionPreprocessor( + private val jarMapping: JarMapping ) : RemapperProcessor(null, jarMapping, null) { override fun process(classReader: ClassReader): ByteArray { @@ -35,7 +35,7 @@ open class ReflectionPreprocessor( return classWriter.toByteArray() } - open fun remapGetDeclaredField(insn: AbstractInsnNode) { + private fun remapGetDeclaredField(insn: AbstractInsnNode) { val mi = insn as MethodInsnNode if (mi.owner != "java/lang/Class" || mi.name != "getDeclaredField" || mi.desc != "(Ljava/lang/String;)Ljava/lang/reflect/Field;") { @@ -64,14 +64,70 @@ open class ReflectionPreprocessor( } val className = (ldcClass.cst as Type).internalName - val newName = jarMapping.tryClimb(jarMapping.reflectableFields, NodeType.FIELD, className, fieldName, null, 0) + val newName = lookup(className, fieldName) this.logR("Remapping $className/$fieldName -> $newName") if (newName != null) { ldcField.cst = newName } } - open fun remapClassForName(insn: AbstractInsnNode) { + private fun lookup(className: String, fieldName: String): String? { + val key = "$className/$fieldName" + + // Try direct lookup first. + val direct = jarMapping.fields[key] + if (direct != null) { + return direct + } + + // Fall through to indirect lookup in case the mappings are from Proguard. + for (entry in jarMapping.fields) { + // This is a safe index check. We know the string starts with but does not equal the key. + if (!entry.key.startsWith(key) || entry.key[key.length] != '/') { + // Not the same class or field, name shares a common prefix. + continue + } + + // Ensure class is a valid class. This may prevent some collisions due to poor naming. + if (isValidClass(entry.key.substring(key.length + 1))) { + return entry.value + } + } + + // No match. + return null + } + + private fun isValidClass(type: String): Boolean { + if (type.length < 3 || type[0] != 'L' || type[type.length - 1] != ';') { + // Type should be L; + return false + } + val realType = type.substring( + if (type[1] == '[') { + 2 + } else { + 1 + }, + type.length - 1 + ) + + if (jarMapping.classes[realType] != null) { + return true + } + + if (realType.length == 1) { + when (realType[0]) { + 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 'V' -> { + return true + } + } + } + + return false + } + + private fun remapClassForName(insn: AbstractInsnNode) { val mi = insn as MethodInsnNode if (mi.owner != "java/lang/Class" || mi.name != "forName" || mi.desc != "(Ljava/lang/String;)Ljava/lang/Class;") { @@ -96,7 +152,7 @@ open class ReflectionPreprocessor( } } - protected fun logR(message: String) { + private fun logR(message: String) { if (this.debug) { println("[ReflectionRemapper] $message") } From f4ed616fb82ac46553cc2f3359086faa4cafe2ab Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 9 Mar 2025 10:34:18 -0400 Subject: [PATCH 277/340] Consider any class name format valid This technically allows edge cases where the package (or class, for classes with no package) can start with L and be incorrectly matched, but that feels like a case of play stupid games, win stupid prizes. Classes should be packaged, and package names should be lower case. Requires a much less hacky change to JarMapping to optimize. --- .../specialsource/ReflectionPreprocessor.kt | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt index 2ce0a283..9f6d6893 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt @@ -88,8 +88,9 @@ class ReflectionPreprocessor( continue } - // Ensure class is a valid class. This may prevent some collisions due to poor naming. - if (isValidClass(entry.key.substring(key.length + 1))) { + val type = entry.key.substring(key.length + 1) + // Type should be L[?; + if (type.length >= 3 && type[0] == 'L' && type[type.length - 1] == ';') { return entry.value } } @@ -98,35 +99,6 @@ class ReflectionPreprocessor( return null } - private fun isValidClass(type: String): Boolean { - if (type.length < 3 || type[0] != 'L' || type[type.length - 1] != ';') { - // Type should be L; - return false - } - val realType = type.substring( - if (type[1] == '[') { - 2 - } else { - 1 - }, - type.length - 1 - ) - - if (jarMapping.classes[realType] != null) { - return true - } - - if (realType.length == 1) { - when (realType[0]) { - 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 'V' -> { - return true - } - } - } - - return false - } - private fun remapClassForName(insn: AbstractInsnNode) { val mi = insn as MethodInsnNode From 268e91007c43e11fbc0a100c1e57f9a127ba4901 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 9 Mar 2025 12:55:39 -0400 Subject: [PATCH 278/340] Fix array types In my defense, reading is really hard --- .../jikoo/openinv/specialsource/ReflectionPreprocessor.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt index 9f6d6893..5ddf4fbc 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt @@ -15,6 +15,10 @@ class ReflectionPreprocessor( private val jarMapping: JarMapping ) : RemapperProcessor(null, jarMapping, null) { + companion object { + private val JVM_TYPE_PATTERN = "^\\[*L.+;\$".toRegex() + } + override fun process(classReader: ClassReader): ByteArray { val classNode = ClassNode() classReader.accept(classNode, 0) @@ -89,8 +93,8 @@ class ReflectionPreprocessor( } val type = entry.key.substring(key.length + 1) - // Type should be L[?; - if (type.length >= 3 && type[0] == 'L' && type[type.length - 1] == ';') { + // Type should be [*L; + if (type.length >= 3 && type.matches(JVM_TYPE_PATTERN)) { return entry.value } } From 7a2cef50ecb97a0038af78a9d4f76a93d52052cd Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 11 Mar 2025 10:37:32 -0400 Subject: [PATCH 279/340] Update SpecialSource to 1.11.5 (#295) --- buildSrc/build.gradle.kts | 2 +- .../github/jikoo/openinv/SpigotReobfTask.kt | 6 +- .../specialsource/ReflectionPreprocessor.kt | 137 ------------------ gradle/libs.versions.toml | 2 +- 4 files changed, 5 insertions(+), 142 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 753a6634..2fbab782 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -9,5 +9,5 @@ repositories { dependencies { val libs = project.extensions.getByType(VersionCatalogsExtension::class.java).named("libs") - implementation(variantOf(libs.findLibrary("specialsource").orElseThrow()) { classifier("shaded") }) + implementation(libs.findLibrary("specialsource").orElseThrow()) } diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt index 79bc6813..f2db2c0b 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt @@ -1,9 +1,9 @@ package com.github.jikoo.openinv -import com.github.jikoo.openinv.specialsource.ReflectionPreprocessor import net.md_5.specialsource.Jar import net.md_5.specialsource.JarMapping import net.md_5.specialsource.JarRemapper +import net.md_5.specialsource.RemapperProcessor import net.md_5.specialsource.provider.JarProvider import net.md_5.specialsource.provider.JointProvider import org.gradle.api.file.RegularFileProperty @@ -68,10 +68,10 @@ abstract class SpigotReobfTask: org.gradle.api.tasks.bundling.Jar() { inheritance.add(JarProvider(inputJar)) // Remap reflective access. - val preprocessor = ReflectionPreprocessor(jarMapping) + val preprocessor = RemapperProcessor(null, jarMapping, null) val remapper = JarRemapper(preprocessor, jarMapping, null) - remapper.remapJar(inputJar, output) + remapper.remapJar(inputJar, output, emptySet()) serverJar.close() inputJar.close() diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt deleted file mode 100644 index 5ddf4fbc..00000000 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/specialsource/ReflectionPreprocessor.kt +++ /dev/null @@ -1,137 +0,0 @@ -package com.github.jikoo.openinv.specialsource - -import net.md_5.specialsource.JarMapping -import net.md_5.specialsource.RemapperProcessor -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Opcodes -import org.objectweb.asm.Type -import org.objectweb.asm.tree.AbstractInsnNode -import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.tree.LdcInsnNode -import org.objectweb.asm.tree.MethodInsnNode - -class ReflectionPreprocessor( - private val jarMapping: JarMapping -) : RemapperProcessor(null, jarMapping, null) { - - companion object { - private val JVM_TYPE_PATTERN = "^\\[*L.+;\$".toRegex() - } - - override fun process(classReader: ClassReader): ByteArray { - val classNode = ClassNode() - classReader.accept(classNode, 0) - - for (methodNode in classNode.methods) { - var insn = methodNode.instructions.first - while (insn != null) { - when (insn.opcode) { - Opcodes.INVOKEVIRTUAL -> this.remapGetDeclaredField(insn) - Opcodes.INVOKESTATIC -> this.remapClassForName(insn) - } - insn = insn.next - } - } - - val classWriter = ClassWriter(0) - classNode.accept(classWriter) - return classWriter.toByteArray() - } - - private fun remapGetDeclaredField(insn: AbstractInsnNode) { - val mi = insn as MethodInsnNode - - if (mi.owner != "java/lang/Class" || mi.name != "getDeclaredField" || mi.desc != "(Ljava/lang/String;)Ljava/lang/reflect/Field;") { - return - } - - this.logR("Found getDeclaredField!") - if (insn.previous == null || insn.previous.opcode != Opcodes.LDC) { - this.logR("- not constant field; skipping, prev=" + insn.getPrevious()) - return - } - val ldcField = insn.getPrevious() as LdcInsnNode - if (ldcField.cst !is String) { - this.logR("- not field string; skipping: ${ldcField.cst}") - return - } - val fieldName = ldcField.cst as String - if (ldcField.previous == null || ldcField.previous.opcode != Opcodes.LDC) { - this.logR("- not constant class; skipping: field=$fieldName") - return - } - val ldcClass = ldcField.previous as LdcInsnNode - if (ldcClass.cst !is Type) { - this.logR("- not class type; skipping: field=${ldcClass.cst}, class=${ldcClass.cst}") - return - } - - val className = (ldcClass.cst as Type).internalName - val newName = lookup(className, fieldName) - this.logR("Remapping $className/$fieldName -> $newName") - if (newName != null) { - ldcField.cst = newName - } - } - - private fun lookup(className: String, fieldName: String): String? { - val key = "$className/$fieldName" - - // Try direct lookup first. - val direct = jarMapping.fields[key] - if (direct != null) { - return direct - } - - // Fall through to indirect lookup in case the mappings are from Proguard. - for (entry in jarMapping.fields) { - // This is a safe index check. We know the string starts with but does not equal the key. - if (!entry.key.startsWith(key) || entry.key[key.length] != '/') { - // Not the same class or field, name shares a common prefix. - continue - } - - val type = entry.key.substring(key.length + 1) - // Type should be [*L; - if (type.length >= 3 && type.matches(JVM_TYPE_PATTERN)) { - return entry.value - } - } - - // No match. - return null - } - - private fun remapClassForName(insn: AbstractInsnNode) { - val mi = insn as MethodInsnNode - - if (mi.owner != "java/lang/Class" || mi.name != "forName" || mi.desc != "(Ljava/lang/String;)Ljava/lang/Class;") { - return - } - this.logR("Found Class forName!") - if (insn.getPrevious() == null || insn.getPrevious().opcode != Opcodes.LDC) { - this.logR("- not constant field; skipping, prev=${insn.previous}") - return - } - val ldcClassName = insn.getPrevious() as LdcInsnNode - if (ldcClassName.cst !is String) { - this.logR("- not field string; skipping: " + ldcClassName.cst) - return - } - - val className = ldcClassName.cst as String - val newName = jarMapping.classes[className.replace('.', '/')] - this.logR("Remapping $className -> $newName") - if (newName != null) { - ldcClassName.cst = newName.replace('/', '.') - } - } - - private fun logR(message: String) { - if (this.debug) { - println("[ReflectionRemapper] $message") - } - } - -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 18861484..95c0c944 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] spigotapi = "1.21.4-R0.1-SNAPSHOT" -specialsource = "1.11.4" +specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2" paperweight = "2.0.0-beta.14" From ada819b30aac7c76a9d563fa048dd6a6950fa501 Mon Sep 17 00:00:00 2001 From: mooviies Date: Wed, 19 Mar 2025 12:08:13 -0400 Subject: [PATCH 280/340] Fix JitPack API build (#296) --- api/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 0731f2f3..9b2665ad 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -8,6 +8,7 @@ publishing { create("jitpack") { groupId = "com.github.Jikoo.OpenInv" artifactId = "openinvapi" + from(components["java"]) } } } From b89f34915e8c25ffd5806296fc76b909b725a6ca Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 21 Mar 2025 17:27:54 -0400 Subject: [PATCH 281/340] Hack around JitPack building Spigot and publishing it, but we don't talk about that --- jitpack.yml | 2 +- settings.gradle.kts | 50 +++++++++++++++++++++++---------------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/jitpack.yml b/jitpack.yml index bd3dacf9..4130c60e 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -3,4 +3,4 @@ before_install: - sdk install java 21-tem - sdk use java 21-tem install: - - ./gradlew :openinvapi:publishJitpackPublicationToMavenLocal + - ./gradlew -Djitpack=true :openinvapi:publishJitpackPublicationToMavenLocal diff --git a/settings.gradle.kts b/settings.gradle.kts index d6fdd75a..e3e638e7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,31 +3,33 @@ rootProject.name = "openinvparent" include(":openinvapi") project(":openinvapi").projectDir = file("api") -val addons = listOf( - "togglepersist" -) -for (addon in addons) { - include(":addon$addon") - val proj = project(":addon$addon") - proj.projectDir = file("addon/$addon") - proj.name = "openinv$addon" -} +if (!java.lang.Boolean.getBoolean("jitpack")) { + val addons = listOf( + "togglepersist" + ) + for (addon in addons) { + include(":addon$addon") + val proj = project(":addon$addon") + proj.projectDir = file("addon/$addon") + proj.name = "openinv$addon" + } -include(":openinvcommon") -project(":openinvcommon").projectDir = file("common") + include(":openinvcommon") + project(":openinvcommon").projectDir = file("common") -val internals = listOf( - "common", - "paper1_21_3", - "paper1_21_1", - "spigot" -) -for (internal in internals) { - include(":openinvadapter$internal") - project(":openinvadapter$internal").projectDir = file("internal/$internal") -} + val internals = listOf( + "common", + "paper1_21_3", + "paper1_21_1", + "spigot" + ) + for (internal in internals) { + include(":openinvadapter$internal") + project(":openinvadapter$internal").projectDir = file("internal/$internal") + } -include(":resource-pack") + include(":resource-pack") -include(":plugin") -project(":plugin").name = "openinvplugin" + include(":plugin") + project(":plugin").name = "openinvplugin" +} From 0c8c364c45223ce0760e73908eb5d4862a14cf45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:33:47 +0000 Subject: [PATCH 282/340] Bump org.spigotmc:spigot-api (#298) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 95c0c944..7a357113 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -spigotapi = "1.21.4-R0.1-SNAPSHOT" +spigotapi = "1.21.5-R0.1-SNAPSHOT" specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2" From 05ac5c92c8a858858b640e982d139e78564ac40f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:34:11 +0000 Subject: [PATCH 283/340] Bump io.papermc.paperweight.userdev from 2.0.0-beta.14 to 2.0.0-beta.16 (#297) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a357113..7e9d5f3e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ spigotapi = "1.21.5-R0.1-SNAPSHOT" specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2" -paperweight = "2.0.0-beta.14" +paperweight = "2.0.0-beta.16" shadow = "8.3.6" folia-scheduler-wrapper = "v0.0.3" From ad06b46b37373ddb545acd605e579ac0db6eca4f Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 25 Apr 2025 11:47:20 -0400 Subject: [PATCH 284/340] Update to 1.21.5 (#300) --- .../jikoo/openinv/BuildToolsValueSource.kt | 4 - internal/common/build.gradle.kts | 2 +- .../common/container/OpenInventory.java | 90 ++-- .../bukkit/OpenDummyPlayerInventory.java | 12 +- .../container/bukkit/OpenPlayerInventory.java | 93 ++-- .../common/container/menu/OpenChestMenu.java | 56 ++- .../container/menu/OpenInventoryMenu.java | 8 +- .../container/slot/ContentEquipment.java | 40 +- .../common/container/slot/ContentOffHand.java | 6 +- .../slot/placeholder/PlaceholderLoader.java | 24 +- .../placeholder/PlaceholderLoaderBase.java | 24 +- .../internal/common/player/OpenPlayer.java | 21 +- .../internal/common/player/PlayerManager.java | 38 +- internal/paper1_21_1/build.gradle.kts | 1 + .../paper1_21_1/InternalAccessor.java | 2 +- .../paper1_21_1/container/OpenInventory.java | 2 +- .../paper1_21_1/player/PlayerManager.java | 3 +- internal/paper1_21_3/build.gradle.kts | 1 + .../paper1_21_3/InternalAccessor.java | 2 +- .../NumericDataPlaceholderLoader.java | 27 +- internal/paper1_21_4/build.gradle.kts | 26 + .../paper1_21_4/InternalAccessor.java | 80 +++ .../paper1_21_4/container/OpenEnderChest.java | 23 + .../paper1_21_4/container/OpenInventory.java | 177 +++++++ .../container/bukkit/OpenPlayerInventory.java | 223 +++++++++ .../bukkit/OpenPlayerInventorySelf.java | 26 + .../container/menu/OpenChestMenu.java | 464 ++++++++++++++++++ .../container/menu/OpenEnderChestMenu.java | 59 +++ .../container/menu/OpenInventoryMenu.java | 262 ++++++++++ .../container/slot/ContentEquipment.java | 80 +++ .../container/slot/ContentOffHand.java | 52 ++ .../slot/placeholder/CustomModelBase.java | 41 ++ .../CustomModelPlaceholderLoader.java | 13 + .../paper1_21_4/player/OpenPlayer.java | 35 ++ .../paper1_21_4/player/PlayerManager.java | 70 +++ internal/spigot/build.gradle.kts | 4 +- .../container/bukkit/OpenPlayerInventory.java | 188 +++++++ .../container/slot/ContentEquipment.java | 116 +++++ .../reobf/container/slot/ContentOffHand.java | 49 ++ .../internal/reobf/player/PlayerManager.java | 6 +- plugin/build.gradle.kts | 1 + .../lishid/openinv/util/InternalAccessor.java | 12 +- .../openinv-legibility-pack/pack.mcmeta | 6 +- settings.gradle.kts | 1 + 44 files changed, 2257 insertions(+), 213 deletions(-) create mode 100644 internal/paper1_21_4/build.gradle.kts create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/InternalAccessor.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventorySelf.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentEquipment.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelBase.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelPlaceholderLoader.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java create mode 100644 internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java create mode 100644 internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java create mode 100644 internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt index bb73f8fb..d89987bc 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt @@ -4,7 +4,6 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.provider.ValueSource import org.gradle.api.provider.ValueSourceParameters -import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.process.ExecOperations import java.io.File import java.net.URI @@ -16,9 +15,6 @@ abstract class BuildToolsValueSource: ValueSource val workingDir: DirectoryProperty diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 565c251d..4ccfd675 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) - paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") } val spigot = tasks.register("spigotRelocations") { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index ae7bdcf2..0992192e 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -16,6 +16,7 @@ import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import com.lishid.openinv.internal.common.player.PlayerManager; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -38,13 +39,15 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; public class OpenInventory implements Container, InternalOwned, ISpecialPlayerInventory { - private final List slots; + protected final List slots; private final int size; - private ServerPlayer owner; + protected ServerPlayer owner; private int maxStackSize = 99; private CraftInventory bukkitEntity; public List transaction = new ArrayList<>(); @@ -60,15 +63,14 @@ public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { setupSlots(); } - private void setupSlots() { + protected void setupSlots() { // Top of inventory: Regular contents. int nextIndex = addMainInventory(); // If inventory is expected size, we can arrange slots to be pretty. Inventory ownerInv = owner.getInventory(); - if (ownerInv.items.size() == 36 - && ownerInv.armor.size() == 4 - && ownerInv.offhand.size() == 1 + if (ownerInv.getNonEquipmentItems().size() == 36 + && Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5 && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) { // Armor slots: Bottom left. addArmor(36); @@ -95,7 +97,7 @@ private void setupSlots() { } private int addMainInventory() { - int listSize = owner.getInventory().items.size(); + int listSize = owner.getInventory().getNonEquipmentItems().size(); // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, // so everything else needs to move up a row. int hotbarDiff = listSize - 9; @@ -113,7 +115,7 @@ private int addMainInventory() { slots.set(localIndex, new ContentList(owner, invIndex, type) { @Override public void setHolder(@NotNull ServerPlayer holder) { - items = holder.getInventory().items; + items = holder.getInventory().getNonEquipmentItems(); } }); } @@ -121,50 +123,36 @@ public void setHolder(@NotNull ServerPlayer holder) { } private int addArmor(int startIndex) { - int listSize = owner.getInventory().armor.size(); - - for (int i = 0; i < listSize; ++i) { - // Armor slots go bottom to top; boots are slot 0, helmet is slot 3. - // Since we have to display horizontally due to space restrictions, - // making the left side the "top" is more user-friendly. - int armorIndex; - EquipmentSlot slot; - switch (i) { - case 3 -> { - armorIndex = 0; - slot = EquipmentSlot.FEET; - } - case 2 -> { - armorIndex = 1; - slot = EquipmentSlot.LEGS; - } - case 1 -> { - armorIndex = 2; - slot = EquipmentSlot.CHEST; - } - case 0 -> { - armorIndex = 3; - slot = EquipmentSlot.HEAD; - } - default -> { - // In the event that new armor slots are added, they can be placed at the end. - armorIndex = i; - slot = EquipmentSlot.MAINHAND; - } + // Armor slots go bottom to top; boots are first and helmet is last. + // Since we have to display horizontally due to space restrictions, + // making the left side the "top" is more user-friendly. + EquipmentSlot[] sorted = Inventory.EQUIPMENT_SLOT_MAPPING.int2ObjectEntrySet() + .stream() + .sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey)) + .map(Map.Entry::getValue) + .toArray(EquipmentSlot[]::new); + int localIndex = 0; + for (int i = sorted.length - 1; i >= 0; --i) { + // Skip off-hand, handled separately. + if (sorted[i] == EquipmentSlot.OFFHAND) { + continue; } - slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot)); + slots.set(startIndex + localIndex, new ContentEquipment(owner, sorted[i])); + ++localIndex; } - return startIndex + listSize; + return startIndex + localIndex; } private int addOffHand(int startIndex) { - int listSize = owner.getInventory().offhand.size(); - for (int localIndex = 0; localIndex < listSize; ++localIndex) { - slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex)); + // No off-hand? + if (!Inventory.EQUIPMENT_SLOT_MAPPING.containsValue(EquipmentSlot.OFFHAND)) { + return startIndex; } - return startIndex + listSize; + + slots.set(startIndex, new ContentOffHand(owner)); + return startIndex + 1; } private int addCrafting(int startIndex, boolean pretty) { @@ -280,22 +268,22 @@ public boolean isEmpty() { } @Override - public ItemStack getItem(int index) { + public @NotNull ItemStack getItem(int index) { return slots.get(index).get(); } @Override - public ItemStack removeItem(int index, int amount) { + public @NotNull ItemStack removeItem(int index, int amount) { return slots.get(index).removePartial(amount); } @Override - public ItemStack removeItemNoUpdate(int index) { + public @NotNull ItemStack removeItemNoUpdate(int index) { return slots.get(index).remove(); } @Override - public void setItem(int index, ItemStack itemStack) { + public void setItem(int index, @NotNull ItemStack itemStack) { slots.get(index).set(itemStack); } @@ -327,17 +315,17 @@ public boolean stillValid(@NotNull Player player) { } @Override - public void onOpen(CraftHumanEntity viewer) { + public void onOpen(@NotNull CraftHumanEntity viewer) { transaction.add(viewer); } @Override - public void onClose(CraftHumanEntity viewer) { + public void onClose(@NotNull CraftHumanEntity viewer) { transaction.remove(viewer); } @Override - public List getViewers() { + public @NotNull List getViewers() { return transaction; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java index a98aca1f..c6bb8f10 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java @@ -22,12 +22,12 @@ public HumanEntity getHolder() { } @Override - public @NotNull ItemStack[] getArmorContents() { + public @NotNull ItemStack @NotNull [] getArmorContents() { return new ItemStack[4]; } @Override - public @NotNull ItemStack[] getExtraContents() { + public @NotNull ItemStack @NotNull [] getExtraContents() { return new ItemStack[4]; } @@ -57,17 +57,17 @@ public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) { } @Override - public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) { - return null; + public @NotNull ItemStack getItem(@NotNull EquipmentSlot slot) { + return new ItemStack(Material.AIR); } @Override - public void setArmorContents(@Nullable ItemStack[] items) { + public void setArmorContents(ItemStack @NotNull [] items) { } @Override - public void setExtraContents(@Nullable ItemStack[] items) { + public void setExtraContents(ItemStack @NotNull [] items) { } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java index 434e0ca3..ed126f71 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java @@ -14,6 +14,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { public OpenPlayerInventory(@NotNull OpenInventory inventory) { @@ -26,7 +28,7 @@ public OpenPlayerInventory(@NotNull OpenInventory inventory) { } @Override - public ItemStack[] getContents() { + public ItemStack @NotNull [] getContents() { return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents()); } @@ -46,13 +48,13 @@ public void setContents(ItemStack[] items) { } @Override - public ItemStack[] getStorageContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().items); + public ItemStack @NotNull [] getStorageContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getNonEquipmentItems()); } @Override public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { - NonNullList list = getInventory().getOwnerHandle().getInventory().items; + NonNullList list = getInventory().getOwnerHandle().getInventory().getNonEquipmentItems(); int size = list.size(); Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); for (int index = 0; index < items.length; ++index) { @@ -71,103 +73,104 @@ public void setStorageContents(ItemStack[] items) throws IllegalArgumentExceptio } @Override - public @NotNull ItemStack[] getArmorContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor); + public @NotNull ItemStack @NotNull [] getArmorContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmorContents()); } @Override - public void setArmorContents(@Nullable ItemStack[] items) { - NonNullList list = getInventory().getOwnerHandle().getInventory().armor; - int size = list.size(); + public void setArmorContents(ItemStack @NotNull [] items) { + int size = Inventory.EQUIPMENT_SLOTS_SORTED_BY_INDEX.length; Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); for (int index = 0; index < items.length; ++index) { - list.set(index, CraftItemStack.asNMSCopy(items[index])); + getInventory().getOwnerHandle().getInventory().equipment.set( + Inventory.EQUIPMENT_SLOTS_SORTED_BY_INDEX[index], + CraftItemStack.asNMSCopy(items[index])); } } @Override - public @NotNull ItemStack[] getExtraContents() { - return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand); + public @NotNull ItemStack @NotNull [] getExtraContents() { + return asCraftMirror(List.of(getInventory().getOwnerHandle().getInventory().equipment.get(EquipmentSlot.OFFHAND))); } @Override - public void setExtraContents(@Nullable ItemStack[] items) { - NonNullList list = getInventory().getOwnerHandle().getInventory().offhand; - int size = list.size(); - Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); - for (int index = 0; index < items.length; ++index) { - list.set(index, CraftItemStack.asNMSCopy(items[index])); + public void setExtraContents(ItemStack @NotNull [] items) { + Preconditions.checkArgument(items.length <= 1, "items.length must be <= 1"); + for (ItemStack item : items) { + getInventory().getOwnerHandle().getInventory().equipment.set(EquipmentSlot.OFFHAND, CraftItemStack.asNMSCopy(item)); } } @Override - public @Nullable ItemStack getHelmet() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.HEAD.getIndex())); + public @NotNull ItemStack getHelmet() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment + .get(EquipmentSlot.HEAD)); } @Override public void setHelmet(@Nullable ItemStack helmet) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet)); + getInventory().getOwnerHandle().getInventory().equipment + .set(EquipmentSlot.HEAD, CraftItemStack.asNMSCopy(helmet)); } @Override - public @Nullable ItemStack getChestplate() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.HEAD.getIndex())); + public @NotNull ItemStack getChestplate() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment + .get(EquipmentSlot.CHEST)); } @Override public void setChestplate(@Nullable ItemStack chestplate) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate)); + getInventory().getOwnerHandle().getInventory().equipment + .set(EquipmentSlot.CHEST, CraftItemStack.asNMSCopy(chestplate)); } @Override - public @Nullable ItemStack getLeggings() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.LEGS.getIndex())); + public @NotNull ItemStack getLeggings() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment + .get(EquipmentSlot.LEGS)); } @Override public void setLeggings(@Nullable ItemStack leggings) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings)); + getInventory().getOwnerHandle().getInventory().equipment + .set(EquipmentSlot.LEGS, CraftItemStack.asNMSCopy(leggings)); } @Override - public @Nullable ItemStack getBoots() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() - .getArmor(EquipmentSlot.FEET.getIndex())); + public @NotNull ItemStack getBoots() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment + .get(EquipmentSlot.FEET)); } @Override public void setBoots(@Nullable ItemStack boots) { - getInventory().getOwnerHandle().getInventory().armor - .set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots)); + getInventory().getOwnerHandle().getInventory().equipment + .set(EquipmentSlot.FEET, CraftItemStack.asNMSCopy(boots)); } @Override public @NotNull ItemStack getItemInMainHand() { Inventory internal = getInventory().getOwnerHandle().getInventory(); - return CraftItemStack.asCraftMirror(internal.getItem(internal.selected)); + return CraftItemStack.asCraftMirror(internal.getSelectedItem()); } @Override public void setItemInMainHand(@Nullable ItemStack item) { Inventory internal = getInventory().getOwnerHandle().getInventory(); - internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item)); + internal.setSelectedItem(CraftItemStack.asNMSCopy(item)); } @Override public @NotNull ItemStack getItemInOffHand() { - return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst()); + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment + .get(EquipmentSlot.OFFHAND)); } @Override public void setItemInOffHand(@Nullable ItemStack item) { - getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); + getInventory().getOwnerHandle().getInventory().equipment + .set(EquipmentSlot.OFFHAND, CraftItemStack.asNMSCopy(item)); } @Deprecated @@ -185,17 +188,17 @@ public void setItemInHand(@Nullable ItemStack stack) { @Override public int getHeldItemSlot() { Inventory internal = getInventory().getOwnerHandle().getInventory(); - return internal.items.size() - 9 + internal.selected; + return internal.getNonEquipmentItems().size() - 9 + internal.getSelectedSlot(); } @Override public void setHeldItemSlot(int slot) { slot %= 9; - getInventory().getOwnerHandle().getInventory().selected = slot; + getInventory().getOwnerHandle().getInventory().setSelectedSlot(slot); } @Override - public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { + public @NotNull ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { return switch (slot) { case HAND -> getItemInMainHand(); case OFF_HAND -> getItemInOffHand(); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index 9c73170c..d4c99e2c 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -8,6 +8,7 @@ import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.network.HashedStack; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; @@ -19,6 +20,7 @@ import net.minecraft.world.inventory.ContainerSynchronizer; import net.minecraft.world.inventory.DataSlot; import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.RemoteSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.craftbukkit.inventory.CraftInventoryView; @@ -47,12 +49,12 @@ public abstract class OpenChestMenu, Inventory> bukkitEntity; // Syncher fields - private @Nullable ContainerSynchronizer synchronizer; - private final List dataSlots = new ArrayList<>(); - private final IntList remoteDataSlots = new IntArrayList(); - private final List containerListeners = new ArrayList<>(); - private ItemStack remoteCarried = ItemStack.EMPTY; - private boolean suppressRemoteUpdates; + protected @Nullable ContainerSynchronizer synchronizer; + protected final List dataSlots = new ArrayList<>(); + protected final IntList remoteDataSlots = new IntArrayList(); + protected final List containerListeners = new ArrayList<>(); + private RemoteSlot remoteCarried = RemoteSlot.PLACEHOLDER; + protected boolean suppressRemoteUpdates; protected OpenChestMenu( @NotNull MenuType type, @@ -304,7 +306,7 @@ public boolean stillValid(@NotNull Player player) { slot.index = this.slots.size(); this.slots.add(slot); this.lastSlots.add(ItemStack.EMPTY); - this.remoteSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(this.synchronizer != null ? this.synchronizer.createSlot() : RemoteSlot.PLACEHOLDER); return slot; } @@ -333,32 +335,38 @@ public void addSlotListener(@NotNull ContainerListener containerListener) { @Override public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { this.synchronizer = containerSynchronizer; + this.remoteCarried = synchronizer.createSlot(); + this.remoteSlots.replaceAll(slot -> synchronizer.createSlot()); this.sendAllDataToRemote(); } @Override public void sendAllDataToRemote() { + List contentsCopy = new ArrayList<>(); for (int index = 0; index < slots.size(); ++index) { Slot slot = slots.get(index); - this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); + ItemStack itemStack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + contentsCopy.add(itemStack); + this.remoteSlots.get(index).force(itemStack); } - remoteCarried = getCarried().copy(); + remoteCarried.force(getCarried()); for (int index = 0; index < this.dataSlots.size(); ++index) { this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); } if (this.synchronizer != null) { - this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); + this.synchronizer.sendInitialData(this, contentsCopy, this.getCarried().copy(), this.remoteDataSlots.toIntArray()); } } @Override public void broadcastCarriedItem() { - this.remoteCarried = this.getCarried().copy(); + ItemStack carried = this.getCarried(); + this.remoteCarried.force(carried); if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); + this.synchronizer.sendCarriedChange(this, carried.copy()); } } @@ -427,12 +435,11 @@ private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { if (!this.suppressRemoteUpdates) { - ItemStack itemStack1 = this.remoteSlots.get(i); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemstack2 = supplier.get(); - this.remoteSlots.set(i, itemstack2); + RemoteSlot slot = this.remoteSlots.get(i); + if (!slot.matches(itemStack)) { + slot.force(itemStack); if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(this, i, itemstack2); + this.synchronizer.sendSlotChange(this, i, supplier.get()); } } } @@ -451,17 +458,20 @@ private void synchronizeDataSlotToRemote(int index, int value) { } private void synchronizeCarriedToRemote() { - if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); + if (!this.suppressRemoteUpdates) { + ItemStack carried = this.getCarried(); + if (!this.remoteCarried.matches(carried)) { + this.remoteCarried.force(carried); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, carried.copy()); + } } } } @Override - public void setRemoteCarried(ItemStack itemstack) { - this.remoteCarried = itemstack.copy(); + public void setRemoteCarried(@NotNull HashedStack stack) { + this.remoteCarried.receive(stack); } @Override diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java index afe70bad..c5bf5f88 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java @@ -35,7 +35,7 @@ private static MenuType getMenuType(OpenInventory inventory, ServerPl int size = inventory.getContainerSize(); // Disallow duplicate access to own main inventory contents. if (inventory.getOwnerHandle().equals(viewer)) { - size -= viewer.getInventory().items.size(); + size -= viewer.getInventory().getNonEquipmentItems().size(); size = ((int) Math.ceil(size / 9.0)) * 9; } @@ -44,7 +44,7 @@ private static MenuType getMenuType(OpenInventory inventory, ServerPl @Override protected void preSlotSetup() { - offset = ownContainer ? viewer.getInventory().items.size() : 0; + offset = ownContainer ? viewer.getInventory().getNonEquipmentItems().size() : 0; } @Override @@ -221,7 +221,7 @@ public int countSlots() { // If this is gear, try to move it to the correct slot first. case OFFHAND, FEET, LEGS, CHEST, HEAD -> { // Locate the correct slot in the contents following the main inventory. - for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { + for (int extra = container.getOwnerHandle().getInventory().getNonEquipmentItems().size() - offset; extra < topSize; ++extra) { Slot extraSlot = getSlot(extra); if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot && equipSlot.getEquipmentSlot() == equipmentSlot) { @@ -244,7 +244,7 @@ public int countSlots() { } } else { // If we didn't move to a gear slot, try to move to a main inventory slot. - if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) { + if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().getNonEquipmentItems().size(), true)) { return ItemStack.EMPTY; } } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java index 71d9f826..a066dc5a 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java @@ -3,6 +3,8 @@ import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.EntityEquipment; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; @@ -12,13 +14,14 @@ /** * A slot for equipment that displays placeholders if empty. */ -public class ContentEquipment extends ContentList { +public class ContentEquipment implements Content { + private EntityEquipment equipment; private final ItemStack placeholder; private final EquipmentSlot equipmentSlot; - public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { - super(holder, index, InventoryType.SlotType.ARMOR); + public ContentEquipment(ServerPlayer holder, EquipmentSlot equipmentSlot) { + setHolder(holder); placeholder = switch (equipmentSlot) { case HEAD -> Placeholders.emptyHelmet; case CHEST -> Placeholders.emptyChestplate; @@ -31,7 +34,31 @@ public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentS @Override public void setHolder(@NotNull ServerPlayer holder) { - this.items = holder.getInventory().armor; + this.equipment = holder.getInventory().equipment; + } + + @Override + public ItemStack get() { + return equipment.get(equipmentSlot); + } + + @Override + public ItemStack remove() { + return equipment.set(equipmentSlot, ItemStack.EMPTY); + } + + @Override + public ItemStack removePartial(int amount) { + ItemStack current = get(); + if (!current.isEmpty() && amount > 0) { + return current.split(amount); + } + return ItemStack.EMPTY; + } + + @Override + public void set(ItemStack itemStack) { + equipment.set(equipmentSlot, itemStack); } @Override @@ -39,6 +66,11 @@ public Slot asSlot(Container container, int slot, int x, int y) { return new SlotEquipment(container, slot, x, y); } + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.ARMOR; + } + public class SlotEquipment extends SlotPlaceholder { private ServerPlayer viewer; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java index 465472df..ae56f9a1 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java @@ -17,13 +17,13 @@ public class ContentOffHand extends ContentEquipment { private ServerPlayer holder; - public ContentOffHand(ServerPlayer holder, int localIndex) { - super(holder, localIndex, EquipmentSlot.OFFHAND); + public ContentOffHand(ServerPlayer holder) { + super(holder, EquipmentSlot.OFFHAND); } @Override public void setHolder(@NotNull ServerPlayer holder) { - this.items = holder.getInventory().offhand; + super.setHolder(holder); this.holder = holder; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java index 1fdafd73..c859e7d6 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoader.java @@ -1,18 +1,40 @@ package com.lishid.openinv.internal.common.container.slot.placeholder; import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.CustomModelData; +import net.minecraft.world.item.component.DyedItemColor; +import net.minecraft.world.item.component.TooltipDisplay; +import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashSet; import java.util.List; public class PlaceholderLoader extends PlaceholderLoaderBase { private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of()); + private static final TooltipDisplay HIDE_TOOLTIP = new TooltipDisplay(true, new LinkedHashSet<>()); @Override - protected void addModelData(ItemStack itemStack) { + protected @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception { + return TagParser.parseCompoundFully(itemText); + } + + @Override + protected void addModelData(@NotNull ItemStack itemStack) { itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); } + @Override + protected void hideTooltip(@NotNull ItemStack itemStack) { + itemStack.set(DataComponents.TOOLTIP_DISPLAY, HIDE_TOOLTIP); + } + + @Override + protected DyedItemColor getDye(int rgb) { + return new DyedItemColor(rgb); + } + } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java index c32a88ce..c054688d 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java @@ -5,9 +5,7 @@ import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.TagParser; import net.minecraft.network.chat.Component; -import net.minecraft.util.Unit; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -63,7 +61,7 @@ public void load(@Nullable ConfigurationSection section) throws Exception { Placeholders.BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", Placeholders.BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); } - private static @NotNull ItemStack parse( + private @NotNull ItemStack parse( @Nullable ConfigurationSection section, @NotNull String path, @NotNull ItemStack defaultStack) throws Exception { @@ -77,12 +75,18 @@ public void load(@Nullable ConfigurationSection section) throws Exception { return defaultStack; } - CompoundTag compoundTag = TagParser.parseTag(itemText); + CompoundTag compoundTag = parseTag(itemText); Optional parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag); return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); } - protected abstract void addModelData(ItemStack itemStack); + protected abstract @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception; + + protected abstract void addModelData(@NotNull ItemStack itemStack); + + protected abstract void hideTooltip(@NotNull ItemStack itemStack); + + protected abstract DyedItemColor getDye(int rgb); protected @NotNull ItemStack defaultCraftingOutput() { // Crafting table: "Crafting" @@ -106,7 +110,7 @@ public void load(@Nullable ConfigurationSection section) throws Exception { new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); addModelData(itemStack); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + hideTooltip(itemStack); return itemStack; } @@ -122,9 +126,9 @@ public void load(@Nullable ConfigurationSection section) throws Exception { protected @NotNull ItemStack getEmptyArmor(@NotNull ItemLike item) { // Inventory-background-grey-ish leather armor with no tooltip ItemStack itemStack = new ItemStack(item); - DyedItemColor color = new DyedItemColor(0xC8C8C8, false); + DyedItemColor color = getDye(0xC8C8C8); itemStack.set(DataComponents.DYED_COLOR, color); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + hideTooltip(itemStack); addModelData(itemStack); return itemStack; } @@ -147,7 +151,7 @@ public void load(@Nullable ConfigurationSection section) throws Exception { new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + hideTooltip(itemStack); addModelData(itemStack); return itemStack; } @@ -155,7 +159,7 @@ public void load(@Nullable ConfigurationSection section) throws Exception { protected @NotNull ItemStack defaultNotSlot() { // White pane with no tooltip ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE); - itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + hideTooltip(itemStack); addModelData(itemStack); return itemStack; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 54a770f7..43632e6e 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -15,6 +15,7 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import java.nio.file.Files; import java.nio.file.Path; @@ -30,7 +31,8 @@ public class OpenPlayer extends CraftPlayer { * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) */ - private static final Set RESET_TAGS = Set.of( + @Unmodifiable + protected static final Set RESET_TAGS = Set.of( // Entity#saveWithoutId(CompoundTag) "CustomName", "CustomNameVisible", @@ -43,6 +45,7 @@ public class OpenPlayer extends CraftPlayer { "Passengers", // ServerPlayer#addAdditionalSaveData(CompoundTag) // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + // As of 1.21.5 only ender_pearls tag still needs to be reset. Rest are for backwards compatibility. "warden_spawn_tracker", "enteredNetherPosition", "SpawnX", @@ -57,18 +60,23 @@ public class OpenPlayer extends CraftPlayer { "ShoulderEntityLeft", "ShoulderEntityRight", "LastDeathLocation", - "current_explosion_impact_pos", + "current_explosion_impact_pos", // Unnecessary in 1.21.5 // LivingEntity#addAdditionalSaveData(CompoundTag) "active_effects", "SleepingX", "SleepingY", "SleepingZ", - "Brain" + "Brain", + "last_hurt_by_player", + "last_hurt_by_player_memory_time", + "last_hurt_by_mob", + "ticks_since_last_hurt_by_mob", + "equipment" ); private final PlayerManager manager; - OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { + protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { super(server, entity); this.manager = manager; } @@ -111,7 +119,7 @@ public void saveData() { } @Contract("null -> new") - private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { return new CompoundTag(); } @@ -120,8 +128,7 @@ public void saveData() { oldData = oldData.copy(); // Remove vanilla/server data that is not written every time. - oldData.getAllKeys() - .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + oldData.keySet().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit") || key.startsWith("Paper") && key.length() > 5); return oldData; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index 9ea2063c..e63ff119 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -34,13 +34,14 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; +import java.util.Optional; import java.util.UUID; import java.util.logging.Logger; public class PlayerManager implements com.lishid.openinv.internal.PlayerManager { protected final @NotNull Logger logger; - private @Nullable Field bukkitEntity; + protected @Nullable Field bukkitEntity; public PlayerManager(@NotNull Logger logger) { this.logger = logger; @@ -154,23 +155,28 @@ boolean loadData(@NotNull ServerPlayer player) { return true; } - private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { // See PlayerList#placeNewPlayer World bukkitWorld; - if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + Optional msbs = loadedData.getLong("WorldUUIDMost"); + Optional lsbs = loadedData.getLong("WorldUUIDLeast"); + if (msbs.isPresent() && lsbs.isPresent()) { // Modern Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); - } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { - // Legacy Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); + bukkitWorld = Bukkit.getServer().getWorld(new UUID(msbs.get(), lsbs.get())); } else { - // Vanilla player data. - DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(logger::warning) - .map(player.server::getLevel) - // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); - return; + Optional worldName = loadedData.getString("world"); + if (worldName.isPresent()) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(worldName.get()); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(logger::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); + return; + } } if (bukkitWorld == null) { spawnInDefaultWorld(player); @@ -179,9 +185,11 @@ private void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loade player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } - private void spawnInDefaultWorld(ServerPlayer player) { + protected void spawnInDefaultWorld(ServerPlayer player) { ServerLevel level = player.server.getLevel(Level.OVERWORLD); if (level != null) { + // Adjust player to default spawn (in keeping with Paper handling) when world not found. + player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F); player.spawnIn(level); } else { logger.warning("Tried to load player with invalid world when no fallback was available!"); diff --git a/internal/paper1_21_1/build.gradle.kts b/internal/paper1_21_1/build.gradle.kts index 6a61a347..3a24a450 100644 --- a/internal/paper1_21_1/build.gradle.kts +++ b/internal/paper1_21_1/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_4")) implementation(project(":openinvadapterpaper1_21_3")) paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java index 078f50fc..100bf31f 100644 --- a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/InternalAccessor.java @@ -6,10 +6,10 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.common.container.AnySilentContainer; -import com.lishid.openinv.internal.common.container.OpenEnderChest; import com.lishid.openinv.internal.paper1_21_1.container.OpenInventory; import com.lishid.openinv.internal.paper1_21_1.container.slot.placeholder.PlaceholderLoader; import com.lishid.openinv.internal.paper1_21_1.player.PlayerManager; +import com.lishid.openinv.internal.paper1_21_4.container.OpenEnderChest; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.world.Container; import org.bukkit.configuration.ConfigurationSection; diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java index c1fff7d6..1368d89a 100644 --- a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/OpenInventory.java @@ -6,7 +6,7 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory { +public class OpenInventory extends com.lishid.openinv.internal.paper1_21_4.container.OpenInventory { public OpenInventory(@NotNull Player bukkitPlayer) { super(bukkitPlayer); diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java index 3ad69f3b..60c1a04b 100644 --- a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java @@ -11,7 +11,8 @@ import java.util.logging.Logger; -public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { +public class PlayerManager extends com.lishid.openinv.internal.paper1_21_4.player.PlayerManager { + public PlayerManager(@NotNull Logger logger) { super(logger); } diff --git a/internal/paper1_21_3/build.gradle.kts b/internal/paper1_21_3/build.gradle.kts index b1a13522..91d2ae33 100644 --- a/internal/paper1_21_3/build.gradle.kts +++ b/internal/paper1_21_3/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_4")) paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT") } diff --git a/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java index 6ec24db0..e72aeb0a 100644 --- a/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java +++ b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/InternalAccessor.java @@ -8,7 +8,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor { +public class InternalAccessor extends com.lishid.openinv.internal.paper1_21_4.InternalAccessor { public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { super(logger, lang); diff --git a/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java index c77fd676..d19552a4 100644 --- a/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java +++ b/internal/paper1_21_3/src/main/java/com/lishid/openinv/internal/paper1_21_3/container/slot/placeholder/NumericDataPlaceholderLoader.java @@ -1,31 +1,12 @@ package com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder; -import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoaderBase; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.Registries; -import net.minecraft.util.Unit; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; +import com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder.CustomModelBase; import net.minecraft.world.item.component.CustomModelData; -import net.minecraft.world.level.block.entity.BannerPattern; -import net.minecraft.world.level.block.entity.BannerPatternLayers; -import net.minecraft.world.level.block.entity.BannerPatterns; -import org.bukkit.craftbukkit.CraftRegistry; -import org.jetbrains.annotations.NotNull; -import java.util.List; +public class NumericDataPlaceholderLoader extends CustomModelBase { -public class NumericDataPlaceholderLoader extends PlaceholderLoaderBase { - - private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999); - - @Override - protected void addModelData(ItemStack itemStack) { - itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA); + public NumericDataPlaceholderLoader() { + super(new CustomModelData(9999)); } } diff --git a/internal/paper1_21_4/build.gradle.kts b/internal/paper1_21_4/build.gradle.kts new file mode 100644 index 00000000..d83eda69 --- /dev/null +++ b/internal/paper1_21_4/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { + id -> id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + + paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/InternalAccessor.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/InternalAccessor.java new file mode 100644 index 00000000..98b09873 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/InternalAccessor.java @@ -0,0 +1,80 @@ +package com.lishid.openinv.internal.paper1_21_4; + +import com.lishid.openinv.internal.Accessor; +import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.ISpecialEnderChest; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.common.container.AnySilentContainer; +import com.lishid.openinv.internal.paper1_21_4.container.OpenEnderChest; +import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory; +import com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder.CustomModelPlaceholderLoader; +import com.lishid.openinv.internal.paper1_21_4.player.PlayerManager; +import com.lishid.openinv.util.lang.LanguageManager; +import net.minecraft.world.Container; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InternalAccessor implements Accessor { + + protected final @NotNull Logger logger; + private final @NotNull PlayerManager manager; + private final @NotNull AnySilentContainer anySilentContainer; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + manager = new PlayerManager(logger); + anySilentContainer = new AnySilentContainer(logger, lang); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { + return anySilentContainer; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new OpenInventory(player); + } + + @Override + public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) { + return new OpenEnderChest(player); + } + + @Override + public @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class clazz) { + if (!(bukkitInventory instanceof CraftInventory craftInventory)) { + return null; + } + Container container = craftInventory.getInventory(); + if (clazz.isInstance(container)) { + return clazz.cast(container); + } + return null; + } + + @Override + public void reload(@NotNull ConfigurationSection config) { + ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); + try { + // Reset placeholders to defaults and try to load configuration. + new CustomModelPlaceholderLoader().load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java new file mode 100644 index 00000000..dfc4f35f --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java @@ -0,0 +1,23 @@ +package com.lishid.openinv.internal.paper1_21_4.container; + +import com.lishid.openinv.internal.paper1_21_4.container.menu.OpenEnderChestMenu; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.inventory.AbstractContainerMenu; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenEnderChest extends com.lishid.openinv.internal.common.container.OpenEnderChest{ + + public OpenEnderChest(@NotNull Player player) { + super(player); + } + + public @Nullable AbstractContainerMenu createMenu(net.minecraft.world.entity.player.Player player, int i, boolean viewOnly) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly); + } + return null; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java new file mode 100644 index 00000000..f6a0d266 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java @@ -0,0 +1,177 @@ +package com.lishid.openinv.internal.paper1_21_4.container; + +import com.lishid.openinv.internal.common.container.slot.ContentCrafting; +import com.lishid.openinv.internal.common.container.slot.ContentCursor; +import com.lishid.openinv.internal.common.container.slot.ContentDrop; +import com.lishid.openinv.internal.common.container.slot.ContentList; +import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; +import com.lishid.openinv.internal.paper1_21_4.container.menu.OpenInventoryMenu; +import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentEquipment; +import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentOffHand; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory { + + public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { + super(bukkitPlayer); + } + + @Override + protected void setupSlots() { + // Top of inventory: Regular contents. + int nextIndex = addMainInventory(); + + // If inventory is expected size, we can arrange slots to be pretty. + Inventory ownerInv = owner.getInventory(); + if (ownerInv.items.size() == 36 + && ownerInv.armor.size() == 4 + && ownerInv.offhand.size() == 1 + && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) { + // Armor slots: Bottom left. + addArmor(36); + // Off-hand: Below chestplate. + addOffHand(46); + // Drop slot: Bottom right. + slots.set(53, new ContentDrop(owner)); + // Cursor slot: Above drop. + slots.set(44, new ContentCursor(owner)); + + // Crafting is displayed in the bottom right corner. + // As we're using the pretty view, this is a 3x2. + addCrafting(41, true); + return; + } + + // Otherwise we'll just add elements linearly. + nextIndex = addArmor(nextIndex); + nextIndex = addOffHand(nextIndex); + nextIndex = addCrafting(nextIndex, false); + slots.set(nextIndex, new ContentCursor(owner)); + // Drop slot last. + slots.set(slots.size() - 1, new ContentDrop(owner)); + } + + private int addMainInventory() { + int listSize = owner.getInventory().items.size(); + // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, + // so everything else needs to move up a row. + int hotbarDiff = listSize - 9; + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + InventoryType.SlotType type; + int invIndex; + if (localIndex < hotbarDiff) { + invIndex = localIndex + 9; + type = InventoryType.SlotType.CONTAINER; + } else { + type = InventoryType.SlotType.QUICKBAR; + invIndex = localIndex - hotbarDiff; + } + + slots.set(localIndex, new ContentList(owner, invIndex, type) { + @Override + public void setHolder(@NotNull ServerPlayer holder) { + items = holder.getInventory().items; + } + }); + } + return listSize; + } + + private int addArmor(int startIndex) { + int listSize = owner.getInventory().armor.size(); + + for (int i = 0; i < listSize; ++i) { + // Armor slots go bottom to top; boots are slot 0, helmet is slot 3. + // Since we have to display horizontally due to space restrictions, + // making the left side the "top" is more user-friendly. + int armorIndex; + EquipmentSlot slot; + switch (i) { + case 3 -> { + armorIndex = 0; + slot = EquipmentSlot.FEET; + } + case 2 -> { + armorIndex = 1; + slot = EquipmentSlot.LEGS; + } + case 1 -> { + armorIndex = 2; + slot = EquipmentSlot.CHEST; + } + case 0 -> { + armorIndex = 3; + slot = EquipmentSlot.HEAD; + } + default -> { + // In the event that new armor slots are added, they can be placed at the end. + armorIndex = i; + slot = EquipmentSlot.MAINHAND; + } + } + + slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot)); + } + + return startIndex + listSize; + } + + private int addOffHand(int startIndex) { + int listSize = owner.getInventory().offhand.size(); + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex)); + } + return startIndex + listSize; + } + + private int addCrafting(int startIndex, boolean pretty) { + int listSize = owner.inventoryMenu.getCraftSlots().getContents().size(); + pretty &= listSize == 4; + + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + // Pretty display is a 2x2 rather than linear. + // If index is in top row, grid is not 2x2, or pretty is disabled, just use current index. + // Otherwise, subtract 2 and add 9 to start in the same position on the next row. + int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); + + slots.set(modIndex, new ContentCrafting(owner, localIndex)); + } + + if (pretty) { + slots.set(startIndex + 2, new ContentViewOnly(owner) { + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + return Placeholders.craftingOutput; + } + }; + } + }); + slots.set(startIndex + 11, getCraftingResult(owner)); + } + + return startIndex + listSize; + } + + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); + } + return null; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java new file mode 100644 index 00000000..b5989e5c --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java @@ -0,0 +1,223 @@ +package com.lishid.openinv.internal.paper1_21_4.container.bukkit; + +import com.google.common.base.Preconditions; +import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { + + public OpenPlayerInventory(@NotNull OpenInventory inventory) { + super(inventory); + } + + @Override + public @NotNull OpenInventory getInventory() { + return (OpenInventory) super.getInventory(); + } + + @Override + public ItemStack @NotNull [] getContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents()); + } + + @Override + public void setContents(ItemStack[] items) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + int size = internal.getContainerSize(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + + for (int index = 0; index < size; ++index) { + if (index < items.length) { + internal.setItem(index, CraftItemStack.asNMSCopy(items[index])); + } else { + internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY); + } + } + } + + @Override + public ItemStack @NotNull [] getStorageContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().items); + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + NonNullList list = getInventory().getOwnerHandle().getInventory().items; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull InventoryType getType() { + return InventoryType.PLAYER; + } + + @Override + public @NotNull Player getHolder() { + return getInventory().getOwner(); + } + + @Override + public @NotNull ItemStack @NotNull [] getArmorContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor); + } + + @Override + public void setArmorContents(ItemStack @NotNull [] items) { + NonNullList list = getInventory().getOwnerHandle().getInventory().armor; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull ItemStack @NotNull [] getExtraContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand); + } + + @Override + public void setExtraContents(ItemStack @NotNull [] items) { + NonNullList list = getInventory().getOwnerHandle().getInventory().offhand; + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull ItemStack getHelmet() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.HEAD.getIndex())); + } + + @Override + public void setHelmet(@Nullable ItemStack helmet) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet)); + } + + @Override + public @NotNull ItemStack getChestplate() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.CHEST.getIndex())); + } + + @Override + public void setChestplate(@Nullable ItemStack chestplate) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate)); + } + + @Override + public @NotNull ItemStack getLeggings() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.LEGS.getIndex())); + } + + @Override + public void setLeggings(@Nullable ItemStack leggings) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings)); + } + + @Override + public @NotNull ItemStack getBoots() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory() + .getArmor(EquipmentSlot.FEET.getIndex())); + } + + @Override + public void setBoots(@Nullable ItemStack boots) { + getInventory().getOwnerHandle().getInventory().armor + .set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots)); + } + + @Override + public @NotNull ItemStack getItemInMainHand() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return CraftItemStack.asCraftMirror(internal.getItem(internal.selected)); + } + + @Override + public void setItemInMainHand(@Nullable ItemStack item) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item)); + } + + @Override + public @NotNull ItemStack getItemInOffHand() { + return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst()); + } + + @Override + public void setItemInOffHand(@Nullable ItemStack item) { + getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); + } + + @Deprecated + @Override + public @NotNull ItemStack getItemInHand() { + return getItemInMainHand(); + } + + @Deprecated + @Override + public void setItemInHand(@Nullable ItemStack stack) { + setItemInMainHand(stack); + } + + @Override + public int getHeldItemSlot() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return internal.items.size() - 9 + internal.selected; + } + + @Override + public void setHeldItemSlot(int slot) { + slot %= 9; + getInventory().getOwnerHandle().getInventory().selected = slot; + } + + @Override + public @NotNull ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { + return switch (slot) { + case HAND -> getItemInMainHand(); + case OFF_HAND -> getItemInOffHand(); + case FEET -> getBoots(); + case LEGS -> getLeggings(); + case CHEST -> getChestplate(); + case HEAD -> getHelmet(); + default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); + }; + } + + @Override + public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) { + switch (slot) { + case HAND -> setItemInMainHand(item); + case OFF_HAND -> setItemInOffHand(item); + case FEET -> setBoots(item); + case LEGS -> setLeggings(item); + case CHEST -> setChestplate(item); + case HEAD -> setHelmet(item); + default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot); + } + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventorySelf.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventorySelf.java new file mode 100644 index 00000000..d6e0ce0b --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventorySelf.java @@ -0,0 +1,26 @@ +package com.lishid.openinv.internal.paper1_21_4.container.bukkit; + +import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class OpenPlayerInventorySelf extends OpenPlayerInventory { + + private final int offset; + + public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) { + super(inventory); + this.offset = offset; + } + + @Override + public ItemStack getItem(int index) { + return super.getItem(offset + index); + } + + @Override + public void setItem(int index, ItemStack item) { + super.setItem(offset + index, item); + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java new file mode 100644 index 00000000..7c4f0f9f --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java @@ -0,0 +1,464 @@ +package com.lishid.openinv.internal.paper1_21_4.container.menu; + +import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.common.container.bukkit.OpenDummyInventory; +import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.ContainerListener; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. + */ +public abstract class OpenChestMenu> + extends AbstractContainerMenu { + + protected static final int BOTTOM_INVENTORY_SIZE = 36; + + protected final T container; + protected final ServerPlayer viewer; + protected final boolean viewOnly; + protected final boolean ownContainer; + protected final int topSize; + private CraftInventoryView, Inventory> bukkitEntity; + // Syncher fields + private @Nullable ContainerSynchronizer synchronizer; + private final List dataSlots = new ArrayList<>(); + private final IntList remoteDataSlots = new IntArrayList(); + private final List containerListeners = new ArrayList<>(); + private ItemStack remoteCarried = ItemStack.EMPTY; + private boolean suppressRemoteUpdates; + + protected OpenChestMenu( + @NotNull MenuType type, + int containerCounter, + @NotNull T container, + @NotNull ServerPlayer viewer, + boolean viewOnly) { + super(type, containerCounter); + this.container = container; + this.viewer = viewer; + this.viewOnly = viewOnly; + ownContainer = container.getOwnerHandle().equals(viewer); + topSize = getTopSize(viewer); + + preSlotSetup(); + + int upperRows = topSize / 9; + // View's upper inventory - our container + for (int row = 0; row < upperRows; ++row) { + for (int col = 0; col < 9; ++col) { + // x and y for client purposes, but hey, we're thorough here. + // Adapted from net.minecraft.world.inventory.ChestMenu + int x = 8 + col * 18; + int y = 18 + row * 18; + int index = row * 9 + col; + + // Guard against weird inventory sizes. + if (index >= container.getContainerSize()) { + addSlot(new SlotViewOnly(container, index, x, y)); + continue; + } + + Slot slot = getUpperSlot(index, x, y); + + addSlot(slot); + } + } + + // View's lower inventory - viewer inventory + int playerInvPad = (upperRows - 4) * 18; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + row * 18 + 103; + addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); + } + } + // Hotbar + for (int col = 0; col < 9; ++col) { + int x = 8 + col * 18; + int y = playerInvPad + 161; + addSlot(new Slot(viewer.getInventory(), col, x, y)); + } + } + + protected void preSlotSetup() {} + + protected @NotNull Slot getUpperSlot(int index, int x, int y) { + Slot slot = new Slot(container, index, x, y); + if (viewOnly) { + return SlotViewOnly.wrap(slot); + } + return slot; + } + + + @Override + public final @NotNull CraftInventoryView, Inventory> getBukkitView() { + if (bukkitEntity == null) { + bukkitEntity = createBukkitEntity(); + } + + return bukkitEntity; + } + + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { + Inventory top; + if (viewOnly) { + top = new OpenDummyInventory(container, container.getBukkitType()); + } else { + top = container.getBukkitInventory(); + } + return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) { + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } + return super.getInventory(rawSlot); + } + + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + return super.convertSlot(rawSlot); + } + + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly) { + return InventoryType.SlotType.OUTSIDE; + } + return super.getSlotType(slot); + } + }; + } + + private int getTopSize(ServerPlayer viewer) { + MenuType menuType = getType(); + if (menuType == MenuType.GENERIC_9x1) { + return 9; + } else if (menuType == MenuType.GENERIC_9x2) { + return 18; + } else if (menuType == MenuType.GENERIC_9x3) { + return 27; + } else if (menuType == MenuType.GENERIC_9x4) { + return 36; + } else if (menuType == MenuType.GENERIC_9x5) { + return 45; + } else if (menuType == MenuType.GENERIC_9x6) { + return 54; + } + // This is a bit gross, but allows us a safe fallthrough. + return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE; + } + + /** + * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake + * slots and respects {@link Slot#hasItem()}. + * + * @param itemStack the stack to quick-move + * @param rangeLow the start of the range of slots that can be moved to, inclusive + * @param rangeHigh the end of the range of slots that can be moved to, exclusive + * @param topDown whether to start at the top of the range or bottom + * @return whether the stack was modified as a result of being quick-moved + */ + @Override + protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { + boolean modified = false; + boolean stackable = itemStack.isStackable(); + Slot firstEmpty = null; + + for (int index = topDown ? rangeHigh - 1 : rangeLow; + !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); + index += topDown ? -1 : 1 + ) { + Slot slot = slots.get(index); + // If the slot cannot be added to, check the next slot. + if (slot.isFake() || !slot.mayPlace(itemStack)) { + continue; + } + + if (slot.hasItem()) { + // If the item isn't stackable, check the next slot. + if (!stackable) { + continue; + } + // Otherwise, add as many as we can from our stack to the slot. + modified |= addToExistingStack(itemStack, slot); + } else { + // If this is the first empty slot, keep track of it for later use. + if (firstEmpty == null) { + firstEmpty = slot; + } + // If the item isn't stackable, we've located the slot we're adding it to, so we're done. + if (!stackable) { + break; + } + } + } + + // If the item hasn't been fully added yet, add as many as we can to the first open slot. + if (!itemStack.isEmpty() && firstEmpty != null) { + firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); + firstEmpty.setChanged(); + modified = true; + } + + return modified; + } + + private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { + ItemStack existing = slot.getItem(); + + // If the items aren't the same, we can't add our item. + if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { + return false; + } + + int max = slot.getMaxStackSize(existing); + int existingCount = existing.getCount(); + + // If the stack is already full, we can't add more. + if (existingCount >= max) { + return false; + } + + int total = existingCount + itemStack.getCount(); + + // If the existing item can accept the entirety of our item, we're done! + if (total <= max) { + itemStack.setCount(0); + existing.setCount(total); + slot.setChanged(); + return true; + } + + // Otherwise, add as many as we can. + itemStack.shrink(max - existingCount); + existing.setCount(max); + slot.setChanged(); + return true; + } + + @Override + public void clicked(int i, int j, @NotNull ClickType clickType, @NotNull Player player) { + if (viewOnly) { + if (clickType == ClickType.QUICK_CRAFT) { + sendAllDataToRemote(); + } + return; + } + super.clicked(i, j, clickType, player); + } + + @Override + public boolean stillValid(@NotNull Player player) { + return true; + } + + // Overrides from here on are purely to modify the sync process to send placeholder items. + @Override + protected @NotNull Slot addSlot(@NotNull Slot slot) { + slot.index = this.slots.size(); + this.slots.add(slot); + this.lastSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(ItemStack.EMPTY); + return slot; + } + + @Override + protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) { + this.dataSlots.add(dataSlot); + this.remoteDataSlots.add(0); + return dataSlot; + } + + @Override + protected void addDataSlots(ContainerData containerData) { + for (int i = 0; i < containerData.getCount(); i++) { + this.addDataSlot(DataSlot.forContainer(containerData, i)); + } + } + + @Override + public void addSlotListener(@NotNull ContainerListener containerListener) { + if (!this.containerListeners.contains(containerListener)) { + this.containerListeners.add(containerListener); + this.broadcastChanges(); + } + } + + @Override + public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { + this.synchronizer = containerSynchronizer; + this.sendAllDataToRemote(); + } + + @Override + public void sendAllDataToRemote() { + for (int index = 0; index < slots.size(); ++index) { + Slot slot = slots.get(index); + this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); + } + + remoteCarried = getCarried().copy(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); + } + + if (this.synchronizer != null) { + this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); + } + } + + @Override + public void broadcastCarriedItem() { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + + @Override + public void removeSlotListener(@NotNull ContainerListener containerListener) { + this.containerListeners.remove(containerListener); + } + + @Override + public void broadcastChanges() { + for (int index = 0; index < this.slots.size(); ++index) { + Slot slot = this.slots.get(index); + ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + Supplier supplier = Suppliers.memoize(itemstack::copy); + this.triggerSlotListeners(index, itemstack, supplier); + this.synchronizeSlotToRemote(index, itemstack, supplier); + } + + this.synchronizeCarriedToRemote(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot dataSlot = this.dataSlots.get(index); + int j = dataSlot.get(); + if (dataSlot.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, j); + } + + this.synchronizeDataSlotToRemote(index, j); + } + } + + @Override + public void broadcastFullState() { + for (int index = 0; index < this.slots.size(); ++index) { + ItemStack itemstack = this.slots.get(index).getItem(); + this.triggerSlotListeners(index, itemstack, itemstack::copy); + } + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot containerproperty = this.dataSlots.get(index); + if (containerproperty.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, containerproperty.get()); + } + } + + this.sendAllDataToRemote(); + } + + private void updateDataSlotListeners(int i, int j) { + for (ContainerListener containerListener : this.containerListeners) { + containerListener.dataChanged(this, i, j); + } + } + + private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + ItemStack itemStack1 = this.lastSlots.get(index); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemStack2 = supplier.get(); + this.lastSlots.set(index, itemStack2); + + for (ContainerListener containerListener : this.containerListeners) { + containerListener.slotChanged(this, index, itemStack2); + } + } + } + + private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { + if (!this.suppressRemoteUpdates) { + ItemStack itemStack1 = this.remoteSlots.get(i); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemstack2 = supplier.get(); + this.remoteSlots.set(i, itemstack2); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, i, itemstack2); + } + } + } + } + + private void synchronizeDataSlotToRemote(int index, int value) { + if (!this.suppressRemoteUpdates) { + int existing = this.remoteDataSlots.getInt(index); + if (existing != value) { + this.remoteDataSlots.set(index, value); + if (this.synchronizer != null) { + this.synchronizer.sendDataChange(this, index, value); + } + } + } + } + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + } + + @Override + public void setRemoteCarried(ItemStack itemstack) { + this.remoteCarried = itemstack.copy(); + } + + @Override + public void suppressRemoteUpdates() { + this.suppressRemoteUpdates = true; + } + + @Override + public void resumeRemoteUpdates() { + this.suppressRemoteUpdates = false; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java new file mode 100644 index 00000000..bf26040f --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java @@ -0,0 +1,59 @@ +package com.lishid.openinv.internal.paper1_21_4.container.menu; + +import com.lishid.openinv.internal.common.container.OpenEnderChest; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class OpenEnderChestMenu extends OpenChestMenu { + + public OpenEnderChestMenu( + @NotNull OpenEnderChest enderChest, + @NotNull ServerPlayer viewer, + int containerId, + boolean viewOnly) { + super( + com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(enderChest.getContainerSize()), + containerId, + enderChest, + viewer, + viewOnly + ); + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) { + if (viewOnly) { + return ItemStack.EMPTY; + } + + // See ChestMenu + Slot slot = this.slots.get(index); + + if (slot.isFake() || !slot.hasItem()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack original = itemStack.copy(); + + if (index < topSize) { + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) { + return ItemStack.EMPTY; + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return original; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java new file mode 100644 index 00000000..136e6b91 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java @@ -0,0 +1,262 @@ +package com.lishid.openinv.internal.paper1_21_4.container.menu; + +import com.google.common.base.Preconditions; +import com.lishid.openinv.internal.common.container.bukkit.OpenDummyPlayerInventory; +import com.lishid.openinv.internal.common.container.slot.ContentDrop; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory; +import com.lishid.openinv.internal.paper1_21_4.container.bukkit.OpenPlayerInventorySelf; +import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentEquipment; +import com.lishid.openinv.util.Permissions; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenInventoryMenu extends OpenChestMenu { + + private int offset; + + public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) { + super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly); + } + + private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + int size = inventory.getContainerSize(); + // Disallow duplicate access to own main inventory contents. + if (inventory.getOwnerHandle().equals(viewer)) { + size -= viewer.getInventory().items.size(); + size = ((int) Math.ceil(size / 9.0)) * 9; + } + + return com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(size); + } + + @Override + protected void preSlotSetup() { + offset = ownContainer ? viewer.getInventory().items.size() : 0; + } + + @Override + protected @NotNull Slot getUpperSlot(int index, int x, int y) { + index += offset; + Slot slot = container.getMenuSlot(index, x, y); + + // If the slot cannot be interacted with there's nothing to configure. + if (slot.getClass().equals(SlotViewOnly.class)) { + return slot; + } + + // Remove drop slot if viewer is not allowed to use it. + if (slot instanceof ContentDrop.SlotDrop + && (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) { + return new SlotViewOnly(container, index, x, y); + } + + if (slot instanceof ContentEquipment.SlotEquipment equipment) { + if (viewOnly) { + return SlotViewOnly.wrap(slot); + } + + Permissions perm = switch (equipment.getEquipmentSlot()) { + case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY; + case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY; + case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY; + case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY; + // Off-hand can hold anything, not just equipment. + default -> null; + }; + + // If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot. + if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) { + equipment.onlyEquipmentFor(container.getOwnerHandle()); + } + + // Equipment slots are a core part of the inventory, so they will always be shown. + return slot; + } + + // When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above). + if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) { + return new SlotViewOnly(container, index, x, y); + } + + if (viewOnly) { + return SlotViewOnly.wrap(slot); + } + + return slot; + } + + @Override + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { + org.bukkit.inventory.Inventory bukkitInventory; + if (viewOnly) { + bukkitInventory = new OpenDummyPlayerInventory(container); + } else if (ownContainer) { + bukkitInventory = new OpenPlayerInventorySelf(container, offset); + } else { + bukkitInventory = container.getBukkitInventory(); + } + + return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) { + @Override + public org.bukkit.inventory.ItemStack getItem(int index) { + if (viewOnly || index < 0) { + return null; + } + + Slot slot = slots.get(index); + return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY); + } + + @Override + public boolean isInTop(int rawSlot) { + return rawSlot < topSize; + } + + @Override + public @Nullable Inventory getInventory(int rawSlot) { + if (viewOnly) { + return null; + } + if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) { + return null; + } + Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, + "Slot %s outside of inventory", rawSlot); + if (rawSlot > topSize) { + return getBottomInventory(); + } + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return null; + } + return getTopInventory(); + } + + @Override + public int convertSlot(int rawSlot) { + if (viewOnly) { + return InventoryView.OUTSIDE; + } + if (rawSlot < 0) { + return rawSlot; + } + if (rawSlot < topSize) { + Slot slot = slots.get(rawSlot); + if (slot.isFake()) { + return InventoryView.OUTSIDE; + } + return rawSlot; + } + + int slot = rawSlot - topSize; + + if (slot >= 27) { + slot -= 27; + } else { + slot += 9; + } + + return slot; + } + + @Override + public @NotNull InventoryType.SlotType getSlotType(int slot) { + if (viewOnly || slot < 0) { + return InventoryType.SlotType.OUTSIDE; + } + if (slot >= topSize) { + slot -= topSize; + if (slot >= 27) { + return InventoryType.SlotType.QUICKBAR; + } + return InventoryType.SlotType.CONTAINER; + } + return OpenInventoryMenu.this.container.getSlotType(offset + slot); + } + + @Override + public int countSlots() { + return topSize + BOTTOM_INVENTORY_SIZE; + } + }; + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) { + if (viewOnly) { + return ItemStack.EMPTY; + } + + // See ChestMenu and InventoryMenu + Slot slot = this.slots.get(index); + + if (!slot.hasItem() || slot.isFake()) { + return ItemStack.EMPTY; + } + + ItemStack itemStack = slot.getItem(); + ItemStack originalStack = itemStack.copy(); + + if (index < topSize) { + // If we're moving top to bottom, do a normal transfer. + if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else { + EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack); + boolean movedGear = switch (equipmentSlot) { + // If this is gear, try to move it to the correct slot first. + case OFFHAND, FEET, LEGS, CHEST, HEAD -> { + // Locate the correct slot in the contents following the main inventory. + for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) { + Slot extraSlot = getSlot(extra); + if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot + && equipSlot.getEquipmentSlot() == equipmentSlot) { + // If we've found a matching slot, try to move to it. + // If this succeeds, even partially, we will not attempt to move to other slots. + // Otherwise, armor is already occupied, so we'll fall through to main inventory. + yield this.moveItemStackTo(itemStack, extra, extra + 1, false); + } + } + yield false; + } + // Non-gear gets no special treatment. + default -> false; + }; + + // If main inventory is not available, there's nowhere else to move. + if (offset != 0) { + if (!movedGear) { + return ItemStack.EMPTY; + } + } else { + // If we didn't move to a gear slot, try to move to a main inventory slot. + if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) { + return ItemStack.EMPTY; + } + } + } + + if (itemStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + return originalStack; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentEquipment.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentEquipment.java new file mode 100644 index 00000000..b5513499 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentEquipment.java @@ -0,0 +1,80 @@ +package com.lishid.openinv.internal.paper1_21_4.container.slot; + +import com.lishid.openinv.internal.common.container.slot.ContentList; +import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot for equipment that displays placeholders if empty. + */ +public class ContentEquipment extends ContentList { + + private final ItemStack placeholder; + private final EquipmentSlot equipmentSlot; + + public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) { + super(holder, index, InventoryType.SlotType.ARMOR); + placeholder = switch (equipmentSlot) { + case HEAD -> Placeholders.emptyHelmet; + case CHEST -> Placeholders.emptyChestplate; + case LEGS -> Placeholders.emptyLeggings; + case FEET -> Placeholders.emptyBoots; + default -> Placeholders.emptyOffHand; + }; + this.equipmentSlot = equipmentSlot; + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.items = holder.getInventory().armor; + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y); + } + + public class SlotEquipment extends SlotPlaceholder { + + private ServerPlayer viewer; + + SlotEquipment(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + ItemStack itemStack = getItem(); + if (!itemStack.isEmpty()) { + return itemStack; + } + return placeholder; + } + + public EquipmentSlot getEquipmentSlot() { + return equipmentSlot; + } + + public void onlyEquipmentFor(ServerPlayer viewer) { + this.viewer = viewer; + } + + @Override + public boolean mayPlace(@NotNull ItemStack itemStack) { + if (viewer == null) { + return true; + } + + return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(itemStack) == equipmentSlot; + } + + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java new file mode 100644 index 00000000..178ebb3a --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java @@ -0,0 +1,52 @@ +package com.lishid.openinv.internal.paper1_21_4.container.slot; + +import com.lishid.openinv.internal.common.player.OpenPlayer; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +/** + * A slot for equipment that updates held items if necessary. + */ +public class ContentOffHand extends ContentEquipment { + + private ServerPlayer holder; + + public ContentOffHand(ServerPlayer holder, int localIndex) { + super(holder, localIndex, EquipmentSlot.OFFHAND); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.items = holder.getInventory().offhand; + this.holder = holder; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.QUICKBAR; + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y) { + @Override + public void setChanged() { + if (OpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { + holder.connection.send( + new ClientboundContainerSetSlotPacket( + holder.inventoryMenu.containerId, + holder.inventoryMenu.incrementStateId(), + InventoryMenu.SHIELD_SLOT, + holder.getOffhandItem())); + } + } + }; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelBase.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelBase.java new file mode 100644 index 00000000..33dd3a21 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelBase.java @@ -0,0 +1,41 @@ +package com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder; + +import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoaderBase; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.util.Unit; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomModelData; +import net.minecraft.world.item.component.DyedItemColor; +import org.jetbrains.annotations.NotNull; + +public class CustomModelBase extends PlaceholderLoaderBase { + + private final @NotNull CustomModelData defaultCustomModelData; + + public CustomModelBase(@NotNull CustomModelData defaultCustomModelData) { + this.defaultCustomModelData = defaultCustomModelData; + } + + @Override + protected @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception { + return TagParser.parseTag(itemText); + } + + @Override + protected void addModelData(@NotNull ItemStack itemStack) { + itemStack.set(DataComponents.CUSTOM_MODEL_DATA, defaultCustomModelData); + } + + @Override + protected void hideTooltip(@NotNull ItemStack itemStack) { + itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); + } + + @Override + protected DyedItemColor getDye(int rgb) { + return new DyedItemColor(rgb, false); + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelPlaceholderLoader.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelPlaceholderLoader.java new file mode 100644 index 00000000..d6c2c0b5 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/placeholder/CustomModelPlaceholderLoader.java @@ -0,0 +1,13 @@ +package com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder; + +import net.minecraft.world.item.component.CustomModelData; + +import java.util.List; + +public class CustomModelPlaceholderLoader extends CustomModelBase { + + public CustomModelPlaceholderLoader() { + super(new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of())); + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java new file mode 100644 index 00000000..5dbe80bf --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java @@ -0,0 +1,35 @@ +package com.lishid.openinv.internal.paper1_21_4.player; + +import com.lishid.openinv.internal.common.player.PlayerManager; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.craftbukkit.CraftServer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenPlayer extends com.lishid.openinv.internal.common.player.OpenPlayer { + + protected OpenPlayer(CraftServer server, ServerPlayer entity, + PlayerManager manager) { + super(server, entity, manager); + } + + @Contract("null -> new") + @Override + protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + if (oldData == null) { + return new CompoundTag(); + } + + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + + return oldData; + } + +} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java new file mode 100644 index 00000000..81d0cc8d --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java @@ -0,0 +1,70 @@ +package com.lishid.openinv.internal.paper1_21_4.player; + +import com.mojang.serialization.Dynamic; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; +import java.util.logging.Logger; + +public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { + + public PlayerManager(@NotNull Logger logger) { + super(logger); + } + + protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) { + // Modern Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast"))); + } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world")); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(logger::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); + return; + } + if (bukkitWorld == null) { + spawnInDefaultWorld(player); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); + } + + @Override + protected void spawnInDefaultWorld(ServerPlayer player) { + ServerLevel level = player.server.getLevel(Level.OVERWORLD); + if (level != null) { + // Adjust player to default spawn (in keeping with Paper handling) when world not found. + player.moveTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F); + player.spawnIn(level); + } else { + logger.warning("Tried to load player with invalid world when no fallback was available!"); + } + } + + protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); + } + +} diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index b531ee4b..73723aea 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -10,9 +10,9 @@ plugins { apply() apply() -val spigotVer = "1.21.4-R0.1-SNAPSHOT" +val spigotVer = "1.21.5-R0.1-SNAPSHOT" // Used by common adapter to relocate Craftbukkit classes to a versioned package. -rootProject.extra["craftbukkitPackage"] = "v1_21_R3" +rootProject.extra["craftbukkitPackage"] = "v1_21_R4" configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java new file mode 100644 index 00000000..fd6d60f9 --- /dev/null +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java @@ -0,0 +1,188 @@ +package com.lishid.openinv.internal.reobf.container.bukkit; + +import com.google.common.base.Preconditions; +import com.lishid.openinv.internal.reobf.container.OpenInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.player.Inventory; +import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { + + public OpenPlayerInventory(@NotNull OpenInventory inventory) { + super(inventory); + } + + @Override + public @NotNull OpenInventory getInventory() { + return (OpenInventory) super.getInventory(); + } + + @Override + public ItemStack @NotNull [] getContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents()); + } + + @Override + public void setContents(ItemStack[] items) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + int size = internal.getContainerSize(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + + for (int index = 0; index < size; ++index) { + if (index < items.length) { + internal.setItem(index, CraftItemStack.asNMSCopy(items[index])); + } else { + internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY); + } + } + } + + @Override + public ItemStack @NotNull [] getStorageContents() { + return asCraftMirror(getInventory().getOwnerHandle().getInventory().getNonEquipmentItems()); + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + NonNullList list = getInventory().getOwnerHandle().getInventory().getNonEquipmentItems(); + int size = list.size(); + Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size); + for (int index = 0; index < items.length; ++index) { + list.set(index, CraftItemStack.asNMSCopy(items[index])); + } + } + + @Override + public @NotNull InventoryType getType() { + return InventoryType.PLAYER; + } + + @Override + public @NotNull Player getHolder() { + return getInventory().getOwner(); + } + + @Override + public @NotNull ItemStack @NotNull [] getArmorContents() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getArmorContents(); + } + + @Override + public void setArmorContents(ItemStack @NotNull [] items) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setArmorContents(items); + } + + @Override + public @NotNull ItemStack @NotNull [] getExtraContents() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getExtraContents(); + } + + @Override + public void setExtraContents(ItemStack @NotNull [] items) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setExtraContents(items); + } + + @Override + public @Nullable ItemStack getHelmet() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getHelmet(); + } + + @Override + public void setHelmet(@Nullable ItemStack helmet) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setHelmet(helmet); + } + + @Override + public @Nullable ItemStack getChestplate() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getChestplate(); + } + + @Override + public void setChestplate(@Nullable ItemStack chestplate) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setChestplate(chestplate); + } + + @Override + public @Nullable ItemStack getLeggings() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getLeggings(); + } + + @Override + public void setLeggings(@Nullable ItemStack leggings) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setLeggings(leggings); + } + + @Override + public @Nullable ItemStack getBoots() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getBoots(); + } + + @Override + public void setBoots(@Nullable ItemStack boots) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setBoots(boots); + } + + @Override + public @NotNull ItemStack getItemInMainHand() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return CraftItemStack.asCraftMirror(internal.getSelectedItem()); + } + + @Override + public void setItemInMainHand(@Nullable ItemStack item) { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + internal.setSelectedItem(CraftItemStack.asNMSCopy(item)); + } + + @Override + public @NotNull ItemStack getItemInOffHand() { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getItemInOffHand(); + } + + @Override + public void setItemInOffHand(@Nullable ItemStack item) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setItemInOffHand(item); + } + + @Deprecated + @Override + public @NotNull ItemStack getItemInHand() { + return getItemInMainHand(); + } + + @Deprecated + @Override + public void setItemInHand(@Nullable ItemStack stack) { + setItemInMainHand(stack); + } + + @Override + public int getHeldItemSlot() { + Inventory internal = getInventory().getOwnerHandle().getInventory(); + return internal.getNonEquipmentItems().size() - 9 + internal.getSelectedSlot(); + } + + @Override + public void setHeldItemSlot(int slot) { + slot %= 9; + getInventory().getOwnerHandle().getInventory().setSelectedSlot(slot); + } + + @Override + public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) { + return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getItem(slot); + } + + @Override + public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) { + getInventory().getOwnerHandle().getBukkitEntity().getInventory().setItem(slot, item); + } + +} diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java new file mode 100644 index 00000000..ca56e548 --- /dev/null +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java @@ -0,0 +1,116 @@ +package com.lishid.openinv.internal.reobf.container.slot; + +import com.lishid.openinv.internal.reobf.container.slot.placeholder.Placeholders; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_21_R4.CraftEquipmentSlot; +import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; + +public class ContentEquipment implements Content { + + private PlayerInventory equipment; + private final ItemStack placeholder; + private final org.bukkit.inventory.EquipmentSlot equipmentSlot; + + public ContentEquipment(ServerPlayer holder, EquipmentSlot equipmentSlot) { + setHolder(holder); + placeholder = switch (equipmentSlot) { + case HEAD -> Placeholders.emptyHelmet; + case CHEST -> Placeholders.emptyChestplate; + case LEGS -> Placeholders.emptyLeggings; + case FEET -> Placeholders.emptyBoots; + default -> Placeholders.emptyOffHand; + }; + this.equipmentSlot = CraftEquipmentSlot.getSlot(equipmentSlot); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + this.equipment = holder.getBukkitEntity().getInventory(); + } + + @Override + public ItemStack get() { + return CraftItemStack.asNMSCopy(equipment.getItem(equipmentSlot)); + } + + @Override + public ItemStack remove() { + org.bukkit.inventory.ItemStack old = equipment.getItem(equipmentSlot); + equipment.setItem(equipmentSlot, null); + return CraftItemStack.asNMSCopy(old); + } + + @Override + public ItemStack removePartial(int amount) { + if (amount <= 0) { + return ItemStack.EMPTY; + } + ItemStack current = get(); + if (current.isEmpty()) { + return ItemStack.EMPTY; + } + ItemStack split = current.split(amount); + set(current); + return split; + } + + @Override + public void set(ItemStack itemStack) { + equipment.setItem(equipmentSlot, CraftItemStack.asCraftMirror(itemStack)); + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y); + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.ARMOR; + } + + public class SlotEquipment extends SlotPlaceholder { + + private ServerPlayer viewer; + + SlotEquipment(Container container, int index, int x, int y) { + super(container, index, x, y); + } + + @Override + public ItemStack getOrDefault() { + ItemStack itemStack = getItem(); + if (!itemStack.isEmpty()) { + return itemStack; + } + return placeholder; + } + + public EquipmentSlot getEquipmentSlot() { + return CraftEquipmentSlot.getNMS(equipmentSlot); + } + + public void onlyEquipmentFor(ServerPlayer viewer) { + this.viewer = viewer; + } + + @Override + public boolean mayPlace(@NotNull ItemStack itemStack) { + if (viewer == null) { + return true; + } + + return equipmentSlot == org.bukkit.inventory.EquipmentSlot.OFF_HAND + || viewer.getEquipmentSlotForItem(itemStack) == CraftEquipmentSlot.getNMS(equipmentSlot); + } + + } + +} diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java new file mode 100644 index 00000000..d7a173b5 --- /dev/null +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java @@ -0,0 +1,49 @@ +package com.lishid.openinv.internal.reobf.container.slot; + +import com.lishid.openinv.internal.reobf.player.OpenPlayer; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; + +public class ContentOffHand extends ContentEquipment { + + private ServerPlayer holder; + + public ContentOffHand(ServerPlayer holder) { + super(holder, EquipmentSlot.OFFHAND); + } + + @Override + public void setHolder(@NotNull ServerPlayer holder) { + super.setHolder(holder); + this.holder = holder; + } + + @Override + public InventoryType.SlotType getSlotType() { + return InventoryType.SlotType.QUICKBAR; + } + + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotEquipment(container, slot, x, y) { + @Override + public void setChanged() { + if (OpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { + holder.connection.send( + new ClientboundContainerSetSlotPacket( + holder.inventoryMenu.containerId, + holder.inventoryMenu.incrementStateId(), + InventoryMenu.SHIELD_SLOT, + holder.getOffhandItem())); + } + } + }; + } + +} diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 38deed4a..213bcf7b 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -19,9 +19,9 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_21_R3.CraftServer; -import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_21_R3.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R4.CraftServer; +import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R4.event.CraftEventFactory; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 8ca6ce9a..2e1cf641 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_4")) implementation(project(":openinvadapterpaper1_21_3")) implementation(project(":openinvadapterpaper1_21_1")) implementation(project(":openinvadapterspigot", configuration = SpigotReobf.ARTIFACT_CONFIG)) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 68c42e0b..64f1d898 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -64,7 +64,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - Version maxSupported = Version.of(1, 21, 4); + Version maxSupported = Version.of(1, 21, 5); Version minSupported = Version.of(1, 21, 1); // Ensure version is in supported range. @@ -84,14 +84,18 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } // Paper or a Paper fork, can use Mojang-mapped internals. - if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.4 + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.5 return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); - } else if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21, 3))) { + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) { // 1.21.4 + return new com.lishid.openinv.internal.paper1_21_4.InternalAccessor(logger, lang); + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21, 2))) { // 1.21.1-1.21.2 placeholder format return new com.lishid.openinv.internal.paper1_21_1.InternalAccessor(logger, lang); } - // 1.21.3 + // 1.21.2, 1.21.3 return new com.lishid.openinv.internal.paper1_21_3.InternalAccessor(logger, lang); } diff --git a/resource-pack/openinv-legibility-pack/pack.mcmeta b/resource-pack/openinv-legibility-pack/pack.mcmeta index 89488360..fd27eeef 100644 --- a/resource-pack/openinv-legibility-pack/pack.mcmeta +++ b/resource-pack/openinv-legibility-pack/pack.mcmeta @@ -1,13 +1,13 @@ { "pack": { "description": "Improve OpenInv's legibility", - "pack_format": 46, - "supported_formats": [ 34, 46 ] + "pack_format": 55, + "supported_formats": [ 34, 55 ] }, "overlays": { "entries": [ { - "formats": [ 44, 46 ], + "formats": [ 44, 55 ], "directory": "openinv_44" }, { diff --git a/settings.gradle.kts b/settings.gradle.kts index e3e638e7..d4a1252c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ if (!java.lang.Boolean.getBoolean("jitpack")) { val internals = listOf( "common", + "paper1_21_4", "paper1_21_3", "paper1_21_1", "spigot" From 8c2ec2121a70aa50749c7c9f96f9260dbe2136a3 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 25 Apr 2025 11:49:43 -0400 Subject: [PATCH 285/340] Bump version to 5.1.10 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 39725644..7f9d342f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.10-SNAPSHOT +version=5.1.10 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 1c40091cfab1512ed9fc66994e01e38ff0e272b8 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 25 Apr 2025 11:50:11 -0400 Subject: [PATCH 286/340] Bump version to 5.1.11-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7f9d342f..d120e1af 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.10 +version=5.1.11-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From e479a5eaa73f328932dd166c5b99be66b0017464 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 13:35:11 -0400 Subject: [PATCH 287/340] Bump softprops/action-gh-release from 2.2.1 to 2.2.2 (#306) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.1 to 2.2.2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.2.1...v2.2.2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.2.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index f8693b6b..37ebc11a 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -20,7 +20,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.2.1 + uses: softprops/action-gh-release@v2.2.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 6647334072da6a86ee0d46f0c0256bc3844ebb2f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 2 May 2025 00:52:42 -0400 Subject: [PATCH 288/340] Fix missed override Closes #307 --- .../openinv/internal/common/container/OpenInventory.java | 2 +- .../internal/paper1_21_4/container/OpenInventory.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 0992192e..4585179b 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -49,7 +49,7 @@ public class OpenInventory implements Container, InternalOwned, IS private final int size; protected ServerPlayer owner; private int maxStackSize = 99; - private CraftInventory bukkitEntity; + protected CraftInventory bukkitEntity; public List transaction = new ArrayList<>(); public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java index f6a0d266..69e3883f 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java @@ -7,6 +7,7 @@ import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; +import com.lishid.openinv.internal.paper1_21_4.container.bukkit.OpenPlayerInventory; import com.lishid.openinv.internal.paper1_21_4.container.menu.OpenInventoryMenu; import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentEquipment; import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentOffHand; @@ -167,6 +168,14 @@ public ItemStack getOrDefault() { return startIndex + listSize; } + @Override + public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { + if (bukkitEntity == null) { + bukkitEntity = new OpenPlayerInventory(this); + } + return bukkitEntity; + } + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); From 8418cb14fe045b131195d3c3ecbe71838409abb5 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 18 May 2025 08:57:31 -0400 Subject: [PATCH 289/340] Add /clearinv and /clearender (#310) Co-authored-by: trard --- .../com/lishid/openinv/util/Permissions.java | 3 + .../main/java/com/lishid/openinv/OpenInv.java | 3 +- .../openinv/command/ClearInvCommand.java | 95 +++++++ .../openinv/command/OpenInvCommand.java | 200 +++++--------- .../openinv/command/PlayerLookupCommand.java | 261 ++++++++++++++++++ .../lishid/openinv/util/InventoryManager.java | 5 +- .../com/lishid/openinv/util/PlayerLoader.java | 12 +- plugin/src/main/resources/locale/en.yml | 1 + plugin/src/main/resources/plugin.yml | 21 +- 9 files changed, 459 insertions(+), 142 deletions(-) create mode 100644 plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java create mode 100644 plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java diff --git a/common/src/main/java/com/lishid/openinv/util/Permissions.java b/common/src/main/java/com/lishid/openinv/util/Permissions.java index dc1a4e74..83d98054 100644 --- a/common/src/main/java/com/lishid/openinv/util/Permissions.java +++ b/common/src/main/java/com/lishid/openinv/util/Permissions.java @@ -36,6 +36,9 @@ public enum Permissions { ENDERCHEST_EDIT_SELF("enderchest.edit.self"), ENDERCHEST_EDIT_OTHER("enderchest.edit.other"), + CLEAR_SELF("clear.self"), + CLEAR_OTHER("clear.other"), + ACCESS_CROSSWORLD("access.crossworld"), ACCESS_OFFLINE("access.offline"), ACCESS_ONLINE("access.online"), diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index a06b2492..06278971 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -16,6 +16,7 @@ package com.lishid.openinv; +import com.lishid.openinv.command.ClearInvCommand; import com.lishid.openinv.command.ContainerSettingCommand; import com.lishid.openinv.command.OpenInvCommand; import com.lishid.openinv.command.SearchContainerCommand; @@ -48,7 +49,6 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -153,6 +153,7 @@ private void registerCommands() { this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); + this.setCommandExecutor(new ClearInvCommand(this, config, inventoryManager, languageManager, playerLoader), "clearinv", "clearender"); ContainerSettingCommand settingCommand = new ContainerSettingCommand(languageManager); for (PlayerToggle toggle : PlayerToggles.get()) { diff --git a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java new file mode 100644 index 00000000..21b1fd8d --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java @@ -0,0 +1,95 @@ +package com.lishid.openinv.command; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.util.InventoryManager; +import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.PlayerLoader; +import com.lishid.openinv.util.config.Config; +import com.lishid.openinv.util.lang.LanguageManager; +import com.lishid.openinv.util.lang.Replacement; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.logging.Level; + +public class ClearInvCommand extends PlayerLookupCommand { + + private final @NotNull InventoryManager manager; + + public ClearInvCommand( + @NotNull OpenInv plugin, + @NotNull Config config, + @NotNull InventoryManager manager, + @NotNull LanguageManager lang, + @NotNull PlayerLoader playerLoader + ) { + super(plugin, lang, config, playerLoader); + this.manager = manager; + } + + @Override + protected boolean isAccessInventory(@NotNull Command command) { + return command.getName().equals("clearinv"); + } + + @Override + protected @Nullable String getTargetIdentifer( + @NotNull CommandSender sender, + @Nullable String argument, + boolean accessInv + ) { + if (argument != null) { + return argument; + } + if (sender instanceof Player player) { + return player.getUniqueId().toString(); + } + return null; + } + + @Override + protected @Nullable OfflinePlayer getTarget(@NotNull String identifier) { + return playerLoader.matchExact(identifier); + } + + @Override + protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv) { + if (onlineTarget.equals(sender)) { + return !Permissions.CLEAR_SELF.hasPermission(sender); + } + return !Permissions.CLEAR_OTHER.hasPermission(sender); + } + + @Override + protected void handle( + @NotNull CommandSender sender, + @NotNull Player onlineTarget, + boolean accessInv, + @NotNull String @NotNull [] args + ) { + // Create the inventory + final ISpecialInventory inv; + try { + inv = accessInv ? manager.getInventory(onlineTarget) : manager.getEnderChest(onlineTarget); + } catch (Exception e) { + lang.sendMessage(sender, "messages.error.commandException"); + plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); + return; + } + + // Clear the inventory + inv.getBukkitInventory().clear(); + manager.save(onlineTarget.getUniqueId()); + lang.sendMessage( + sender, + "messages.info.inventoryCleared", + new Replacement("%target%", onlineTarget.getDisplayName()) + ); + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index b5f4e4b0..b90a3e46 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -18,114 +18,83 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.util.AccessEqualMode; import com.lishid.openinv.util.InventoryManager; import com.lishid.openinv.util.Permissions; import com.lishid.openinv.util.PlayerLoader; -import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.config.Config; import com.lishid.openinv.util.lang.LanguageManager; -import com.lishid.openinv.util.lang.Replacement; -import me.nahu.scheduler.wrapper.runnable.WrappedRunnable; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; -import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.StringJoiner; +import java.util.WeakHashMap; import java.util.logging.Level; -public class OpenInvCommand implements TabExecutor { +public class OpenInvCommand extends PlayerLookupCommand { - private final @NotNull OpenInv plugin; - private final @NotNull Config config; private final @NotNull InventoryManager manager; - private final @NotNull LanguageManager lang; - private final @NotNull PlayerLoader playerLoader; - private final HashMap openInvHistory = new HashMap<>(); - private final HashMap openEnderHistory = new HashMap<>(); + private final Map openInvHistory = new WeakHashMap<>(); + private final Map openEnderHistory = new WeakHashMap<>(); public OpenInvCommand( - @NotNull OpenInv plugin, - @NotNull Config config, - @NotNull InventoryManager manager, - @NotNull LanguageManager lang, - @NotNull PlayerLoader playerLoader) { - this.plugin = plugin; - this.config = config; + @NotNull OpenInv plugin, + @NotNull Config config, + @NotNull InventoryManager manager, + @NotNull LanguageManager lang, + @NotNull PlayerLoader playerLoader + ) { + super(plugin, lang, config, playerLoader); this.manager = manager; - this.lang = lang; - this.playerLoader = playerLoader; } @Override - public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, @NotNull final String[] args) { - boolean openInv = command.getName().equals("openinv"); + protected boolean isAccessInventory(@NotNull Command command) { + return command.getName().equals("openinv"); + } - if (openInv && args.length > 0 && (args[0].equalsIgnoreCase("help") || args[0].equals("?"))) { + @Override + protected @Nullable String getTargetIdentifer( + @NotNull CommandSender sender, + @Nullable String argument, + boolean accessInv + ) { + // /openinv help + if (accessInv && argument != null && (argument.equalsIgnoreCase("help") || argument.equals("?"))) { this.showHelp(sender); - return true; + return null; } + // Command is player-only. if (!(sender instanceof Player player)) { lang.sendMessage(sender, "messages.error.consoleUnsupported"); - return true; + return null; } - String noArgValue; - if (config.doesNoArgsOpenSelf()) { - noArgValue = player.getUniqueId().toString(); - } else { - // History management - noArgValue = (openInv ? this.openInvHistory : this.openEnderHistory).get(player); - - if (noArgValue == null || noArgValue.isEmpty()) { - noArgValue = player.getUniqueId().toString(); - (openInv ? this.openInvHistory : this.openEnderHistory).put(player, noArgValue); + // Use fallthrough for no name provided. + if (argument == null) { + if (config.doesNoArgsOpenSelf()) { + return player.getUniqueId().toString(); } + return (accessInv ? this.openInvHistory : this.openEnderHistory) + .computeIfAbsent(player, localPlayer -> localPlayer.getUniqueId().toString()); } - final String name; - - if (args.length < 1) { - name = noArgValue; - } else { - name = args[0]; + if (!config.doesNoArgsOpenSelf()) { + // History management + (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, argument); } - new WrappedRunnable() { - @Override - public void run() { - final OfflinePlayer offlinePlayer = playerLoader.match(name); - - if (offlinePlayer == null || (!offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline())) { - lang.sendMessage(player, "messages.error.invalidPlayer"); - return; - } - - new WrappedRunnable() { - @Override - public void run() { - if (!player.isOnline()) { - return; - } - OpenInvCommand.this.openInventory(player, offlinePlayer, openInv); - } - }.runTask(OpenInvCommand.this.plugin); - - } - }.runTaskAsynchronously(this.plugin); - - return true; + return argument; } - private void showHelp(final CommandSender sender) { + private void showHelp(@NotNull CommandSender sender) { // Get registered commands for (String commandName : plugin.getDescription().getCommands().keySet()) { PluginCommand command = plugin.getCommand(commandName); @@ -153,80 +122,48 @@ private void showHelp(final CommandSender sender) { } } - private void openInventory(final Player player, final OfflinePlayer target, boolean openinv) { - Player onlineTarget; - boolean online = target.isOnline(); - - if (!online) { - if (!config.isOfflineDisabled() && Permissions.ACCESS_OFFLINE.hasPermission(player)) { - // Try loading the player's data - onlineTarget = playerLoader.load(target); - } else { - lang.sendMessage(player, "messages.error.permissionPlayerOffline"); - return; - } - } else { - if (Permissions.ACCESS_ONLINE.hasPermission(player)) { - onlineTarget = target.getPlayer(); - } else { - lang.sendMessage(player, "messages.error.permissionPlayerOnline"); - return; - } - } - - if (onlineTarget == null) { - lang.sendMessage(player, "messages.error.invalidPlayer"); - return; - } + @Override + protected @Nullable OfflinePlayer getTarget(@NotNull String identifier) { + return playerLoader.match(identifier); + } - // Permissions checks - if (onlineTarget.equals(player)) { + @Override + protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv) { + if (onlineTarget.equals(sender)) { // Permission for opening own inventory. - if (!(openinv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(player)) { - lang.sendMessage(player, "messages.error.permissionOpenSelf"); - return; + if (!(accessInv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(sender)) { + lang.sendMessage(sender, "messages.error.permissionOpenSelf"); + return true; } } else { // Permission for opening others' inventories. - if (!(openinv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(player)) { - lang.sendMessage(player, "messages.error.permissionOpenOther"); - return; - } - - // Protected check - for (int level = 4; level > 0; --level) { - String permission = "openinv.access.level." + level; - if (onlineTarget.hasPermission(permission) - && (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY)) { - lang.sendMessage( - player, - "messages.error.permissionExempt", - new Replacement("%target%", onlineTarget.getDisplayName())); - return; - } - } - - // Crossworld check - if (!Permissions.ACCESS_CROSSWORLD.hasPermission(player) - && !onlineTarget.getWorld().equals(player.getWorld())) { - lang.sendMessage( - player, - "messages.error.permissionCrossWorld", - new Replacement("%target%", onlineTarget.getDisplayName())); - return; + if (!(accessInv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(sender)) { + lang.sendMessage(sender, "messages.error.permissionOpenOther"); + return true; } } + return false; + } + + @Override + protected void handle( + @NotNull CommandSender sender, + @NotNull Player target, + boolean accessInv, + @NotNull String @NotNull [] args + ) { + Player player = (Player) sender; if (!config.doesNoArgsOpenSelf()) { // Record the target - (openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); + (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); } // Create the inventory final ISpecialInventory inv; try { - inv = openinv ? manager.getInventory(onlineTarget) : manager.getEnderChest(onlineTarget); + inv = accessInv ? manager.getInventory(target) : manager.getEnderChest(target); } catch (Exception e) { lang.sendMessage(player, "messages.error.commandException"); plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); @@ -237,13 +174,4 @@ private void openInventory(final Player player, final OfflinePlayer target, bool plugin.openInventory(player, inv); } - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!command.testPermissionSilent(sender) || args.length != 1) { - return Collections.emptyList(); - } - - return TabCompleter.completeOnlinePlayer(sender, args[0]); - } - } diff --git a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java new file mode 100644 index 00000000..5244d7f5 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java @@ -0,0 +1,261 @@ +package com.lishid.openinv.command; + +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.util.AccessEqualMode; +import com.lishid.openinv.util.Permissions; +import com.lishid.openinv.util.PlayerLoader; +import com.lishid.openinv.util.TabCompleter; +import com.lishid.openinv.util.config.Config; +import com.lishid.openinv.util.lang.LanguageManager; +import com.lishid.openinv.util.lang.Replacement; +import me.nahu.scheduler.wrapper.runnable.WrappedRunnable; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** + * A command abstraction for performing actions after looking up and loading a player. + */ +public abstract class PlayerLookupCommand implements TabExecutor { + + protected final @NotNull OpenInv plugin; + protected final @NotNull LanguageManager lang; + protected final @NotNull Config config; + protected final @NotNull PlayerLoader playerLoader; + + public PlayerLookupCommand( + @NotNull OpenInv plugin, + @NotNull LanguageManager lang, + @NotNull Config config, + @NotNull PlayerLoader playerLoader) { + this.plugin = plugin; + this.lang = lang; + this.config = config; + this.playerLoader = playerLoader; + } + + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String @NotNull [] args) { + + // Inventory or ender chest? + boolean accessInv = isAccessInventory(command); + + // Get target identifier from parameters. + String targetId = getTargetIdentifer(sender, args.length > 0 ? args[0] : null, accessInv); + if (targetId == null) { + return true; + } + + new WrappedRunnable() { + @Override + public void run() { + // Get target from identifier. + final OfflinePlayer target = getTarget(targetId); + + if (target == null || (!target.hasPlayedBefore() && !target.isOnline())) { + lang.sendMessage(sender, "messages.error.invalidPlayer"); + return; + } + + new WrappedRunnable() { + @Override + public void run() { + // Ensure sender still exists. + if ((sender instanceof Player player) && !player.isValid()) { + return; + } + + // Perform access checks and load target if necessary. + Player onlineTarget = access(sender, target, accessInv); + + if (onlineTarget != null) { + handle(sender, onlineTarget, accessInv, args); + } + } + }.runTask(PlayerLookupCommand.this.plugin); + + } + }.runTaskAsynchronously(this.plugin); + + return false; + } + + /** + * Get whether a player inventory or ender chest is accessed by the {@link Command} executed. + * + * @param command the {@code Command} being executed + * @return {@code true} if the command is for inventories, {@code false} for ender chests + */ + protected abstract boolean isAccessInventory(@NotNull Command command); + + /** + * Determine the target identifier from the first command argument. + * + *

Implementation note: a return value of {@code null} will cause the command to cease + * execution with no feedback. Appropriate feedback should be sent in the implementation.

+ * + * @param sender the sender of the command + * @param argument the argument, or {@code null} if none provided + * @param accessInv {@code true} if an inventory is being accessed, {@code false} for ender chest + * @return an updated target identifier or {@code null} if no target is available + */ + protected abstract @Nullable String getTargetIdentifer(@NotNull CommandSender sender, @Nullable String argument, boolean accessInv); + + /** + * Get an {@link OfflinePlayer} by identifier. + * + * @param identifier the identifier + * @return the corresponding player or {@code null} if no match was found + */ + protected abstract @Nullable OfflinePlayer getTarget(@NotNull String identifier); + + /** + * Attempt to access the target as an online player. Performs feedback in the event of denial. + * + * @param sender the {@link CommandSender} attempting access + * @param target the {@link OfflinePlayer} being targeted by the command + * @param invPerms {@code true} to use inventory permissions, {@code false} for ender chest + * @return the {@link Player} loaded or {@code null} if target is not accessible + */ + protected @Nullable Player access(@NotNull CommandSender sender, @NotNull OfflinePlayer target, boolean invPerms) { + // Attempt to load online player dependent on permissions and configuration. + Player onlineTarget = accessAsPlayer(sender, target); + + if (onlineTarget == null) { + return null; + } + + // Permissions checks. + if (deniedCommand(sender, onlineTarget, invPerms) || deniedAccess(sender, onlineTarget)) { + return null; + } + + return onlineTarget; + } + + /** + * Helper for accessing target as an online {@link Player}. Performs checks + * and feedback for configuration and online/offline permissions. + * + * @param sender the {@link CommandSender} attempting access + * @param target the {@link OfflinePlayer} being targeted by the command + * @return the {@link Player} loaded or {@code null} if target is not accessible + */ + protected @Nullable Player accessAsPlayer(@NotNull CommandSender sender, @NotNull OfflinePlayer target) { + Player onlineTarget; + + if (!target.isOnline()) { + if (!config.isOfflineDisabled() && Permissions.ACCESS_OFFLINE.hasPermission(sender)) { + // Try loading the player's data. + onlineTarget = playerLoader.load(target); + } else { + lang.sendMessage(sender, "messages.error.permissionPlayerOffline"); + return null; + } + } else { + if (Permissions.ACCESS_ONLINE.hasPermission(sender)) { + onlineTarget = target.getPlayer(); + } else { + lang.sendMessage(sender, "messages.error.permissionPlayerOnline"); + return null; + } + } + + if (onlineTarget == null) { + lang.sendMessage(sender, "messages.error.invalidPlayer"); + return null; + } + + return onlineTarget; + } + + /** + * Check for a lack of permissions related to the specific command being executed for the sender. + * For example, {@link Permissions#INVENTORY_OPEN_OTHER} might be required if the target and sender differ. + * + * @param sender the {@link CommandSender} attempting access + * @param onlineTarget the {@link Player} being targeted by the command + * @param accessInv {@code true} to use inventory permissions, {@code false} for ender chest + * @return {@code true} if the sender does not have the correct execution-specific permission + */ + protected abstract boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv); + + /** + * Check for a lack of generalized permissions for accessing the target. + * By default, this is access levels and cross-world restrictions. + * + * @param sender the {@link CommandSender} attempting access + * @param onlineTarget the {@link Player} being targeted by the command + * @return {@code true} if the sender does not have access to the target + */ + protected boolean deniedAccess(@NotNull CommandSender sender, @NotNull Player onlineTarget) { + if (sender.equals(onlineTarget)) { + return false; + } + + // Protected check + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + if (onlineTarget.hasPermission(permission) + && (!sender.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY)) { + lang.sendMessage( + sender, + "messages.error.permissionExempt", + new Replacement("%target%", onlineTarget.getDisplayName())); + return true; + } + } + + // Crossworld check + if (sender instanceof Player player + && !Permissions.ACCESS_CROSSWORLD.hasPermission(sender) + && !onlineTarget.getWorld().equals(player.getWorld())) { + lang.sendMessage( + sender, + "messages.error.permissionCrossWorld", + new Replacement("%target%", onlineTarget.getDisplayName())); + return true; + } + + return false; + } + + /** + * Perform main command functionality. + * + * @param sender the {@link CommandSender} executing the command + * @param target the {@link Player} being targeted + * @param accessInv {@code true} if an inventory is being accessed, {@code false} for ender chest + * @param args the original command arguments + */ + protected abstract void handle( + @NotNull CommandSender sender, + @NotNull Player target, + boolean accessInv, + @NotNull String @NotNull [] args); + + @Override + public List onTabComplete( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args) { + if (!command.testPermissionSilent(sender) || args.length != 1) { + return Collections.emptyList(); + } + + return TabCompleter.completeOnlinePlayer(sender, args[0]); + } + +} diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index c3b0c050..2214af81 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -20,7 +20,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.Inventory; -import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -109,6 +108,10 @@ public void unload(@NotNull UUID uuid) { enderChests.computeIfPresent(uuid, this::remove); } + public void save(@NotNull UUID uuid) { + consumeLoaded(uuid, inventory -> {}); + } + @Keep @EventHandler(priority = EventPriority.LOWEST) private void onPlayerJoin(@NotNull PlayerJoinEvent event) { diff --git a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java index 0d30e200..66dd9d33 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java +++ b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java @@ -93,7 +93,7 @@ public PlayerLoader( return player; } - public @Nullable OfflinePlayer match(@NotNull String name) { + public @Nullable OfflinePlayer matchExact(@NotNull String name) { // Warn if called on the main thread - if we resort to searching offline players, this may take several seconds. if (Bukkit.getServer().isPrimaryThread()) { logger.warning("Call to PlayerSearchCache#matchPlayer made on the main thread!"); @@ -143,6 +143,16 @@ public PlayerLoader( return player; } + return null; + } + + public @Nullable OfflinePlayer match(@NotNull String name) { + OfflinePlayer player = this.matchExact(name); + + if (player != null) { + return player; + } + // Inexact online match. player = Bukkit.getServer().getPlayer(name); diff --git a/plugin/src/main/resources/locale/en.yml b/plugin/src/main/resources/locale/en.yml index 37ec8449..2cdf06e2 100644 --- a/plugin/src/main/resources/locale/en.yml +++ b/plugin/src/main/resources/locale/en.yml @@ -16,6 +16,7 @@ messages: containerBlocked: 'You are opening a blocked container.' containerBlockedSilent: 'You are opening a blocked container silently.' containerSilent: 'You are opening a container silently.' + inventoryCleared: 'Cleared %target%''s inventory.' settingState: '%setting%: %state%' player: noMatches: 'No players found with %target%.' diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index dbc8ba69..a50fad34 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -51,6 +51,11 @@ permissions: openinv.enderchest.edit.other: children: openinv.enderchest.open.other: true + # Clear nodes (/clearinv and /clearender) + openinv.clear: + children: + openinv.clear.self: true + openinv.clear.other: true # Player access openinv.access: children: @@ -83,12 +88,22 @@ commands: permission: openinv.inventory.open.self;openinv.inventory.open.other usage: |- / [Player] - Open a player's inventory + clearinv: + description: Clear a player's inventory + permission: openinv.clear.self;openinv.clear.other + usage: |- + / [Player] - Clear a player's inventory openender: aliases: [oe] - description: Opens the enderchest of a player + description: Open a player's ender chest permission: openinv.enderchest.open.self;openinv.enderchest.open.other usage: |- - / [Player] - Open a player's enderchest + / [Player] - Open a player's ender chest + clearender: + description: Clear a player's ender chest + permission: openinv.clear.self;openinv.clear.other + usage: |- + / [Player] - Clear a player's ender chest searchinv: aliases: [si] description: Search and list players having a specific item @@ -98,7 +113,7 @@ commands: searchender: aliases: [se] permission: openinv.search.inventory - description: Searches and lists players having a specific item in their ender chest + description: Search and list players having a specific item in their ender chest usage: |- / [MinAmount] - MinAmount is optional, the minimum amount required silentcontainer: From 2a07673b1ac691338864c48eeb650f51c7152eca Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 18 May 2025 11:09:16 -0400 Subject: [PATCH 290/340] Add .gitattributes and .editorconfig --- .editorconfig | 491 +++++++++++++++++++++++++++++++++++++++++++++++++ .gitattributes | 11 ++ 2 files changed, 502 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..a38e6ef4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,491 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = false +max_line_length = 120 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = true +ij_smart_tabs = false +ij_visual_guides = +ij_wrap_on_typing = false + +[*.java] +ij_smart_tabs = true +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = false +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_deconstruction_list_components = true +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = true +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = false +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = true +ij_java_align_multiline_resources = true +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_align_types_in_multi_catch = true +ij_java_annotation_parameter_wrap = off +ij_java_array_initializer_new_line_after_left_brace = false +ij_java_array_initializer_right_brace_on_new_line = true +ij_java_array_initializer_wrap = off +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = off +ij_java_assignment_wrap = off +ij_java_binary_operation_sign_on_next_line = false +ij_java_binary_operation_wrap = off +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_field_with_annotations = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 1 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 0 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_add_space = false +ij_java_block_comment_at_first_column = true +ij_java_builder_methods = +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = true +ij_java_call_parameters_wrap = off +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 1000 +ij_java_class_names_in_javadoc = 1 +ij_java_deconstruction_list_wrap = normal +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_not_wrap_after_single_annotation_in_parameter = false +ij_java_do_while_brace_force = never +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = false +ij_java_doc_align_param_comments = false +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_enum_constants_wrap = off +ij_java_enum_field_annotation_wrap = off +ij_java_extends_keyword_wrap = off +ij_java_extends_list_wrap = off +ij_java_field_annotation_wrap = split_into_lines +ij_java_field_name_prefix = +ij_java_field_name_suffix = +ij_java_finally_on_new_line = false +ij_java_for_brace_force = never +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = true +ij_java_for_statement_wrap = off +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_generate_use_type_annotation_before_type = true +ij_java_if_brace_force = never +ij_java_imports_layout = @*,*,|,java.**,|,$* +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = false +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 2 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_builder_methods_indents = false +ij_java_keep_control_statement_in_one_line = true +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = true +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = true +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_on_demand_import_from_same_package_first = true +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_add_space_on_reformat = false +ij_java_line_comment_at_first_column = true +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = off +ij_java_method_parameters_new_line_after_left_paren = true +ij_java_method_parameters_right_paren_on_new_line = true +ij_java_method_parameters_wrap = on_every_item +ij_java_modifier_list_wrap = false +ij_java_multi_catch_types_wrap = normal +ij_java_names_count_to_use_import_on_demand = 10 +ij_java_new_line_after_lparen_in_annotation = false +ij_java_new_line_after_lparen_in_deconstruction_pattern = true +ij_java_new_line_after_lparen_in_record_header = false +ij_java_new_line_when_body_is_presented = false +ij_java_packages_to_use_import_on_demand = +ij_java_parameter_annotation_wrap = off +ij_java_parameter_name_prefix = +ij_java_parameter_name_suffix = +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = true +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_preserve_module_imports = true +ij_java_record_components_wrap = normal +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = true +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = true +ij_java_resource_list_wrap = off +ij_java_rparen_on_new_line_in_annotation = true +ij_java_rparen_on_new_line_in_deconstruction_pattern = true +ij_java_rparen_on_new_line_in_record_header = true +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = false +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_deconstruction_list = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_annotation_eq = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_inside_block_braces_when_body_is_present = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_deconstruction_list = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_record_header = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = +ij_java_subclass_name_suffix = Impl +ij_java_switch_expressions_wrap = normal +ij_java_ternary_operation_signs_on_next_line = false +ij_java_ternary_operation_wrap = off +ij_java_test_name_prefix = +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = off +ij_java_throws_list_wrap = off +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = off +ij_java_visibility = public +ij_java_while_brace_force = never +ij_java_while_on_new_line = false +ij_java_wrap_comments = false +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = false +ij_java_wrap_semicolon_after_call_chain = false + +[*.properties] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_add_space = false +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 2 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal + +[{*.bash,*.sh,*.zsh}] +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false +ij_shell_use_unix_line_separator = true + +[{*.kt,*.kts}] +ij_kotlin_align_in_columns_case_branch = false +ij_kotlin_align_multiline_binary_operation = false +ij_kotlin_align_multiline_extends_list = false +ij_kotlin_align_multiline_method_parentheses = false +ij_kotlin_align_multiline_parameters = true +ij_kotlin_align_multiline_parameters_in_calls = false +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_assignment_wrap = normal +ij_kotlin_blank_lines_after_class_header = 0 +ij_kotlin_blank_lines_around_block_when_branches = 0 +ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 +ij_kotlin_block_comment_add_space = false +ij_kotlin_block_comment_at_first_column = true +ij_kotlin_call_parameters_new_line_after_left_paren = true +ij_kotlin_call_parameters_right_paren_on_new_line = true +ij_kotlin_call_parameters_wrap = on_every_item +ij_kotlin_catch_on_new_line = false +ij_kotlin_class_annotation_wrap = split_into_lines +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +ij_kotlin_continuation_indent_for_chained_calls = false +ij_kotlin_continuation_indent_for_expression_bodies = false +ij_kotlin_continuation_indent_in_argument_lists = false +ij_kotlin_continuation_indent_in_elvis = false +ij_kotlin_continuation_indent_in_if_conditions = false +ij_kotlin_continuation_indent_in_parameter_lists = false +ij_kotlin_continuation_indent_in_supertype_lists = false +ij_kotlin_else_on_new_line = false +ij_kotlin_enum_constants_wrap = off +ij_kotlin_extends_list_wrap = normal +ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_finally_on_new_line = false +ij_kotlin_if_rparen_on_new_line = true +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ +ij_kotlin_indent_before_arrow_on_new_line = true +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_keep_blank_lines_before_right_brace = 2 +ij_kotlin_keep_blank_lines_in_code = 2 +ij_kotlin_keep_blank_lines_in_declarations = 2 +ij_kotlin_keep_first_column_comment = true +ij_kotlin_keep_indents_on_empty_lines = false +ij_kotlin_keep_line_breaks = true +ij_kotlin_lbrace_on_next_line = false +ij_kotlin_line_break_after_multiline_when_entry = true +ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_add_space_on_reformat = false +ij_kotlin_line_comment_at_first_column = true +ij_kotlin_method_annotation_wrap = split_into_lines +ij_kotlin_method_call_chain_wrap = normal +ij_kotlin_method_parameters_new_line_after_left_paren = true +ij_kotlin_method_parameters_right_paren_on_new_line = true +ij_kotlin_method_parameters_wrap = on_every_item +ij_kotlin_name_count_to_use_star_import = 5 +ij_kotlin_name_count_to_use_star_import_for_members = 3 +ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.** +ij_kotlin_parameter_annotation_wrap = off +ij_kotlin_space_after_comma = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_comma = false +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_lambda_arrow = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_before_when_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_elvis = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_range = false +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_variable_annotation_wrap = off +ij_kotlin_while_on_new_line = false +ij_kotlin_wrap_elvis_expressions = 1 +ij_kotlin_wrap_expression_body_functions = 1 +ij_kotlin_wrap_first_method_in_call_chain = false + +[{*.har,*.json,*.jsonc,*.mcmeta,.prettierrc}] +ij_json_array_wrapping = split_into_lines +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_keep_trailing_comma = false +ij_json_object_wrapping = split_into_lines +ij_json_property_alignment = do_not_align +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = false +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm,*.html,*.sht,*.shtm,*.shtml}] +ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_add_space = false +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p +ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span,pre,textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.markdown,*.md}] +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock,uv.lock}] +ij_toml_keep_indents_on_empty_lines = false + +[{*.yaml,*.yml}] +ij_yaml_align_values_properties = do_not_align +ij_yaml_autoinsert_sequence_marker = true +ij_yaml_block_mapping_on_new_line = false +ij_yaml_indent_sequence_value = true +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true +ij_yaml_line_comment_add_space = false +ij_yaml_line_comment_add_space_on_reformat = false +ij_yaml_line_comment_at_first_column = true +ij_yaml_sequence_on_new_line = false +ij_yaml_space_before_colon = false +ij_yaml_spaces_within_braces = true +ij_yaml_spaces_within_brackets = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..f017c46e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Use lf endings by default. +* text=auto eol=lf + +# Declare text file types just in case +*.java text +*.yml text +*.xml text +*.md text + +# Exclude binary files +*.png binary From 8647f8e156de1b4ab169eeb3a0c5035b5d7dce24 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 18 May 2025 11:09:30 -0400 Subject: [PATCH 291/340] Autoformat --- README.MD => README.md | 15 +- .../openinv/togglepersist/TogglePersist.java | 45 +- .../java/com/lishid/openinv/IOpenInv.java | 298 ++++++------ .../openinv/event/OpenPlayerSaveEvent.java | 3 +- .../lishid/openinv/event/PlayerSaveEvent.java | 3 +- .../openinv/event/PlayerToggledEvent.java | 3 +- .../openinv/internal/IAnySilentContainer.java | 164 +++---- .../openinv/internal/ISpecialInventory.java | 72 +-- .../internal/ISpecialPlayerInventory.java | 8 +- .../lishid/openinv/util/InventoryAccess.java | 129 ++--- .../jikoo/openinv/BuildToolsValueSource.kt | 10 +- .../openinv/SpigotDependencyExtension.kt | 2 +- .../com/github/jikoo/openinv/SpigotReobf.kt | 2 +- .../github/jikoo/openinv/SpigotReobfTask.kt | 2 +- .../com/github/jikoo/openinv/SpigotSetup.kt | 5 +- .../src/main/kotlin/openinv-base.gradle.kts | 5 - .../openinv/internal/InventoryViewTitle.java | 10 +- .../openinv/internal/PlayerManager.java | 48 +- .../com/lishid/openinv/util/Permissions.java | 76 +-- .../lishid/openinv/util/ReflectionHelper.java | 84 ++-- .../openinv/util/lang/LanguageManager.java | 447 +++++++++--------- internal/common/build.gradle.kts | 4 +- .../common/container/AnySilentContainer.java | 293 ++++++------ .../common/container/OpenEnderChest.java | 3 +- .../common/container/OpenInventory.java | 38 +- .../container/bukkit/OpenDummyInventory.java | 3 +- .../container/bukkit/OpenPlayerInventory.java | 3 +- .../common/container/menu/OpenChestMenu.java | 6 +- .../container/menu/OpenEnderChestMenu.java | 3 +- .../container/menu/OpenInventoryMenu.java | 7 +- .../container/slot/ContentEquipment.java | 1 - .../common/container/slot/ContentOffHand.java | 3 +- .../placeholder/PlaceholderLoaderBase.java | 17 +- .../internal/common/player/OpenPlayer.java | 9 +- .../internal/common/player/PlayerManager.java | 17 +- internal/paper1_21_1/build.gradle.kts | 4 +- .../slot/placeholder/PlaceholderLoader.java | 31 +- .../paper1_21_1/player/PlayerManager.java | 9 +- internal/paper1_21_3/build.gradle.kts | 4 +- internal/paper1_21_4/build.gradle.kts | 4 +- .../paper1_21_4/container/OpenEnderChest.java | 8 +- .../paper1_21_4/container/OpenInventory.java | 32 +- .../container/menu/OpenChestMenu.java | 6 +- .../container/menu/OpenEnderChestMenu.java | 3 +- .../container/menu/OpenInventoryMenu.java | 7 +- .../container/slot/ContentOffHand.java | 3 +- .../paper1_21_4/player/OpenPlayer.java | 6 +- internal/spigot/build.gradle.kts | 4 +- .../reobf/container/AnySilentContainer.java | 18 +- .../reobf/container/slot/ContentOffHand.java | 3 +- .../internal/reobf/player/PlayerManager.java | 17 +- .../main/java/com/lishid/openinv/OpenInv.java | 431 ++++++++--------- .../openinv/command/ClearInvCommand.java | 126 ++--- .../command/ContainerSettingCommand.java | 131 ++--- .../openinv/command/OpenInvCommand.java | 232 ++++----- .../openinv/command/PlayerLookupCommand.java | 30 +- .../command/SearchContainerCommand.java | 183 +++---- .../openinv/command/SearchEnchantCommand.java | 228 ++++----- .../openinv/command/SearchInvCommand.java | 154 +++--- .../openinv/listener/ContainerListener.java | 2 +- .../lishid/openinv/util/InternalAccessor.java | 412 ++++++++-------- .../lishid/openinv/util/InventoryManager.java | 28 +- .../com/lishid/openinv/util/PlayerLoader.java | 52 +- .../com/lishid/openinv/util/StringMetric.java | 225 ++++----- .../com/lishid/openinv/util/TabCompleter.java | 206 ++++---- .../openinv/util/config/ConfigUpdater.java | 176 +++---- plugin/src/main/resources/plugin.yml | 36 +- resource-pack/build.gradle.kts | 2 +- 68 files changed, 2430 insertions(+), 2221 deletions(-) rename README.MD => README.md (88%) diff --git a/README.MD b/README.md similarity index 88% rename from README.MD rename to README.md index ab3e2a0c..618e7860 100644 --- a/README.MD +++ b/README.md @@ -1,7 +1,10 @@ ## About -OpenInv is a [Bukkit plugin](https://dev.bukkit.org/projects/openinv) which allows users to open and edit anyone's inventory or ender chest - online or not! + +OpenInv is a [Bukkit plugin](https://dev.bukkit.org/projects/openinv) which allows users to open and edit anyone's +inventory or ender chest - online or not! ## Features + - **OpenInv**: Open anyone's inventory, even if they're offline. - Read-only mode! Don't grant edit permission. - Cross-world support! Allow access only from the same world. @@ -16,15 +19,19 @@ OpenInv is a [Bukkit plugin](https://dev.bukkit.org/projects/openinv) which allo - **AnyContainer**: Open containers, even if blocked by ocelots or blocks. ## Commands + See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands). ## Permissions + See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Permissions) ## For Developers ### As a Dependency + The OpenInv API is available via [JitPack](https://jitpack.io/). + ```xml @@ -33,6 +40,7 @@ The OpenInv API is available via [JitPack](https://jitpack.io/). ``` + ```xml @@ -42,9 +50,12 @@ The OpenInv API is available via [JitPack](https://jitpack.io/). ``` -Note that since JitPack only builds the API now, the "full" OpenInv jar on JitPack is actually the openinvapi artifact. This is a change from previous dependency declaration that I hope to revert. + +Note that since JitPack only builds the API now, the "full" OpenInv jar on JitPack is actually the openinvapi artifact. +This is a change from previous dependency declaration that I hope to revert. ### Compilation + Execute the gradle wrapper: `./gradlew build` diff --git a/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java b/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java index 070ff081..6a0cc298 100644 --- a/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java +++ b/addon/togglepersist/src/main/java/com/github/jikoo/openinv/togglepersist/TogglePersist.java @@ -64,13 +64,16 @@ public void onEnable() { } private void set(UUID playerId, String toggleName) { - enabledToggles.compute(playerId, (uuid, toggles) -> { - if (toggles == null) { - toggles = new HashSet<>(); - } - toggles.add(toggleName); - return toggles; - }); + enabledToggles.compute( + playerId, + (uuid, toggles) -> { + if (toggles == null) { + toggles = new HashSet<>(); + } + toggles.add(toggleName); + return toggles; + } + ); } @Override @@ -97,13 +100,16 @@ public void onDisable() { String idString = playerToggles.getKey().toString(); for (String toggleName : playerToggles.getValue()) { // Add player ID to listing for each enabled toggle. - converted.compute(toggleName, (name, ids) -> { - if (ids == null) { - ids = new ArrayList<>(); - } - ids.add(idString); - return ids; - }); + converted.compute( + toggleName, + (name, ids) -> { + if (ids == null) { + ids = new ArrayList<>(); + } + ids.add(idString); + return ids; + } + ); } } return converted; @@ -133,10 +139,13 @@ private void onToggleSet(@NotNull PlayerToggledEvent event) { if (event.isEnabled()) { set(event.getPlayerId(), event.getToggle().getName()); } else { - enabledToggles.computeIfPresent(event.getPlayerId(), (uuid, toggles) -> { - toggles.remove(event.getToggle().getName()); - return toggles.isEmpty() ? null : toggles; - }); + enabledToggles.computeIfPresent( + event.getPlayerId(), + (uuid, toggles) -> { + toggles.remove(event.getToggle().getName()); + return toggles.isEmpty() ? null : toggles; + } + ); } } diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index 7c4e1bc8..4639f328 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -35,151 +35,157 @@ */ public interface IOpenInv { - /** - * Check if the server version is supported by OpenInv. - * - * @return true if the server version is supported - */ - boolean isSupportedVersion(); - - /** - * Check the configuration value for whether OpenInv saves player data when unloading players. This is exclusively - * for users who do not allow editing of inventories, only viewing, and wish to prevent any possibility of bugs such - * as lishid#40. If true, OpenInv will not ever save any edits made to players. - * - * @return false unless configured otherwise - */ - boolean disableSaving(); - - /** - * Check the configuration value for whether OpenInv allows offline access. If true, OpenInv will not load or allow - * modification of players while they are not online. This does not prevent other plugins from using existing loaded - * players who have gone offline. - * - * @return false unless configured otherwise - * @since 4.2.0 - */ - boolean disableOfflineAccess(); - - /** - * Check the configuration value for whether OpenInv uses history for opening commands. If false, OpenInv will use - * the previous parameterized search when no parameters are provided. - * - * @return false unless configured otherwise - * @since 4.3.0 - */ - boolean noArgsOpensSelf(); - - /** - * Get the active {@link IAnySilentContainer} implementation. - * - * @return the active implementation for the server version - * @throws IllegalStateException if the server version is unsupported - */ - @NotNull IAnySilentContainer getAnySilentContainer(); - - /** - * Get whether a user has AnyContainer mode enabled. - * - * @param offline the user to obtain the state of - * @return true if AnyContainer mode is enabled - */ - boolean getAnyContainerStatus(@NotNull OfflinePlayer offline); - - /** - * Set whether a user has AnyContainer mode enabled. - * - * @param offline the user to set the state of - * @param status the state of the mode - */ - void setAnyContainerStatus(@NotNull OfflinePlayer offline, boolean status); - - /** - * Get whether a user has SilentContainer mode enabled. - * - * @param offline the user to obtain the state of - * @return true if SilentContainer mode is enabled - */ - boolean getSilentContainerStatus(@NotNull OfflinePlayer offline); - - /** - * Set whether a user has SilentContainer mode enabled. - * - * @param offline the user to set the state of - * @param status the state of the mode - */ - void setSilentContainerStatus(@NotNull OfflinePlayer offline, boolean status); - - /** - * Get an {@link ISpecialEnderChest} for a user. - * - * @param player the {@link Player} owning the inventory - * @param online whether the owner is currently online - * @return the created inventory - * @throws IllegalStateException if the server version is unsupported - * @throws InstantiationException if there was an issue creating the inventory - */ - @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull Player player, boolean online) throws InstantiationException; - - /** - * Get an {@link ISpecialPlayerInventory} for a user. - * - * @param player the {@link Player} owning the inventory - * @param online whether the owner is currently online - * @return the created inventory - * @throws IllegalStateException if the server version is unsupported - * @throws InstantiationException if there was an issue creating the inventory - */ - @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull Player player, boolean online) throws InstantiationException; - - /** - * Open an {@link ISpecialInventory} for a {@link Player}. - * - * @param player the viewer - * @param inventory the inventory to open - * @return the resulting {@link InventoryView} - */ - @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); - - /** - * Check if a {@link Player} is currently loaded by OpenInv. - * - * @param playerUuid the {@link UUID} of the {@code Player} - * @return whether the {@code Player} is loaded - * @since 4.2.0 - */ - boolean isPlayerLoaded(@NotNull UUID playerUuid); - - /** - * Load a {@link Player} from an {@link OfflinePlayer}. If the user has not played before or the default world for - * the server is not loaded, this will return {@code null}. - * - * @param offline the {@code OfflinePlayer} to load a {@code Player} for - * @return the loaded {@code Player} - * @throws IllegalStateException if the server version is unsupported - */ - @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline); - - /** - * Match an existing {@link OfflinePlayer}. If the name is a {@link UUID#toString() UUID string}, this will only - * return the user if they have actually played on the server before, unlike {@link Bukkit#getOfflinePlayer(UUID)}. - * - *

This method is potentially very heavily blocking. It should not ever be called on the - * main thread, and if it is, a stack trace will be displayed alerting server owners to the - * call. - * - * @param name the string to match - * @return the user with the closest matching name - */ - @Nullable OfflinePlayer matchPlayer(@NotNull String name); - - /** - * Forcibly close inventories of and unload any cached data for a user. - * - * @param offline the {@link OfflinePlayer} to unload - */ - void unload(@NotNull OfflinePlayer offline); - - Logger getLogger(); + /** + * Check if the server version is supported by OpenInv. + * + * @return true if the server version is supported + */ + boolean isSupportedVersion(); + + /** + * Check the configuration value for whether OpenInv saves player data when unloading players. This is exclusively + * for users who do not allow editing of inventories, only viewing, and wish to prevent any possibility of bugs such + * as lishid#40. If true, OpenInv will not ever save any edits made to players. + * + * @return false unless configured otherwise + */ + boolean disableSaving(); + + /** + * Check the configuration value for whether OpenInv allows offline access. If true, OpenInv will not load or allow + * modification of players while they are not online. This does not prevent other plugins from using existing loaded + * players who have gone offline. + * + * @return false unless configured otherwise + * @since 4.2.0 + */ + boolean disableOfflineAccess(); + + /** + * Check the configuration value for whether OpenInv uses history for opening commands. If false, OpenInv will use + * the previous parameterized search when no parameters are provided. + * + * @return false unless configured otherwise + * @since 4.3.0 + */ + boolean noArgsOpensSelf(); + + /** + * Get the active {@link IAnySilentContainer} implementation. + * + * @return the active implementation for the server version + * @throws IllegalStateException if the server version is unsupported + */ + @NotNull IAnySilentContainer getAnySilentContainer(); + + /** + * Get whether a user has AnyContainer mode enabled. + * + * @param offline the user to obtain the state of + * @return true if AnyContainer mode is enabled + */ + boolean getAnyContainerStatus(@NotNull OfflinePlayer offline); + + /** + * Set whether a user has AnyContainer mode enabled. + * + * @param offline the user to set the state of + * @param status the state of the mode + */ + void setAnyContainerStatus(@NotNull OfflinePlayer offline, boolean status); + + /** + * Get whether a user has SilentContainer mode enabled. + * + * @param offline the user to obtain the state of + * @return true if SilentContainer mode is enabled + */ + boolean getSilentContainerStatus(@NotNull OfflinePlayer offline); + + /** + * Set whether a user has SilentContainer mode enabled. + * + * @param offline the user to set the state of + * @param status the state of the mode + */ + void setSilentContainerStatus(@NotNull OfflinePlayer offline, boolean status); + + /** + * Get an {@link ISpecialEnderChest} for a user. + * + * @param player the {@link Player} owning the inventory + * @param online whether the owner is currently online + * @return the created inventory + * @throws IllegalStateException if the server version is unsupported + * @throws InstantiationException if there was an issue creating the inventory + */ + @NotNull ISpecialEnderChest getSpecialEnderChest( + @NotNull Player player, + boolean online + ) throws InstantiationException; + + /** + * Get an {@link ISpecialPlayerInventory} for a user. + * + * @param player the {@link Player} owning the inventory + * @param online whether the owner is currently online + * @return the created inventory + * @throws IllegalStateException if the server version is unsupported + * @throws InstantiationException if there was an issue creating the inventory + */ + @NotNull ISpecialPlayerInventory getSpecialInventory( + @NotNull Player player, + boolean online + ) throws InstantiationException; + + /** + * Open an {@link ISpecialInventory} for a {@link Player}. + * + * @param player the viewer + * @param inventory the inventory to open + * @return the resulting {@link InventoryView} + */ + @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); + + /** + * Check if a {@link Player} is currently loaded by OpenInv. + * + * @param playerUuid the {@link UUID} of the {@code Player} + * @return whether the {@code Player} is loaded + * @since 4.2.0 + */ + boolean isPlayerLoaded(@NotNull UUID playerUuid); + + /** + * Load a {@link Player} from an {@link OfflinePlayer}. If the user has not played before or the default world for + * the server is not loaded, this will return {@code null}. + * + * @param offline the {@code OfflinePlayer} to load a {@code Player} for + * @return the loaded {@code Player} + * @throws IllegalStateException if the server version is unsupported + */ + @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline); + + /** + * Match an existing {@link OfflinePlayer}. If the name is a {@link UUID#toString() UUID string}, this will only + * return the user if they have actually played on the server before, unlike {@link Bukkit#getOfflinePlayer(UUID)}. + * + *

This method is potentially very heavily blocking. It should not ever be called on the + * main thread, and if it is, a stack trace will be displayed alerting server owners to the + * call. + * + * @param name the string to match + * @return the user with the closest matching name + */ + @Nullable OfflinePlayer matchPlayer(@NotNull String name); + + /** + * Forcibly close inventories of and unload any cached data for a user. + * + * @param offline the {@link OfflinePlayer} to unload + */ + void unload(@NotNull OfflinePlayer offline); + + Logger getLogger(); } diff --git a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java index b6da22dc..f38457b5 100644 --- a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java @@ -27,7 +27,8 @@ public class OpenPlayerSaveEvent extends PlayerSaveEvent { @RestrictedApi( explanation = "Constructor is not considered part of the API and may be subject to change.", link = "", - allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java") + allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java" + ) @ApiStatus.Internal OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) { super(player); diff --git a/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java index 4ca1d715..a70f222a 100644 --- a/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java @@ -28,7 +28,8 @@ public class PlayerSaveEvent extends PlayerEvent implements Cancellable { @RestrictedApi( explanation = "Constructor is not considered part of the API and may be subject to change.", link = "", - allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java") + allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java" + ) @ApiStatus.Internal PlayerSaveEvent(@NotNull Player player) { super(player); diff --git a/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java b/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java index 82f23228..2072ecfa 100644 --- a/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java @@ -23,7 +23,8 @@ public class PlayerToggledEvent extends Event { @RestrictedApi( explanation = "Constructor is not considered part of the API and may be subject to change.", link = "", - allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java") + allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java" + ) @ApiStatus.Internal PlayerToggledEvent(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean enabled) { this.toggle = toggle; diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index fe470a70..3f83147a 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -29,94 +29,94 @@ public interface IAnySilentContainer { - /** - * Forcibly open the container at the given coordinates for the Player. This will open blocked containers! Be sure - * to check {@link #isAnyContainerNeeded(Block)} first if that is not desirable. - * - * @param player the {@link Player} opening the container - * @param silent whether the container's noise is to be silenced - * @param block the {@link Block} of the container - * @return true if the container can be opened - */ - boolean activateContainer(@NotNull Player player, boolean silent, @NotNull Block block); + /** + * Forcibly open the container at the given coordinates for the Player. This will open blocked containers! Be sure + * to check {@link #isAnyContainerNeeded(Block)} first if that is not desirable. + * + * @param player the {@link Player} opening the container + * @param silent whether the container's noise is to be silenced + * @param block the {@link Block} of the container + * @return true if the container can be opened + */ + boolean activateContainer(@NotNull Player player, boolean silent, @NotNull Block block); - /** - * Perform operations required to close the current container silently. - * - * @param player the {@link Player} closing a container - */ - void deactivateContainer(@NotNull Player player); + /** + * Perform operations required to close the current container silently. + * + * @param player the {@link Player} closing a container + */ + void deactivateContainer(@NotNull Player player); - /** - * Check if the container at the given coordinates is blocked. - * - * @param block the {@link Block} of the container - * @return true if the container is blocked - */ - boolean isAnyContainerNeeded(@NotNull Block block); + /** + * Check if the container at the given coordinates is blocked. + * + * @param block the {@link Block} of the container + * @return true if the container is blocked + */ + boolean isAnyContainerNeeded(@NotNull Block block); - /** - * Check if a shulker box block cannot be opened under ordinary circumstances. - * - * @param shulkerBox the shulker box block - * @return whether the container is blocked - */ - default boolean isShulkerBlocked(@NotNull Block shulkerBox) { - Directional directional = (Directional) shulkerBox.getBlockData(); - BlockFace facing = directional.getFacing(); - // Construct a new 1-block bounding box at the origin. - BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1); - // Expand the box in the direction the shulker will open. - box.expand(facing, 0.5); - // Move the box away from the origin by a block so only the expansion intersects with a box around the origin. - box.shift(facing.getOppositeFace().getDirection()); - // Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box. - return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box); - } + /** + * Check if a shulker box block cannot be opened under ordinary circumstances. + * + * @param shulkerBox the shulker box block + * @return whether the container is blocked + */ + default boolean isShulkerBlocked(@NotNull Block shulkerBox) { + Directional directional = (Directional) shulkerBox.getBlockData(); + BlockFace facing = directional.getFacing(); + // Construct a new 1-block bounding box at the origin. + BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1); + // Expand the box in the direction the shulker will open. + box.expand(facing, 0.5); + // Move the box away from the origin by a block so only the expansion intersects with a box around the origin. + box.shift(facing.getOppositeFace().getDirection()); + // Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box. + return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box); + } - /** - * Check if a chest cannot be opened under ordinary circumstances. - * - * @param chest the chest block - * @return whether the container is blocked - */ - default boolean isChestBlocked(@NotNull Block chest) { - org.bukkit.block.Block relative = chest.getRelative(0, 1, 0); - return relative.getType().isOccluding() - || !chest.getWorld().getNearbyEntities(BoundingBox.of(relative), Cat.class::isInstance).isEmpty(); - } + /** + * Check if a chest cannot be opened under ordinary circumstances. + * + * @param chest the chest block + * @return whether the container is blocked + */ + default boolean isChestBlocked(@NotNull Block chest) { + org.bukkit.block.Block relative = chest.getRelative(0, 1, 0); + return relative.getType().isOccluding() + || !chest.getWorld().getNearbyEntities(BoundingBox.of(relative), Cat.class::isInstance).isEmpty(); + } - /** - * Check if the given {@link Block} is a container which can be unblocked or silenced. - * - * @param block the potential container - * @return true if the type is a supported container - */ - boolean isAnySilentContainer(@NotNull Block block); + /** + * Check if the given {@link Block} is a container which can be unblocked or silenced. + * + * @param block the potential container + * @return true if the type is a supported container + */ + boolean isAnySilentContainer(@NotNull Block block); - /** - * Check if the given {@link BlockState} is a container which can be unblocked or silenced. - * - * @param blockState the potential container - * @return true if the type is a supported container - */ - default boolean isAnySilentContainer(@NotNull BlockState blockState) { - return (blockState instanceof InventoryHolder holder && isAnySilentContainer(holder)) - || blockState instanceof EnderChest; - } + /** + * Check if the given {@link BlockState} is a container which can be unblocked or silenced. + * + * @param blockState the potential container + * @return true if the type is a supported container + */ + default boolean isAnySilentContainer(@NotNull BlockState blockState) { + return (blockState instanceof InventoryHolder holder && isAnySilentContainer(holder)) + || blockState instanceof EnderChest; + } - /** - * Check if the given {@link InventoryHolder} is a container which can be unblocked or silenced. - * - * @param holder the potential container - * @return true if the type is a supported container - */ - default boolean isAnySilentContainer(@NotNull InventoryHolder holder) { - return holder instanceof org.bukkit.block.EnderChest - || holder instanceof org.bukkit.block.Chest - || holder instanceof org.bukkit.block.DoubleChest - || holder instanceof org.bukkit.block.ShulkerBox - || holder instanceof org.bukkit.block.Barrel; - } + /** + * Check if the given {@link InventoryHolder} is a container which can be unblocked or silenced. + * + * @param holder the potential container + * @return true if the type is a supported container + */ + default boolean isAnySilentContainer(@NotNull InventoryHolder holder) { + return holder instanceof org.bukkit.block.EnderChest + || holder instanceof org.bukkit.block.Chest + || holder instanceof org.bukkit.block.DoubleChest + || holder instanceof org.bukkit.block.ShulkerBox + || holder instanceof org.bukkit.block.Barrel; + } } diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java index 430cca5f..3930165c 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java @@ -27,46 +27,46 @@ */ public interface ISpecialInventory { - /** - * Get the {@link Inventory} associated with this {@code ISpecialInventory}. - * - * @return the Bukkit inventory - */ - @NotNull Inventory getBukkitInventory(); + /** + * Get the {@link Inventory} associated with this {@code ISpecialInventory}. + * + * @return the Bukkit inventory + */ + @NotNull Inventory getBukkitInventory(); - /** - * Get the {@link InventoryType} corresponding to this {@code ISpecialInventory}. - * - * @return the type of Bukkit inventory - */ - @NotNull InventoryType getBukkitType(); + /** + * Get the {@link InventoryType} corresponding to this {@code ISpecialInventory}. + * + * @return the type of Bukkit inventory + */ + @NotNull InventoryType getBukkitType(); - /** - * Set the owning {@link Player} instance to a newly-joined user. - * - * @param player the user coming online - */ - void setPlayerOnline(@NotNull Player player); + /** + * Set the owning {@link Player} instance to a newly-joined user. + * + * @param player the user coming online + */ + void setPlayerOnline(@NotNull Player player); - /** - * Mark the owner of the inventory offline. - */ - void setPlayerOffline(); + /** + * Mark the owner of the inventory offline. + */ + void setPlayerOffline(); - /** - * Get whether the inventory is being viewed by any users. - * - * @return true if the inventory is being viewed - */ - default boolean isInUse() { - return !getBukkitInventory().getViewers().isEmpty(); - } + /** + * Get whether the inventory is being viewed by any users. + * + * @return true if the inventory is being viewed + */ + default boolean isInUse() { + return !getBukkitInventory().getViewers().isEmpty(); + } - /** - * Get the {@link Player} who owns the inventory. - * - * @return the {@link HumanEntity} who owns the inventory - */ - @NotNull HumanEntity getPlayer(); + /** + * Get the {@link Player} who owns the inventory. + * + * @return the {@link HumanEntity} who owns the inventory + */ + @NotNull HumanEntity getPlayer(); } diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java index a571e0a9..91affaf8 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialPlayerInventory.java @@ -24,9 +24,9 @@ */ public interface ISpecialPlayerInventory extends ISpecialInventory { - @Override - default @NotNull InventoryType getBukkitType() { - return InventoryType.PLAYER; - } + @Override + default @NotNull InventoryType getBukkitType() { + return InventoryType.PLAYER; + } } diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index 466cae61..e3a0182c 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -29,76 +29,79 @@ public final class InventoryAccess { - private static @Nullable BiFunction, ISpecialInventory> provider; + private static @Nullable BiFunction, ISpecialInventory> provider; - public static boolean isUsable() { - return provider != null; - } + public static boolean isUsable() { + return provider != null; + } - /** - * Check if an {@link Inventory} is an {@link ISpecialPlayerInventory} implementation. - * - * @param inventory the Bukkit inventory - * @return true if backed by the correct implementation - */ - public static boolean isPlayerInventory(@NotNull Inventory inventory) { - return getPlayerInventory(inventory) != null; - } + /** + * Check if an {@link Inventory} is an {@link ISpecialPlayerInventory} implementation. + * + * @param inventory the Bukkit inventory + * @return true if backed by the correct implementation + */ + public static boolean isPlayerInventory(@NotNull Inventory inventory) { + return getPlayerInventory(inventory) != null; + } - /** - * Get the {@link ISpecialPlayerInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is - * not backed by the correct class. - * - * @param inventory the Bukkit inventory - * @return the backing implementation if available - */ - public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) { - return provider == null ? null : (ISpecialPlayerInventory) provider.apply(inventory, ISpecialPlayerInventory.class); - } + /** + * Get the {@link ISpecialPlayerInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is + * not backed by the correct class. + * + * @param inventory the Bukkit inventory + * @return the backing implementation if available + */ + public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) { + return provider == null ? null : (ISpecialPlayerInventory) provider.apply(inventory, ISpecialPlayerInventory.class); + } - /** - * Check if an {@link Inventory} is an {@link ISpecialEnderChest} implementation. - * - * @param inventory the Bukkit inventory - * @return true if backed by the correct implementation - */ - public static boolean isEnderChest(@NotNull Inventory inventory) { - return getEnderChest(inventory) != null; - } + /** + * Check if an {@link Inventory} is an {@link ISpecialEnderChest} implementation. + * + * @param inventory the Bukkit inventory + * @return true if backed by the correct implementation + */ + public static boolean isEnderChest(@NotNull Inventory inventory) { + return getEnderChest(inventory) != null; + } - /** - * Get the {@link ISpecialEnderChest} backing an {@link Inventory}. Returns {@code null} if the inventory is - * not backed by the correct class. - * - * @param inventory the Bukkit inventory - * @return the backing implementation if available - */ - public static @Nullable ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) { - return provider == null ? null : (ISpecialEnderChest) provider.apply(inventory, ISpecialEnderChest.class); - } + /** + * Get the {@link ISpecialEnderChest} backing an {@link Inventory}. Returns {@code null} if the inventory is + * not backed by the correct class. + * + * @param inventory the Bukkit inventory + * @return the backing implementation if available + */ + public static @Nullable ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) { + return provider == null ? null : (ISpecialEnderChest) provider.apply(inventory, ISpecialEnderChest.class); + } - /** - * Get a {@link ISpecialInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is not backed - * by the correct class. - * - * @param inventory the Bukkit inventory - * @return the backing implementation if available - */ - public static @Nullable ISpecialInventory getInventory(@NotNull Inventory inventory) { - return provider == null ? null : provider.apply(inventory, ISpecialInventory.class); - } + /** + * Get a {@link ISpecialInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is not backed + * by the correct class. + * + * @param inventory the Bukkit inventory + * @return the backing implementation if available + */ + public static @Nullable ISpecialInventory getInventory(@NotNull Inventory inventory) { + return provider == null ? null : provider.apply(inventory, ISpecialInventory.class); + } - @RestrictedApi( - explanation = "Not part of the API.", - link = "", - allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java") - @ApiStatus.Internal - static void setProvider(@Nullable BiFunction, ISpecialInventory> provider) { - InventoryAccess.provider = provider; - } + @RestrictedApi( + explanation = "Not part of the API.", + link = "", + allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java" + ) + @ApiStatus.Internal + static void setProvider( + @Nullable BiFunction, ISpecialInventory> provider + ) { + InventoryAccess.provider = provider; + } - private InventoryAccess() { - throw new IllegalStateException("Cannot create instance of utility class."); - } + private InventoryAccess() { + throw new IllegalStateException("Cannot create instance of utility class."); + } } diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt index d89987bc..2bedf878 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/BuildToolsValueSource.kt @@ -10,12 +10,12 @@ import java.net.URI import java.nio.file.Files import javax.inject.Inject -abstract class BuildToolsValueSource: ValueSource { +abstract class BuildToolsValueSource : ValueSource { @get:Inject abstract val exec: ExecOperations - interface Parameters: ValueSourceParameters { + interface Parameters : ValueSourceParameters { val mavenLocal: Property val workingDir: DirectoryProperty @@ -55,7 +55,8 @@ abstract class BuildToolsValueSource: ValueSource { +class SpigotReobf : Plugin { companion object { const val ARTIFACT_CONFIG = "reobf" diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt index f2db2c0b..e9e12ede 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotReobfTask.kt @@ -14,7 +14,7 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import java.io.File -abstract class SpigotReobfTask: org.gradle.api.tasks.bundling.Jar() { +abstract class SpigotReobfTask : org.gradle.api.tasks.bundling.Jar() { @get:Input val spigotVersion: Property = objectFactory.property(String::class.java) diff --git a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt index 30c81d3b..f9304be4 100644 --- a/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt +++ b/buildSrc/src/main/kotlin/com/github/jikoo/openinv/SpigotSetup.kt @@ -8,7 +8,7 @@ import org.gradle.kotlin.dsl.create import java.nio.file.Paths import javax.inject.Inject -abstract class SpigotSetup: Plugin { +abstract class SpigotSetup : Plugin { @get:Inject abstract val javaToolchainService: JavaToolchainService @@ -51,7 +51,8 @@ abstract class SpigotSetup: Plugin { spigotExt.version.get(), spigotExt.configuration.orNull, spigotExt.classifier.orNull, - spigotExt.ext.orNull) + spigotExt.ext.orNull + ) target.dependencies.add("compileOnly", dependency) } } diff --git a/buildSrc/src/main/kotlin/openinv-base.gradle.kts b/buildSrc/src/main/kotlin/openinv-base.gradle.kts index 9ef5574d..2369a9e0 100644 --- a/buildSrc/src/main/kotlin/openinv-base.gradle.kts +++ b/buildSrc/src/main/kotlin/openinv-base.gradle.kts @@ -1,8 +1,3 @@ -import org.gradle.jvm.toolchain.JavaLanguageVersion -import org.gradle.kotlin.dsl.maven -import org.gradle.kotlin.dsl.repositories -import org.gradle.kotlin.dsl.dependencies - plugins { `java-library` } diff --git a/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java b/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java index ccd8fd13..d0f9a23e 100644 --- a/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java +++ b/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java @@ -25,13 +25,15 @@ public enum InventoryViewTitle { public @NotNull String getTitle( @NotNull LanguageManager lang, @NotNull Player viewer, - @NotNull ISpecialInventory inventory) { + @NotNull ISpecialInventory inventory + ) { HumanEntity owner = inventory.getPlayer(); String localTitle = lang.getLocalizedMessage( - viewer, - localizationKey, - new Replacement("%player%", owner.getName())); + viewer, + localizationKey, + new Replacement("%player%", owner.getName()) + ); return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + defaultSuffix); } diff --git a/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java b/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java index e999eda5..c60c1557 100644 --- a/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java +++ b/common/src/main/java/com/lishid/openinv/internal/PlayerManager.java @@ -24,31 +24,31 @@ public interface PlayerManager { - /** - * Loads a Player for an OfflinePlayer. - *

- * This method is potentially blocking, and should not be called on the main thread. - * - * @param offline the OfflinePlayer - * @return the Player loaded - */ - @Nullable Player loadPlayer(@NotNull OfflinePlayer offline); + /** + * Loads a Player for an OfflinePlayer. + *

+ * This method is potentially blocking, and should not be called on the main thread. + * + * @param offline the OfflinePlayer + * @return the Player loaded + */ + @Nullable Player loadPlayer(@NotNull OfflinePlayer offline); - /** - * Creates a new Player from an existing one that will function slightly better offline. - * - * @return the Player - */ - @NotNull Player inject(@NotNull Player player); + /** + * Creates a new Player from an existing one that will function slightly better offline. + * + * @return the Player + */ + @NotNull Player inject(@NotNull Player player); - /** - * Opens an ISpecialInventory for a Player. - * - * @param player the Player opening the ISpecialInventory - * @param inventory the Inventory - *` - * @return the InventoryView opened - */ - @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly); + /** + * Opens an ISpecialInventory for a Player. + * + * @param player the Player opening the ISpecialInventory + * @param inventory the Inventory + * + * @return the InventoryView opened + */ + @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly); } diff --git a/common/src/main/java/com/lishid/openinv/util/Permissions.java b/common/src/main/java/com/lishid/openinv/util/Permissions.java index 83d98054..e9836951 100644 --- a/common/src/main/java/com/lishid/openinv/util/Permissions.java +++ b/common/src/main/java/com/lishid/openinv/util/Permissions.java @@ -21,43 +21,43 @@ public enum Permissions { - INVENTORY_OPEN_SELF("inventory.open.self"), - INVENTORY_OPEN_OTHER("inventory.open.other"), - INVENTORY_EDIT_SELF("inventory.edit.self"), - INVENTORY_EDIT_OTHER("inventory.edit.other"), - INVENTORY_SLOT_HEAD_ANY("inventory.slot.head.any"), - INVENTORY_SLOT_CHEST_ANY("inventory.slot.chest.any"), - INVENTORY_SLOT_LEGS_ANY("inventory.slot.legs.any"), - INVENTORY_SLOT_FEET_ANY("inventory.slot.feet.any"), - INVENTORY_SLOT_DROP("inventory.slot.drop"), - - ENDERCHEST_OPEN_SELF("enderchest.open.self"), - ENDERCHEST_OPEN_OTHER("enderchest.open.other"), - ENDERCHEST_EDIT_SELF("enderchest.edit.self"), - ENDERCHEST_EDIT_OTHER("enderchest.edit.other"), - - CLEAR_SELF("clear.self"), - CLEAR_OTHER("clear.other"), - - ACCESS_CROSSWORLD("access.crossworld"), - ACCESS_OFFLINE("access.offline"), - ACCESS_ONLINE("access.online"), - - SPECTATE_CLICK("spectate.click"), - - CONTAINER_ANY("container.any"), - CONTAINER_SILENT("container.silent"), - SEARCH_INVENTORY("search.inventory"), - SEARCH_CONTAINER("search.container"); - - private final String permission; - - Permissions(String permission) { - this.permission = "openinv." + permission; - } - - public boolean hasPermission(@NotNull Permissible permissible) { - return permissible.hasPermission(permission); - } + INVENTORY_OPEN_SELF("inventory.open.self"), + INVENTORY_OPEN_OTHER("inventory.open.other"), + INVENTORY_EDIT_SELF("inventory.edit.self"), + INVENTORY_EDIT_OTHER("inventory.edit.other"), + INVENTORY_SLOT_HEAD_ANY("inventory.slot.head.any"), + INVENTORY_SLOT_CHEST_ANY("inventory.slot.chest.any"), + INVENTORY_SLOT_LEGS_ANY("inventory.slot.legs.any"), + INVENTORY_SLOT_FEET_ANY("inventory.slot.feet.any"), + INVENTORY_SLOT_DROP("inventory.slot.drop"), + + ENDERCHEST_OPEN_SELF("enderchest.open.self"), + ENDERCHEST_OPEN_OTHER("enderchest.open.other"), + ENDERCHEST_EDIT_SELF("enderchest.edit.self"), + ENDERCHEST_EDIT_OTHER("enderchest.edit.other"), + + CLEAR_SELF("clear.self"), + CLEAR_OTHER("clear.other"), + + ACCESS_CROSSWORLD("access.crossworld"), + ACCESS_OFFLINE("access.offline"), + ACCESS_ONLINE("access.online"), + + SPECTATE_CLICK("spectate.click"), + + CONTAINER_ANY("container.any"), + CONTAINER_SILENT("container.silent"), + SEARCH_INVENTORY("search.inventory"), + SEARCH_CONTAINER("search.container"); + + private final String permission; + + Permissions(String permission) { + this.permission = "openinv." + permission; + } + + public boolean hasPermission(@NotNull Permissible permissible) { + return permissible.hasPermission(permission); + } } diff --git a/common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java b/common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java index 2c60ffe0..d22e6fcf 100644 --- a/common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java +++ b/common/src/main/java/com/lishid/openinv/util/ReflectionHelper.java @@ -25,54 +25,54 @@ */ public final class ReflectionHelper { - /** - * Grab an {@link Object} stored in a {@link Field} of another {@code Object}. - * - *

This casts the field to the correct class. Any issues will result in a {@code null} return value. - * - * @param fieldType the {@link Class} of {@code Object} stored in the {@code Field} - * @param holder the containing {@code Object} - * @param the type of stored {@code Object} - * @return the first matching {@code Object} or {@code null} if none match - */ - public static @Nullable T grabObjectByType(final Object holder, final Class fieldType) { - Field field = grabFieldByType(holder.getClass(), fieldType); + /** + * Grab an {@link Object} stored in a {@link Field} of another {@code Object}. + * + *

This casts the field to the correct class. Any issues will result in a {@code null} return value. + * + * @param fieldType the {@link Class} of {@code Object} stored in the {@code Field} + * @param holder the containing {@code Object} + * @param the type of stored {@code Object} + * @return the first matching {@code Object} or {@code null} if none match + */ + public static @Nullable T grabObjectByType(final Object holder, final Class fieldType) { + Field field = grabFieldByType(holder.getClass(), fieldType); - if (field != null) { - try { - return fieldType.cast(field.get(holder)); - } catch (IllegalAccessException ignored) { - // Ignore issues obtaining field - } - } - - return null; + if (field != null) { + try { + return fieldType.cast(field.get(holder)); + } catch (IllegalAccessException ignored) { + // Ignore issues obtaining field + } } - /** - * Grab a {@link Field} of an {@link Object} - * - * @param fieldType the {@link Class} of the object - * @param holderType the containing {@code Class} - * @return the first matching object or {@code null} if none match - */ - public static @Nullable Field grabFieldByType(Class holderType, Class fieldType) { - for (Field field : holderType.getDeclaredFields()) { - if (fieldType.isAssignableFrom(field.getType())) { - field.setAccessible(true); - return field; - } - } - - if (holderType.getSuperclass() != null) { - return grabFieldByType(fieldType, holderType.getSuperclass()); - } + return null; + } - return null; + /** + * Grab a {@link Field} of an {@link Object} + * + * @param fieldType the {@link Class} of the object + * @param holderType the containing {@code Class} + * @return the first matching object or {@code null} if none match + */ + public static @Nullable Field grabFieldByType(Class holderType, Class fieldType) { + for (Field field : holderType.getDeclaredFields()) { + if (fieldType.isAssignableFrom(field.getType())) { + field.setAccessible(true); + return field; + } } - private ReflectionHelper() { - throw new IllegalStateException("Cannot create instance of utility class."); + if (holderType.getSuperclass() != null) { + return grabFieldByType(fieldType, holderType.getSuperclass()); } + return null; + } + + private ReflectionHelper() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + } diff --git a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java index 7696e909..ff1b1bbe 100644 --- a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java +++ b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -49,271 +49,278 @@ */ public class LanguageManager { - private final Plugin plugin; - private final File folder; - private final String defaultLocale; - private final Map locales; - - public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) { - this.plugin = plugin; - this.defaultLocale = defaultLocale; - this.locales = new HashMap<>(); - this.folder = new File(plugin.getDataFolder(), "locale"); - - if (!folder.exists() && !folder.mkdirs()) { - plugin.getLogger().warning(() -> "Unable to create " + folder.getPath() + "! Languages may not be editable."); - } - - reload(); + private final Plugin plugin; + private final File folder; + private final String defaultLocale; + private final Map locales; + + public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) { + this.plugin = plugin; + this.defaultLocale = defaultLocale; + this.locales = new HashMap<>(); + this.folder = new File(plugin.getDataFolder(), "locale"); + + if (!folder.exists() && !folder.mkdirs()) { + plugin.getLogger().warning(() -> "Unable to create " + folder.getPath() + "! Languages may not be editable."); } - public void reload() { - this.locales.clear(); - getOrLoadLocale(defaultLocale); + reload(); + } + + public void reload() { + this.locales.clear(); + getOrLoadLocale(defaultLocale); + } + + private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) { + YamlConfiguration loaded = locales.get(locale); + if (loaded != null) { + return loaded; } - private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) { - YamlConfiguration loaded = locales.get(locale); - if (loaded != null) { - return loaded; - } - - LangLocation lang = bestMatch(locale, null); - - // If a parent was a better match, check if it is already loaded. - if (!locale.equals(lang.locale)) { - loaded = locales.get(lang.locale); - if (loaded != null) { - locales.put(locale, loaded); - return loaded; - } - } - - // Load locale config from disk and bundled locale defaults. - YamlConfiguration localeConfig = loadLocale(lang); - - // If the locale is not the default locale, also handle any missing translations from the default locale. - if (!locale.equals(defaultLocale)) { - addTranslationFallthrough(lang, localeConfig); - - if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true) - && localeConfig.isConfigurationSection("guess")) { - // Warn that guess section exists. This should run once per language per server restart - // when accessed by a user to hint to server owners that they can make UX improvements. - plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + lang.locale + ".yml! Check the guess section!"); - } - } - - locales.put(locale, localeConfig); - locales.put(lang.locale, localeConfig); - - return localeConfig; + LangLocation lang = bestMatch(locale, null); + + // If a parent was a better match, check if it is already loaded. + if (!locale.equals(lang.locale)) { + loaded = locales.get(lang.locale); + if (loaded != null) { + locales.put(locale, loaded); + return loaded; + } } - private @NotNull LangLocation bestMatch(@NotNull String locale, @Nullable LangLocation initial) { - File file = new File(folder, locale + ".yml"); - InputStream bundled = plugin.getResource("locale/" + locale + ".yml"); + // Load locale config from disk and bundled locale defaults. + YamlConfiguration localeConfig = loadLocale(lang); - if (file.exists() || bundled != null) { - return new LangLocation(locale, file, bundled); - } + // If the locale is not the default locale, also handle any missing translations from the default locale. + if (!locale.equals(defaultLocale)) { + addTranslationFallthrough(lang, localeConfig); - if (initial == null) { - initial = new LangLocation(locale, file, null); - } + if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true) + && localeConfig.isConfigurationSection("guess")) { + // Warn that guess section exists. This should run once per language per server restart + // when accessed by a user to hint to server owners that they can make UX improvements. + plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + lang.locale + ".yml! Check the guess section!"); + } + } + + locales.put(locale, localeConfig); + locales.put(lang.locale, localeConfig); - int lastSeparator = locale.lastIndexOf('_'); + return localeConfig; + } - // Must be at least some content before separator. - if (lastSeparator < 1) { - return initial; - } + private @NotNull LangLocation bestMatch(@NotNull String locale, @Nullable LangLocation initial) { + File file = new File(folder, locale + ".yml"); + InputStream bundled = plugin.getResource("locale/" + locale + ".yml"); - return bestMatch(locale.substring(0, lastSeparator), initial); + if (file.exists() || bundled != null) { + return new LangLocation(locale, file, bundled); } - private @NotNull YamlConfiguration loadLocale(@NotNull LangLocation lang) { - YamlConfiguration localeConfigDefaults; - if (lang.bundled == null) { - localeConfigDefaults = new YamlConfiguration(); - } else { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(lang.bundled, StandardCharsets.UTF_8))) { - localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + lang.locale + ".yml"); - localeConfigDefaults = new YamlConfiguration(); - } - } - - if (!lang.file.exists()) { - // If the file does not exist on disk, save bundled defaults. - try { - localeConfigDefaults.save(lang.file); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml"); - } - // Return loaded bundled locale. - return localeConfigDefaults; - } - - // If the file does exist on disk, load it. - YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(lang.file); - // Check for missing translations from the bundled file. - List newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet); - - if (newKeys.isEmpty()) { - return localeConfig; - } - - // Get guess section for missing keys. - ConfigurationSection guess = localeConfig.getConfigurationSection("guess"); - - for (String newKey : newKeys) { - // Set all missing keys to defaults. - localeConfig.set(newKey, localeConfigDefaults.get(newKey)); - - // Delete relevant guess keys in case this is a new translation. - if (guess != null) { - guess.set(newKey, null); - } - } - - // If guess section is empty, delete it. - if (guess != null && guess.getKeys(false).isEmpty()) { - localeConfig.set("guess", null); - } - - plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + lang.locale + ".yml: " + String.join(", ", newKeys)); - - // Write new keys to disk. - try { - localeConfig.save(lang.file); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml"); - } - - return localeConfig; + if (initial == null) { + initial = new LangLocation(locale, file, null); } - private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull YamlConfiguration localeConfig) { - YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale); - - // Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write. - List missingKeys = getMissingKeys( - defaultLocaleConfig, - key -> localeConfig.isSet(key) || localeConfig.isSet("guess." + key)); - - if (!missingKeys.isEmpty()) { - // Set up guess section for missing keys. - for (String key : missingKeys) { - localeConfig.set("guess." + key, defaultLocaleConfig.get(key)); - } - - // Write modified guess section to disk. - try { - localeConfig.save(location.file); - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + location.locale + ".yml"); - } - } - - // Fall through to default locale. - localeConfig.setDefaults(defaultLocaleConfig); + int lastSeparator = locale.lastIndexOf('_'); + + // Must be at least some content before separator. + if (lastSeparator < 1) { + return initial; } - private @NotNull List getMissingKeys( - @NotNull Configuration configurationDefault, - @NotNull Predicate nodeSetPredicate) { - List missingKeys = new ArrayList<>(); - for (String key : configurationDefault.getKeys(true)) { - if (!configurationDefault.isConfigurationSection(key) && !nodeSetPredicate.test(key)) { - // Missing keys are non-section keys that fail the predicate. - missingKeys.add(key); - } - } - return missingKeys; + return bestMatch(locale.substring(0, lastSeparator), initial); + } + + private @NotNull YamlConfiguration loadLocale(@NotNull LangLocation lang) { + YamlConfiguration localeConfigDefaults; + if (lang.bundled == null) { + localeConfigDefaults = new YamlConfiguration(); + } else { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(lang.bundled, StandardCharsets.UTF_8))) { + localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + lang.locale + ".yml"); + localeConfigDefaults = new YamlConfiguration(); + } + } + + if (!lang.file.exists()) { + // If the file does not exist on disk, save bundled defaults. + try { + localeConfigDefaults.save(lang.file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml"); + } + // Return loaded bundled locale. + return localeConfigDefaults; + } + + // If the file does exist on disk, load it. + YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(lang.file); + // Check for missing translations from the bundled file. + List newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet); + + if (newKeys.isEmpty()) { + return localeConfig; } - public @Nullable String getValue(@NotNull String key, @Nullable String locale) { - String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ENGLISH)).getString(key); - if (value == null || value.isBlank()) { - return null; - } + // Get guess section for missing keys. + ConfigurationSection guess = localeConfig.getConfigurationSection("guess"); - value = ChatColor.translateAlternateColorCodes('&', value); + for (String newKey : newKeys) { + // Set all missing keys to defaults. + localeConfig.set(newKey, localeConfigDefaults.get(newKey)); - return value; + // Delete relevant guess keys in case this is a new translation. + if (guess != null) { + guess.set(newKey, null); + } } - public @Nullable String getValue(@NotNull String key, @Nullable String locale, Replacement @NotNull ... replacements) { - String value = getValue(key, locale); + // If guess section is empty, delete it. + if (guess != null && guess.getKeys(false).isEmpty()) { + localeConfig.set("guess", null); + } - if (value == null) { - return null; - } + plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + lang.locale + ".yml: " + String.join(", ", newKeys)); - for (Replacement replacement : replacements) { - value = value.replace(replacement.placeholder(), replacement.value()); - } + // Write new keys to disk. + try { + localeConfig.save(lang.file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml"); + } - return value; + return localeConfig; + } + + private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull YamlConfiguration localeConfig) { + YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale); + + // Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write. + List missingKeys = getMissingKeys( + defaultLocaleConfig, + key -> localeConfig.isSet(key) || localeConfig.isSet("guess." + key) + ); + + if (!missingKeys.isEmpty()) { + // Set up guess section for missing keys. + for (String key : missingKeys) { + localeConfig.set("guess." + key, defaultLocaleConfig.get(key)); + } + + // Write modified guess section to disk. + try { + localeConfig.save(location.file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + location.locale + ".yml"); + } } - public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) { - return getValue(key, getLocale(sender)); + // Fall through to default locale. + localeConfig.setDefaults(defaultLocaleConfig); + } + + private @NotNull List getMissingKeys( + @NotNull Configuration configurationDefault, + @NotNull Predicate nodeSetPredicate + ) { + List missingKeys = new ArrayList<>(); + for (String key : configurationDefault.getKeys(true)) { + if (!configurationDefault.isConfigurationSection(key) && !nodeSetPredicate.test(key)) { + // Missing keys are non-section keys that fail the predicate. + missingKeys.add(key); + } } + return missingKeys; + } - public @Nullable String getLocalizedMessage( - @NotNull CommandSender sender, - @NotNull String key, - Replacement @NotNull ... replacements) { - return getValue(key, getLocale(sender), replacements); + public @Nullable String getValue(@NotNull String key, @Nullable String locale) { + String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ENGLISH)).getString(key); + if (value == null || value.isBlank()) { + return null; } - private @NotNull String getLocale(@NotNull CommandSender sender) { - if (sender instanceof Player) { - return ((Player) sender).getLocale(); - } else { - return plugin.getConfig().getString("settings.locale", "en"); - } + value = ChatColor.translateAlternateColorCodes('&', value); + + return value; + } + + public @Nullable String getValue( + @NotNull String key, + @Nullable String locale, + Replacement @NotNull ... replacements + ) { + String value = getValue(key, locale); + + if (value == null) { + return null; } - public void sendMessage(@NotNull CommandSender sender, @NotNull String key) { - String message = getLocalizedMessage(sender, key); + for (Replacement replacement : replacements) { + value = value.replace(replacement.placeholder(), replacement.value()); + } - if (message != null && !message.isEmpty()) { - sender.sendMessage(message); - } + return value; + } + + public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) { + return getValue(key, getLocale(sender)); + } + + public @Nullable String getLocalizedMessage( + @NotNull CommandSender sender, + @NotNull String key, + Replacement @NotNull ... replacements + ) { + return getValue(key, getLocale(sender), replacements); + } + + private @NotNull String getLocale(@NotNull CommandSender sender) { + if (sender instanceof Player) { + return ((Player) sender).getLocale(); + } else { + return plugin.getConfig().getString("settings.locale", "en"); } + } - public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) { - String message = getLocalizedMessage(sender, key, replacements); + public void sendMessage(@NotNull CommandSender sender, @NotNull String key) { + String message = getLocalizedMessage(sender, key); - if (message != null && !message.isEmpty()) { - sender.sendMessage(message); - } + if (message != null && !message.isEmpty()) { + sender.sendMessage(message); } + } - public void sendSystemMessage(@NotNull Player player, @NotNull String key) { - String message = getLocalizedMessage(player, key); + public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull ... replacements) { + String message = getLocalizedMessage(sender, key, replacements); - if (message == null) { - return; - } + if (message != null && !message.isEmpty()) { + sender.sendMessage(message); + } + } - int newline = message.indexOf('\n'); - if (newline != -1) { - // No newlines in action bar chat. - message = message.substring(0, newline); - } + public void sendSystemMessage(@NotNull Player player, @NotNull String key) { + String message = getLocalizedMessage(player, key); - if (message.isEmpty()) { - return; - } + if (message == null) { + return; + } - player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message)); + int newline = message.indexOf('\n'); + if (newline != -1) { + // No newlines in action bar chat. + message = message.substring(0, newline); } - private record LangLocation(@NotNull String locale, @NotNull File file, @Nullable InputStream bundled) {} + if (message.isEmpty()) { + return; + } + + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message)); + } + + private record LangLocation(@NotNull String locale, @NotNull File file, @Nullable InputStream bundled) {} } diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 4ccfd675..4f926d00 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -18,8 +18,8 @@ plugins { configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val paper = candidates.firstOrNull { - it.id.let { - id -> id is ModuleComponentIdentifier && id.module == "paper-api" + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" } } if (paper != null) { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java index 823d3b5f..da4d8776 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java @@ -52,159 +52,164 @@ public class AnySilentContainer extends AnySilentContainerBase { - private final @NotNull Logger logger; - private final @NotNull LanguageManager lang; - private @Nullable Field serverPlayerGameModeGameType; - - public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { - this.logger = logger; - this.lang = lang; - try { - try { - this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("gameModeForPlayer"); - this.serverPlayerGameModeGameType.setAccessible(true); - } catch (NoSuchFieldException e) { - logger.warning("The field ServerPlayerGameMode#gameModeForPlayer is no longer present!"); - logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); - logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); - // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. - this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); - } - } catch (SecurityException e) { - logger.warning("Unable to directly write player game mode! SilentContainer will fail."); - logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); - } + private final @NotNull Logger logger; + private final @NotNull LanguageManager lang; + private @Nullable Field serverPlayerGameModeGameType; + + public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) { + this.logger = logger; + this.lang = lang; + try { + try { + this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("gameModeForPlayer"); + this.serverPlayerGameModeGameType.setAccessible(true); + } catch (NoSuchFieldException e) { + logger.warning("The field ServerPlayerGameMode#gameModeForPlayer is no longer present!"); + logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues"); + logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail."); + // N.B. gameModeForPlayer is (for now) declared before previousGameModeForPlayer so silent shouldn't break. + this.serverPlayerGameModeGameType = ReflectionHelper.grabFieldByType(ServerPlayerGameMode.class, GameType.class); + } + } catch (SecurityException e) { + logger.warning("Unable to directly write player game mode! SilentContainer will fail."); + logger.log(java.util.logging.Level.WARNING, "Error obtaining GameType field", e); } + } + + @Override + public boolean activateContainer( + @NotNull final Player bukkitPlayer, + final boolean silentchest, + @NotNull final org.bukkit.block.Block bukkitBlock + ) { + + // Silent ender chest is API-only + if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { + bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); + + final net.minecraft.world.level.Level level = player.level(); + final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); + final BlockEntity blockEntity = level.getBlockEntity(blockPos); - @Override - public boolean activateContainer( - @NotNull final Player bukkitPlayer, - final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { - - // Silent ender chest is API-only - if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { - bukkitPlayer.openInventory(bukkitPlayer.getEnderChest()); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - - final net.minecraft.world.level.Level level = player.level(); - final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); - final BlockEntity blockEntity = level.getBlockEntity(blockPos); - - if (blockEntity == null) { - return false; - } - - if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { - // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock - PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); - enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { + if (blockEntity == null) { + return false; + } + + if (blockEntity instanceof EnderChestBlockEntity enderChestTile) { + // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock + PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); + enderChest.setActiveChest(enderChestTile); + player.openMenu( + new SimpleMenuProvider( + (containerCounter, playerInventory, ignored) -> { MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); int rows = enderChest.getContainerSize() / 9; return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable("container.enderchest"))); - bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); - return true; - } - - if (!(blockEntity instanceof MenuProvider menuProvider)) { - return false; - } - - BlockState blockState = level.getBlockState(blockPos); - Block block = blockState.getBlock(); - - if (block instanceof ChestBlock chestBlock) { - - // boolean flag: do not check if chest is blocked - menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); - - if (menuProvider == null) { - lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - - if (block instanceof TrappedChestBlock) { - bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); - } else { - bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); - } - } - - if (block instanceof ShulkerBoxBlock) { - bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); - } - - if (block instanceof BarrelBlock) { - bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); - } - - // AnyChest only - SilentChest not active, container unsupported, or unnecessary. - if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { - player.openMenu(menuProvider); - return true; - } - - // SilentChest requires access to setting players' game mode directly. - if (this.serverPlayerGameModeGameType == null) { - return false; - } - - if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { - if (lootable.lootTable != null) { - lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); - return false; - } - } - - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - player.openMenu(menuProvider); - this.forceGameType(player, gameType); - return true; + }, Component.translatable("container.enderchest") + ) + ); + bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); + return true; + } + + if (!(blockEntity instanceof MenuProvider menuProvider)) { + return false; + } + + BlockState blockState = level.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block instanceof ChestBlock chestBlock) { + + // boolean flag: do not check if chest is blocked + menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true); + + if (menuProvider == null) { + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } + + if (block instanceof TrappedChestBlock) { + bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED); + } else { + bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED); + } + } + + if (block instanceof ShulkerBoxBlock) { + bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED); + } + + if (block instanceof BarrelBlock) { + bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL); + } + + // AnyChest only - SilentChest not active, container unsupported, or unnecessary. + if (!silentchest || player.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + player.openMenu(menuProvider); + return true; + } + + // SilentChest requires access to setting players' game mode directly. + if (this.serverPlayerGameModeGameType == null) { + return false; } - @Override - public void deactivateContainer(@NotNull final Player bukkitPlayer) { - if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { - return; - } - - ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - - // Force game mode change without informing plugins or players. - // Regular game mode set calls GameModeChangeEvent and is cancellable. - GameType gameType = player.gameMode.getGameModeForPlayer(); - this.forceGameType(player, GameType.SPECTATOR); - - // ServerPlayer#closeContainer cannot be called without entering an - // infinite loop because this method is called during inventory close. - // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent - player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); - // From ServerPlayer#closeContainer - player.doCloseContainer(); - // Regular inventory close will handle the rest - packet sending, etc. - - // Revert forced game mode. - this.forceGameType(player, gameType); + if (blockEntity instanceof RandomizableContainerBlockEntity lootable) { + if (lootable.lootTable != null) { + lang.sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); + return false; + } } - private void forceGameType(final ServerPlayer player, final GameType gameMode) { - if (this.serverPlayerGameModeGameType == null) { - // No need to warn repeatedly, error on startup and lack of function should be enough. - return; - } - try { - this.serverPlayerGameModeGameType.setAccessible(true); - this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); - } catch (IllegalArgumentException | IllegalAccessException e) { - logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); - } + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + player.openMenu(menuProvider); + this.forceGameType(player, gameType); + return true; + } + + @Override + public void deactivateContainer(@NotNull final Player bukkitPlayer) { + if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) { + return; + } + + ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); + + // Force game mode change without informing plugins or players. + // Regular game mode set calls GameModeChangeEvent and is cancellable. + GameType gameType = player.gameMode.getGameModeForPlayer(); + this.forceGameType(player, GameType.SPECTATOR); + + // ServerPlayer#closeContainer cannot be called without entering an + // infinite loop because this method is called during inventory close. + // From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent + player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity()); + // From ServerPlayer#closeContainer + player.doCloseContainer(); + // Regular inventory close will handle the rest - packet sending, etc. + + // Revert forced game mode. + this.forceGameType(player, gameType); + } + + private void forceGameType(final ServerPlayer player, final GameType gameMode) { + if (this.serverPlayerGameModeGameType == null) { + // No need to warn repeatedly, error on startup and lack of function should be enough. + return; + } + try { + this.serverPlayerGameModeGameType.setAccessible(true); + this.serverPlayerGameModeGameType.set(player.gameMode, gameMode); + } catch (IllegalArgumentException | IllegalAccessException e) { + logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e); } + } } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java index 4567ea1e..d62606cf 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java @@ -72,7 +72,8 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { } @Override - public void setPlayerOffline() {} + public void setPlayerOffline() { + } @Override public @NotNull org.bukkit.entity.Player getPlayer() { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 4585179b..36217a75 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -112,12 +112,15 @@ private int addMainInventory() { invIndex = localIndex - hotbarDiff; } - slots.set(localIndex, new ContentList(owner, invIndex, type) { - @Override - public void setHolder(@NotNull ServerPlayer holder) { - items = holder.getInventory().getNonEquipmentItems(); - } - }); + slots.set( + localIndex, + new ContentList(owner, invIndex, type) { + @Override + public void setHolder(@NotNull ServerPlayer holder) { + items = holder.getInventory().getNonEquipmentItems(); + } + } + ); } return listSize; } @@ -170,16 +173,17 @@ private int addCrafting(int startIndex, boolean pretty) { if (pretty) { slots.set(startIndex + 2, new ContentViewOnly(owner) { - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotViewOnly(container, slot, x, y) { @Override - public ItemStack getOrDefault() { - return Placeholders.craftingOutput; + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + return Placeholders.craftingOutput; + } + }; } - }; - } - }); + } + ); slots.set(startIndex + 11, getCraftingResult(owner)); } @@ -245,7 +249,8 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { } @Override - public void setPlayerOffline() {} + public void setPlayerOffline() { + } @Override public boolean isInUse() { @@ -298,7 +303,8 @@ public void setMaxStackSize(int maxStackSize) { } @Override - public void setChanged() {} + public void setChanged() { + } @Override public boolean stillValid(@NotNull Player player) { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java index 11275830..e505c8c1 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyInventory.java @@ -109,7 +109,8 @@ public boolean containsAtLeast(@Nullable ItemStack item, int amount) { @SuppressWarnings("NonApiType") @Override public @NotNull HashMap all( - @NotNull Material material) throws IllegalArgumentException { + @NotNull Material material + ) throws IllegalArgumentException { return new HashMap<>(); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java index ed126f71..92958618 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java @@ -84,7 +84,8 @@ public void setArmorContents(ItemStack @NotNull [] items) { for (int index = 0; index < items.length; ++index) { getInventory().getOwnerHandle().getInventory().equipment.set( Inventory.EQUIPMENT_SLOTS_SORTED_BY_INDEX[index], - CraftItemStack.asNMSCopy(items[index])); + CraftItemStack.asNMSCopy(items[index]) + ); } } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index d4c99e2c..4c319825 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -61,7 +61,8 @@ protected OpenChestMenu( int containerCounter, @NotNull T container, @NotNull ServerPlayer viewer, - boolean viewOnly) { + boolean viewOnly + ) { super(type, containerCounter); this.container = container; this.viewer = viewer; @@ -123,7 +124,8 @@ protected OpenChestMenu( }; } - protected void preSlotSetup() {} + protected void preSlotSetup() { + } protected @NotNull Slot getUpperSlot(int index, int x, int y) { Slot slot = new Slot(container, index, x, y); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java index bfd6c619..62d94198 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java @@ -13,7 +13,8 @@ public OpenEnderChestMenu( @NotNull OpenEnderChest enderChest, @NotNull ServerPlayer viewer, int containerId, - boolean viewOnly) { + boolean viewOnly + ) { super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java index c5bf5f88..c9f6c1aa 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java @@ -133,8 +133,11 @@ public boolean isInTop(int rawSlot) { if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) { return null; } - Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, - "Slot %s outside of inventory", rawSlot); + Preconditions.checkArgument( + rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, + "Slot %s outside of inventory", + rawSlot + ); if (rawSlot > topSize) { return getBottomInventory(); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java index a066dc5a..9087232d 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentEquipment.java @@ -3,7 +3,6 @@ import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; -import net.minecraft.world.ContainerHelper; import net.minecraft.world.entity.EntityEquipment; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.inventory.Slot; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java index ae56f9a1..993a5caa 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java @@ -43,7 +43,8 @@ public void setChanged() { holder.inventoryMenu.containerId, holder.inventoryMenu.incrementStateId(), InventoryMenu.SHIELD_SLOT, - holder.getOffhandItem())); + holder.getOffhandItem() + )); } } }; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java index c054688d..02d8338c 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java @@ -31,7 +31,8 @@ protected PlaceholderLoaderBase() { ItemStack typeItem = new ItemStack(Items.BARRIER); typeItem.set( DataComponents.ITEM_NAME, - Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName())); + Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName()) + ); Placeholders.BLOCKED_GAME_TYPE.put(type, typeItem); } Placeholders.craftingOutput = defaultCraftingOutput(); @@ -64,7 +65,8 @@ public void load(@Nullable ConfigurationSection section) throws Exception { private @NotNull ItemStack parse( @Nullable ConfigurationSection section, @NotNull String path, - @NotNull ItemStack defaultStack) throws Exception { + @NotNull ItemStack defaultStack + ) throws Exception { if (section == null) { return defaultStack; } @@ -108,7 +110,9 @@ public void load(@Nullable ConfigurationSection section) throws Exception { new BannerPatternLayers(List.of( new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY) + )) + ); addModelData(itemStack); hideTooltip(itemStack); return itemStack; @@ -150,7 +154,9 @@ public void load(@Nullable ConfigurationSection section) throws Exception { new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK) + )) + ); hideTooltip(itemStack); addModelData(itemStack); return itemStack; @@ -170,7 +176,8 @@ public void load(@Nullable ConfigurationSection section) throws Exception { itemStack.set(DataComponents.ITEM_NAME, Component.translatable("options.narrator.notavailable") .append(Component.literal(" - ")) - .append(Component.translatable("gui.socialInteractions.status_offline"))); + .append(Component.translatable("gui.socialInteractions.status_offline")) + ); return itemStack; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 43632e6e..267d9335 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -145,7 +145,8 @@ private void copyValue( @NotNull CompoundTag target, @NotNull String container, @NotNull String key, - @SuppressWarnings("SameParameterValue") @NotNull Class tagType) { + @SuppressWarnings("SameParameterValue") @NotNull Class tagType + ) { CompoundTag oldContainer = getTag(source, container, CompoundTag.class); CompoundTag newContainer = getTag(target, container, CompoundTag.class); @@ -161,7 +162,8 @@ private void copyValue( private @Nullable T getTag( @Nullable CompoundTag container, @NotNull String key, - @NotNull Class dataType) { + @NotNull Class dataType + ) { if (container == null) { return null; } @@ -175,7 +177,8 @@ private void copyValue( private void setTag( @NotNull CompoundTag container, @NotNull String key, - @Nullable T data) { + @Nullable T data + ) { if (data == null) { container.remove(key); } else { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index e63ff119..9d67d448 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -101,11 +101,13 @@ public PlayerManager(@NotNull Logger logger) { protected @NotNull ServerPlayer createNewPlayer( @NotNull MinecraftServer server, @NotNull ServerLevel worldServer, - @NotNull final OfflinePlayer offline) { + @NotNull final OfflinePlayer offline + ) { // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString() + ); ClientInformation dummyInfo = new ClientInformation( "en_us", @@ -127,7 +129,8 @@ public PlayerManager(@NotNull Logger logger) { logger.log( java.util.logging.Level.WARNING, e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); } return entity; @@ -219,13 +222,17 @@ protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { logger.log( java.util.logging.Level.WARNING, e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); return player; } } @Override - public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) { + public @Nullable InventoryView openInventory( + @NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, + boolean viewOnly + ) { ServerPlayer player = getHandle(bukkitPlayer); if (!OpenPlayer.isConnected(player.connection)) { diff --git a/internal/paper1_21_1/build.gradle.kts b/internal/paper1_21_1/build.gradle.kts index 3a24a450..05fe6f68 100644 --- a/internal/paper1_21_1/build.gradle.kts +++ b/internal/paper1_21_1/build.gradle.kts @@ -6,8 +6,8 @@ plugins { configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val paper = candidates.firstOrNull { - it.id.let { - id -> id is ModuleComponentIdentifier && id.module == "paper-api" + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" } } if (paper != null) { diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java index 604a358b..0ac03f26 100644 --- a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/container/slot/placeholder/PlaceholderLoader.java @@ -28,11 +28,16 @@ public class PlaceholderLoader extends NumericDataPlaceholderLoader { BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT); BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT); BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER); - itemStack.set(DataComponents.BANNER_PATTERNS, - new BannerPatternLayers(List.of( - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)))); + itemStack.set( + DataComponents.BANNER_PATTERNS, + new BannerPatternLayers( + List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY) + ) + ) + ); addModelData(itemStack); itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); return itemStack; @@ -51,12 +56,16 @@ public class PlaceholderLoader extends NumericDataPlaceholderLoader { BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT); BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT); itemStack.set(DataComponents.BANNER_PATTERNS, - new BannerPatternLayers(List.of( - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), - new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)))); + new BannerPatternLayers( + List.of( + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK), + new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK) + ) + ) + ); itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE); addModelData(itemStack); return itemStack; diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java index 60c1a04b..bd2ec0fd 100644 --- a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java @@ -21,11 +21,13 @@ public PlayerManager(@NotNull Logger logger) { protected @NotNull ServerPlayer createNewPlayer( @NotNull MinecraftServer server, @NotNull ServerLevel worldServer, - @NotNull final OfflinePlayer offline) { + @NotNull final OfflinePlayer offline + ) { // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString() + ); ClientInformation dummyInfo = new ClientInformation( "en_us", @@ -46,7 +48,8 @@ public PlayerManager(@NotNull Logger logger) { logger.log( java.util.logging.Level.WARNING, e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); } return entity; diff --git a/internal/paper1_21_3/build.gradle.kts b/internal/paper1_21_3/build.gradle.kts index 91d2ae33..2de54978 100644 --- a/internal/paper1_21_3/build.gradle.kts +++ b/internal/paper1_21_3/build.gradle.kts @@ -6,8 +6,8 @@ plugins { configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val paper = candidates.firstOrNull { - it.id.let { - id -> id is ModuleComponentIdentifier && id.module == "paper-api" + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" } } if (paper != null) { diff --git a/internal/paper1_21_4/build.gradle.kts b/internal/paper1_21_4/build.gradle.kts index d83eda69..9716b3ed 100644 --- a/internal/paper1_21_4/build.gradle.kts +++ b/internal/paper1_21_4/build.gradle.kts @@ -6,8 +6,8 @@ plugins { configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val paper = candidates.firstOrNull { - it.id.let { - id -> id is ModuleComponentIdentifier && id.module == "paper-api" + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" } } if (paper != null) { diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java index dfc4f35f..a10b6c58 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java @@ -7,13 +7,17 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenEnderChest extends com.lishid.openinv.internal.common.container.OpenEnderChest{ +public class OpenEnderChest extends com.lishid.openinv.internal.common.container.OpenEnderChest { public OpenEnderChest(@NotNull Player player) { super(player); } - public @Nullable AbstractContainerMenu createMenu(net.minecraft.world.entity.player.Player player, int i, boolean viewOnly) { + public @Nullable AbstractContainerMenu createMenu( + net.minecraft.world.entity.player.Player player, + int i, + boolean viewOnly + ) { if (player instanceof ServerPlayer serverPlayer) { return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly); } diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java index 69e3883f..64ac4255 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java @@ -80,12 +80,15 @@ private int addMainInventory() { invIndex = localIndex - hotbarDiff; } - slots.set(localIndex, new ContentList(owner, invIndex, type) { - @Override - public void setHolder(@NotNull ServerPlayer holder) { - items = holder.getInventory().items; - } - }); + slots.set( + localIndex, + new ContentList(owner, invIndex, type) { + @Override + public void setHolder(@NotNull ServerPlayer holder) { + items = holder.getInventory().items; + } + } + ); } return listSize; } @@ -152,16 +155,17 @@ private int addCrafting(int startIndex, boolean pretty) { if (pretty) { slots.set(startIndex + 2, new ContentViewOnly(owner) { - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotViewOnly(container, slot, x, y) { @Override - public ItemStack getOrDefault() { - return Placeholders.craftingOutput; + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + return Placeholders.craftingOutput; + } + }; } - }; - } - }); + } + ); slots.set(startIndex + 11, getCraftingResult(owner)); } diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java index 7c4f0f9f..f82a63e6 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java @@ -59,7 +59,8 @@ protected OpenChestMenu( int containerCounter, @NotNull T container, @NotNull ServerPlayer viewer, - boolean viewOnly) { + boolean viewOnly + ) { super(type, containerCounter); this.container = container; this.viewer = viewer; @@ -108,7 +109,8 @@ protected OpenChestMenu( } } - protected void preSlotSetup() {} + protected void preSlotSetup() { + } protected @NotNull Slot getUpperSlot(int index, int x, int y) { Slot slot = new Slot(container, index, x, y); diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java index bf26040f..902dd60b 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java @@ -13,7 +13,8 @@ public OpenEnderChestMenu( @NotNull OpenEnderChest enderChest, @NotNull ServerPlayer viewer, int containerId, - boolean viewOnly) { + boolean viewOnly + ) { super( com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(enderChest.getContainerSize()), containerId, diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java index 136e6b91..94c394fc 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java @@ -133,8 +133,11 @@ public boolean isInTop(int rawSlot) { if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) { return null; } - Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, - "Slot %s outside of inventory", rawSlot); + Preconditions.checkArgument( + rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE, + "Slot %s outside of inventory", + rawSlot + ); if (rawSlot > topSize) { return getBottomInventory(); } diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java index 178ebb3a..e6c798c1 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java @@ -43,7 +43,8 @@ public void setChanged() { holder.inventoryMenu.containerId, holder.inventoryMenu.incrementStateId(), InventoryMenu.SHIELD_SLOT, - holder.getOffhandItem())); + holder.getOffhandItem() + )); } } }; diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java index 5dbe80bf..1d58c71f 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java @@ -10,8 +10,10 @@ public class OpenPlayer extends com.lishid.openinv.internal.common.player.OpenPlayer { - protected OpenPlayer(CraftServer server, ServerPlayer entity, - PlayerManager manager) { + protected OpenPlayer( + CraftServer server, ServerPlayer entity, + PlayerManager manager + ) { super(server, entity, manager); } diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 73723aea..5152c4a5 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -17,8 +17,8 @@ rootProject.extra["craftbukkitPackage"] = "v1_21_R4" configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { val spigot = candidates.firstOrNull { - it.id.let { - id -> id is ModuleComponentIdentifier && id.module == "spigot-api" + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "spigot-api" } } if (spigot != null) { diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java index f19091f2..5b3c1559 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/AnySilentContainer.java @@ -64,7 +64,8 @@ public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) public boolean activateContainer( @NotNull final Player bukkitPlayer, final boolean silentchest, - @NotNull final org.bukkit.block.Block bukkitBlock) { + @NotNull final org.bukkit.block.Block bukkitBlock + ) { // Silent ender chest is API-only if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) { @@ -87,11 +88,16 @@ public boolean activateContainer( // Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock PlayerEnderChestContainer enderChest = player.getEnderChestInventory(); enderChest.setActiveChest(enderChestTile); - player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> { - MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); - int rows = enderChest.getContainerSize() / 9; - return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); - }, Component.translatable("container.enderchest"))); + player.openMenu( + new SimpleMenuProvider( + (containerCounter, playerInventory, ignored) -> { + MenuType containers = OpenChestMenu.getChestMenuType(enderChest.getContainerSize()); + int rows = enderChest.getContainerSize() / 9; + return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows); + }, + Component.translatable("container.enderchest") + ) + ); bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED); return true; } diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java index d7a173b5..5052987a 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentOffHand.java @@ -40,7 +40,8 @@ public void setChanged() { holder.inventoryMenu.containerId, holder.inventoryMenu.incrementStateId(), InventoryMenu.SHIELD_SLOT, - holder.getOffhandItem())); + holder.getOffhandItem() + )); } } }; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 213bcf7b..3b964549 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -93,11 +93,13 @@ public PlayerManager(@NotNull Logger logger) { private @NotNull ServerPlayer createNewPlayer( @NotNull MinecraftServer server, @NotNull ServerLevel worldServer, - @NotNull final OfflinePlayer offline) { + @NotNull final OfflinePlayer offline + ) { // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile) // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket) GameProfile profile = new GameProfile(offline.getUniqueId(), - offline.getName() != null ? offline.getName() : offline.getUniqueId().toString()); + offline.getName() != null ? offline.getName() : offline.getUniqueId().toString() + ); ClientInformation dummyInfo = new ClientInformation( "en_us", @@ -119,7 +121,8 @@ public PlayerManager(@NotNull Logger logger) { logger.log( java.util.logging.Level.WARNING, e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); } return entity; @@ -167,13 +170,17 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { logger.log( java.util.logging.Level.WARNING, e, - () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"); + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); return player; } } @Override - public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) { + public @Nullable InventoryView openInventory( + @NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, + boolean viewOnly + ) { ServerPlayer player = getHandle(bukkitPlayer); if (!OpenPlayer.isConnected(player.connection)) { diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 06278971..570452ac 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -62,244 +62,249 @@ */ public class OpenInv extends FoliaWrappedJavaPlugin implements IOpenInv { - private InternalAccessor accessor; - private Config config; - private InventoryManager inventoryManager; - private LanguageManager languageManager; - private PlayerLoader playerLoader; - private boolean isSpigot = false; - - @Override - public void reloadConfig() { - super.reloadConfig(); - config.reload(getConfig()); - languageManager.reload(); - if (accessor != null && accessor.isSupported()) { - accessor.reload(getConfig()); - } + private InternalAccessor accessor; + private Config config; + private InventoryManager inventoryManager; + private LanguageManager languageManager; + private PlayerLoader playerLoader; + private boolean isSpigot = false; + + @Override + public void reloadConfig() { + super.reloadConfig(); + config.reload(getConfig()); + languageManager.reload(); + if (accessor != null && accessor.isSupported()) { + accessor.reload(getConfig()); } - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!isSpigot || !this.accessor.isSupported()) { - this.sendVersionError(sender::sendMessage); - return true; - } - return false; + } + + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (!isSpigot || !this.accessor.isSupported()) { + this.sendVersionError(sender::sendMessage); + return true; } - - @Override - public void onDisable() { - inventoryManager.evictAll(); + return false; + } + + @Override + public void onDisable() { + inventoryManager.evictAll(); + } + + @Override + public void onEnable() { + // Save default configuration if not present. + this.saveDefaultConfig(); + + // Migrate locale files to a subfolder. + Path dataFolder = getDataFolder().toPath(); + new LangMigrator(dataFolder, dataFolder.resolve("locale"), getLogger()).migrate(); + + // Set up configurable features. Note that #reloadConfig is called on the first call to #getConfig! + // Configuration values should not be accessed until after all of these have been set up. + config = new Config(); + languageManager = new LanguageManager(this, "en"); + accessor = new InternalAccessor(getLogger(), languageManager); + + // Perform initial config load. + reloadConfig(); + + inventoryManager = new InventoryManager(this, config, accessor); + playerLoader = new PlayerLoader(this, config, inventoryManager, accessor, getLogger()); + + try { + Class.forName("org.bukkit.entity.Player$Spigot"); + isSpigot = true; + } catch (ClassNotFoundException e) { + isSpigot = false; } - @Override - public void onEnable() { - // Save default configuration if not present. - this.saveDefaultConfig(); + // Version check + if (isSpigot && this.accessor.isSupported()) { + reloadConfig(); - // Migrate locale files to a subfolder. - Path dataFolder = getDataFolder().toPath(); - new LangMigrator(dataFolder, dataFolder.resolve("locale"), getLogger()).migrate(); + // Update existing configuration. May require internal access. + new ConfigUpdater(this).checkForUpdates(); - // Set up configurable features. Note that #reloadConfig is called on the first call to #getConfig! - // Configuration values should not be accessed until after all of these have been set up. - config = new Config(); - languageManager = new LanguageManager(this, "en"); - accessor = new InternalAccessor(getLogger(), languageManager); + // Register relevant event listeners. + registerEvents(); - // Perform initial config load. - reloadConfig(); - - inventoryManager = new InventoryManager(this, config, accessor); - playerLoader = new PlayerLoader(this, config, inventoryManager, accessor, getLogger()); - - try { - Class.forName("org.bukkit.entity.Player$Spigot"); - isSpigot = true; - } catch (ClassNotFoundException e) { - isSpigot = false; - } - - // Version check - if (isSpigot && this.accessor.isSupported()) { - reloadConfig(); - - // Update existing configuration. May require internal access. - new ConfigUpdater(this).checkForUpdates(); - - // Register relevant event listeners. - registerEvents(); - - // Register commands to their executors. - registerCommands(); - - } else { - this.sendVersionError(this.getLogger()::warning); - } + // Register commands to their executors. + registerCommands(); + } else { + this.sendVersionError(this.getLogger()::warning); } - private void registerEvents() { - PluginManager pluginManager = this.getServer().getPluginManager(); - pluginManager.registerEvents(playerLoader, this); - pluginManager.registerEvents(inventoryManager, this); - pluginManager.registerEvents(new ContainerListener(accessor, languageManager), this); - pluginManager.registerEvents(new ToggleListener(), this); + } + + private void registerEvents() { + PluginManager pluginManager = this.getServer().getPluginManager(); + pluginManager.registerEvents(playerLoader, this); + pluginManager.registerEvents(inventoryManager, this); + pluginManager.registerEvents(new ContainerListener(accessor, languageManager), this); + pluginManager.registerEvents(new ToggleListener(), this); + } + + private void registerCommands() { + this.setCommandExecutor(new OpenInvCommand(this, config, inventoryManager, languageManager, playerLoader), "openinv", "openender"); + this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); + this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); + this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); + this.setCommandExecutor(new ClearInvCommand(this, config, inventoryManager, languageManager, playerLoader), "clearinv", "clearender"); + + ContainerSettingCommand settingCommand = new ContainerSettingCommand(languageManager); + for (PlayerToggle toggle : PlayerToggles.get()) { + setCommandExecutor(settingCommand, toggle.getName().toLowerCase(Locale.ENGLISH)); } - - private void registerCommands() { - this.setCommandExecutor(new OpenInvCommand(this, config, inventoryManager, languageManager, playerLoader), "openinv", "openender"); - this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer"); - this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender"); - this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant"); - this.setCommandExecutor(new ClearInvCommand(this, config, inventoryManager, languageManager, playerLoader), "clearinv", "clearender"); - - ContainerSettingCommand settingCommand = new ContainerSettingCommand(languageManager); - for (PlayerToggle toggle : PlayerToggles.get()) { - setCommandExecutor(settingCommand, toggle.getName().toLowerCase(Locale.ENGLISH)); - } - } - - private void setCommandExecutor(@NotNull CommandExecutor executor, String @NotNull ... commands) { - for (String commandName : commands) { - PluginCommand command = this.getCommand(commandName); - if (command != null) { - command.setExecutor(executor); - } - } + } + + private void setCommandExecutor(@NotNull CommandExecutor executor, String @NotNull ... commands) { + for (String commandName : commands) { + PluginCommand command = this.getCommand(commandName); + if (command != null) { + command.setExecutor(executor); + } } + } - private void sendVersionError(@NotNull Consumer messageMethod) { - if (!accessor.isSupported()) { - messageMethod.accept("Your server version (" + accessor.getVersion() + ") is not supported."); - messageMethod.accept("Please download the correct version of OpenInv here: " + accessor.getReleasesLink()); - } - if (!isSpigot) { - messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread"); - messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are"); - messageMethod.accept("encountering an inconsistency with vanilla that prevents you from using Spigot,"); - messageMethod.accept("that is considered a Spigot bug and should be reported as such."); - } + private void sendVersionError(@NotNull Consumer messageMethod) { + if (!accessor.isSupported()) { + messageMethod.accept("Your server version (" + accessor.getVersion() + ") is not supported."); + messageMethod.accept("Please download the correct version of OpenInv here: " + accessor.getReleasesLink()); } - - @Override - public boolean isSupportedVersion() { - return this.accessor != null && this.accessor.isSupported(); + if (!isSpigot) { + messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread"); + messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are"); + messageMethod.accept("encountering an inconsistency with vanilla that prevents you from using Spigot,"); + messageMethod.accept("that is considered a Spigot bug and should be reported as such."); } - - @Override - public boolean disableSaving() { - return config.isSaveDisabled(); + } + + @Override + public boolean isSupportedVersion() { + return this.accessor != null && this.accessor.isSupported(); + } + + @Override + public boolean disableSaving() { + return config.isSaveDisabled(); + } + + @Override + public boolean disableOfflineAccess() { + return config.isOfflineDisabled(); + } + + @Override + public boolean noArgsOpensSelf() { + return config.doesNoArgsOpenSelf(); + } + + @Override + public @NotNull IAnySilentContainer getAnySilentContainer() { + return this.accessor.getAnySilentContainer(); + } + + @Override + public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) { + return PlayerToggles.any().is(offline.getUniqueId()); + } + + @Override + public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { + PlayerToggles.any().set(offline.getUniqueId(), status); + } + + @Override + public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) { + return PlayerToggles.silent().is(offline.getUniqueId()); + } + + @Override + public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { + PlayerToggles.silent().set(offline.getUniqueId(), status); + } + + @Override + public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) { + return inventoryManager.getEnderChest(player); + } + + @Override + public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) { + return inventoryManager.getInventory(player); + } + + @Override + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { + Permissions edit = null; + HumanEntity target = inventory.getPlayer(); + boolean ownContainer = player.equals(target); + if (inventory instanceof ISpecialPlayerInventory) { + edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; + } else if (inventory instanceof ISpecialEnderChest) { + edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_EDIT_OTHER; } - @Override - public boolean disableOfflineAccess() { - return config.isOfflineDisabled(); - } + boolean viewOnly = edit != null && !edit.hasPermission(player); - @Override - public boolean noArgsOpensSelf() { - return config.doesNoArgsOpenSelf(); + if (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) { + return this.accessor.openInventory(player, inventory, viewOnly); } - @Override - public @NotNull IAnySilentContainer getAnySilentContainer() { - return this.accessor.getAnySilentContainer(); - } - - @Override - public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) { - return PlayerToggles.any().is(offline.getUniqueId()); - } - - @Override - public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { - PlayerToggles.any().set(offline.getUniqueId(), status); - } - - @Override - public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) { - return PlayerToggles.silent().is(offline.getUniqueId()); - } - - @Override - public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) { - PlayerToggles.silent().set(offline.getUniqueId(), status); - } - - @Override - public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) { - return inventoryManager.getEnderChest(player); - } - - @Override - public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) { - return inventoryManager.getInventory(player); - } - - @Override - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { - Permissions edit = null; - HumanEntity target = inventory.getPlayer(); - boolean ownContainer = player.equals(target); - if (inventory instanceof ISpecialPlayerInventory) { - edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; - } else if (inventory instanceof ISpecialEnderChest) { - edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_EDIT_OTHER; - } - - boolean viewOnly = edit != null && !edit.hasPermission(player); - - if (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) { - return this.accessor.openInventory(player, inventory, viewOnly); - } - - for (int level = 4; level > 0; --level) { - String permission = "openinv.access.level." + level; - // If the target doesn't have this access level... - if (!target.hasPermission(permission)) { - // If the viewer does have the access level, all good. - if (player.hasPermission(permission)) { - break; - } - // Otherwise check next access level. - continue; - } - - // If the viewer doesn't have an equal access level or equal access is a denial, deny. - if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) { - return null; - } - - // Since this is a tie, setting decides view state. - if (config.getAccessEqualMode() == AccessEqualMode.VIEW) { - viewOnly = true; - } - break; + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + // If the target doesn't have this access level... + if (!target.hasPermission(permission)) { + // If the viewer does have the access level, all good. + if (player.hasPermission(permission)) { + break; } - - return this.accessor.openInventory(player, inventory, viewOnly); + // Otherwise check next access level. + continue; + } + + // If the viewer doesn't have an equal access level or equal access is a denial, deny. + if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) { + return null; + } + + // Since this is a tie, setting decides view state. + if (config.getAccessEqualMode() == AccessEqualMode.VIEW) { + viewOnly = true; + } + break; } - @Override - public boolean isPlayerLoaded(@NotNull UUID playerUuid) { - return inventoryManager.getLoadedPlayer(playerUuid) != null; - } + return this.accessor.openInventory(player, inventory, viewOnly); + } - @Override - public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - return playerLoader.load(offline); - } + @Override + public boolean isPlayerLoaded(@NotNull UUID playerUuid) { + return inventoryManager.getLoadedPlayer(playerUuid) != null; + } - @Override - public @Nullable OfflinePlayer matchPlayer(@NotNull String name) { - return playerLoader.match(name); - } + @Override + public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { + return playerLoader.load(offline); + } - @Override - public void unload(@NotNull final OfflinePlayer offline) { - inventoryManager.unload(offline.getUniqueId()); - } + @Override + public @Nullable OfflinePlayer matchPlayer(@NotNull String name) { + return playerLoader.match(name); + } + + @Override + public void unload(@NotNull final OfflinePlayer offline) { + inventoryManager.unload(offline.getUniqueId()); + } } diff --git a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java index 21b1fd8d..8cd585d3 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java @@ -19,77 +19,77 @@ public class ClearInvCommand extends PlayerLookupCommand { - private final @NotNull InventoryManager manager; + private final @NotNull InventoryManager manager; - public ClearInvCommand( - @NotNull OpenInv plugin, - @NotNull Config config, - @NotNull InventoryManager manager, - @NotNull LanguageManager lang, - @NotNull PlayerLoader playerLoader - ) { - super(plugin, lang, config, playerLoader); - this.manager = manager; - } + public ClearInvCommand( + @NotNull OpenInv plugin, + @NotNull Config config, + @NotNull InventoryManager manager, + @NotNull LanguageManager lang, + @NotNull PlayerLoader playerLoader + ) { + super(plugin, lang, config, playerLoader); + this.manager = manager; + } - @Override - protected boolean isAccessInventory(@NotNull Command command) { - return command.getName().equals("clearinv"); - } + @Override + protected boolean isAccessInventory(@NotNull Command command) { + return command.getName().equals("clearinv"); + } - @Override - protected @Nullable String getTargetIdentifer( - @NotNull CommandSender sender, - @Nullable String argument, - boolean accessInv - ) { - if (argument != null) { - return argument; - } - if (sender instanceof Player player) { - return player.getUniqueId().toString(); - } - return null; + @Override + protected @Nullable String getTargetIdentifer( + @NotNull CommandSender sender, + @Nullable String argument, + boolean accessInv + ) { + if (argument != null) { + return argument; } - - @Override - protected @Nullable OfflinePlayer getTarget(@NotNull String identifier) { - return playerLoader.matchExact(identifier); + if (sender instanceof Player player) { + return player.getUniqueId().toString(); } + return null; + } - @Override - protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv) { - if (onlineTarget.equals(sender)) { - return !Permissions.CLEAR_SELF.hasPermission(sender); - } - return !Permissions.CLEAR_OTHER.hasPermission(sender); - } + @Override + protected @Nullable OfflinePlayer getTarget(@NotNull String identifier) { + return playerLoader.matchExact(identifier); + } - @Override - protected void handle( - @NotNull CommandSender sender, - @NotNull Player onlineTarget, - boolean accessInv, - @NotNull String @NotNull [] args - ) { - // Create the inventory - final ISpecialInventory inv; - try { - inv = accessInv ? manager.getInventory(onlineTarget) : manager.getEnderChest(onlineTarget); - } catch (Exception e) { - lang.sendMessage(sender, "messages.error.commandException"); - plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); - return; - } + @Override + protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv) { + if (onlineTarget.equals(sender)) { + return !Permissions.CLEAR_SELF.hasPermission(sender); + } + return !Permissions.CLEAR_OTHER.hasPermission(sender); + } - // Clear the inventory - inv.getBukkitInventory().clear(); - manager.save(onlineTarget.getUniqueId()); - lang.sendMessage( - sender, - "messages.info.inventoryCleared", - new Replacement("%target%", onlineTarget.getDisplayName()) - ); + @Override + protected void handle( + @NotNull CommandSender sender, + @NotNull Player onlineTarget, + boolean accessInv, + @NotNull String @NotNull [] args + ) { + // Create the inventory + final ISpecialInventory inv; + try { + inv = accessInv ? manager.getInventory(onlineTarget) : manager.getEnderChest(onlineTarget); + } catch (Exception e) { + lang.sendMessage(sender, "messages.error.commandException"); + plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); + return; } + // Clear the inventory + inv.getBukkitInventory().clear(); + manager.save(onlineTarget.getUniqueId()); + lang.sendMessage( + sender, + "messages.info.inventoryCleared", + new Replacement("%target%", onlineTarget.getDisplayName()) + ); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java index af00131b..e7b1c6b8 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ContainerSettingCommand.java @@ -17,11 +17,11 @@ package com.lishid.openinv.command; import com.lishid.openinv.event.OpenEvents; -import com.lishid.openinv.util.setting.PlayerToggle; import com.lishid.openinv.util.TabCompleter; -import com.lishid.openinv.util.setting.PlayerToggles; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import com.lishid.openinv.util.setting.PlayerToggle; +import com.lishid.openinv.util.setting.PlayerToggles; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -36,72 +36,83 @@ public class ContainerSettingCommand implements TabExecutor { - private final @NotNull LanguageManager lang; + private final @NotNull LanguageManager lang; + + public ContainerSettingCommand(@NotNull LanguageManager lang) { + this.lang = lang; + } + + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (!(sender instanceof Player player)) { + lang.sendMessage(sender, "messages.error.consoleUnsupported"); + return true; + } + + PlayerToggle toggle = PlayerToggles.get(command.getName()); - public ContainerSettingCommand(@NotNull LanguageManager lang) { - this.lang = lang; + // Shouldn't be possible. + if (toggle == null) { + JavaPlugin.getProvidingPlugin(getClass()).getLogger().warning("Command /" + command.getName() + " registered with no corresponding toggle!"); + return false; } - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!(sender instanceof Player player)) { - lang.sendMessage(sender, "messages.error.consoleUnsupported"); - return true; - } - - PlayerToggle toggle = PlayerToggles.get(command.getName()); - - // Shouldn't be possible. - if (toggle == null) { - JavaPlugin.getProvidingPlugin(getClass()).getLogger().warning("Command /" + command.getName() + " registered with no corresponding toggle!"); - return false; - } - - UUID playerId = player.getUniqueId(); - - if (args.length > 0) { - args[0] = args[0].toLowerCase(Locale.ENGLISH); - - if (args[0].equals("on")) { - set(toggle, playerId, true); - } else if (args[0].equals("off")) { - set(toggle, playerId, false); - } else if (!args[0].equals("check")) { - // Invalid argument, show usage. - return false; - } - - } else { - set(toggle, playerId, !toggle.is(playerId)); - } - - String onOff = lang.getLocalizedMessage(player, toggle.is(playerId) ? "messages.info.on" : "messages.info.off"); - if (onOff == null) { - onOff = String.valueOf(toggle.is(playerId)); - } - - lang.sendMessage( - sender, - "messages.info.settingState", - new Replacement("%setting%", toggle.getName()), - new Replacement("%state%", onOff)); - - return true; + UUID playerId = player.getUniqueId(); + + if (args.length > 0) { + args[0] = args[0].toLowerCase(Locale.ENGLISH); + + if (args[0].equals("on")) { + set(toggle, playerId, true); + } else if (args[0].equals("off")) { + set(toggle, playerId, false); + } else if (!args[0].equals("check")) { + // Invalid argument, show usage. + return false; + } + + } else { + set(toggle, playerId, !toggle.is(playerId)); } - private void set(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) { - if (toggle.set(uuid, state)) { - OpenEvents.notifyPlayerToggle(toggle, uuid, state); - } + String onOff = lang.getLocalizedMessage(player, toggle.is(playerId) ? "messages.info.on" : "messages.info.off"); + if (onOff == null) { + onOff = String.valueOf(toggle.is(playerId)); } - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!command.testPermissionSilent(sender) || args.length != 1) { - return Collections.emptyList(); - } + lang.sendMessage( + sender, + "messages.info.settingState", + new Replacement("%setting%", toggle.getName()), + new Replacement("%state%", onOff) + ); - return TabCompleter.completeString(args[0], new String[] {"check", "on", "off"}); + return true; + } + + private void set(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) { + if (toggle.set(uuid, state)) { + OpenEvents.notifyPlayerToggle(toggle, uuid, state); + } + } + + @Override + public List onTabComplete( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (!command.testPermissionSilent(sender) || args.length != 1) { + return Collections.emptyList(); } + return TabCompleter.completeString(args[0], new String[]{"check", "on", "off"}); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index b90a3e46..14a5bdf9 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -39,139 +39,139 @@ public class OpenInvCommand extends PlayerLookupCommand { - private final @NotNull InventoryManager manager; - private final Map openInvHistory = new WeakHashMap<>(); - private final Map openEnderHistory = new WeakHashMap<>(); - - public OpenInvCommand( - @NotNull OpenInv plugin, - @NotNull Config config, - @NotNull InventoryManager manager, - @NotNull LanguageManager lang, - @NotNull PlayerLoader playerLoader - ) { - super(plugin, lang, config, playerLoader); - this.manager = manager; + private final @NotNull InventoryManager manager; + private final Map openInvHistory = new WeakHashMap<>(); + private final Map openEnderHistory = new WeakHashMap<>(); + + public OpenInvCommand( + @NotNull OpenInv plugin, + @NotNull Config config, + @NotNull InventoryManager manager, + @NotNull LanguageManager lang, + @NotNull PlayerLoader playerLoader + ) { + super(plugin, lang, config, playerLoader); + this.manager = manager; + } + + @Override + protected boolean isAccessInventory(@NotNull Command command) { + return command.getName().equals("openinv"); + } + + @Override + protected @Nullable String getTargetIdentifer( + @NotNull CommandSender sender, + @Nullable String argument, + boolean accessInv + ) { + // /openinv help + if (accessInv && argument != null && (argument.equalsIgnoreCase("help") || argument.equals("?"))) { + this.showHelp(sender); + return null; } - @Override - protected boolean isAccessInventory(@NotNull Command command) { - return command.getName().equals("openinv"); + // Command is player-only. + if (!(sender instanceof Player player)) { + lang.sendMessage(sender, "messages.error.consoleUnsupported"); + return null; } - @Override - protected @Nullable String getTargetIdentifer( - @NotNull CommandSender sender, - @Nullable String argument, - boolean accessInv - ) { - // /openinv help - if (accessInv && argument != null && (argument.equalsIgnoreCase("help") || argument.equals("?"))) { - this.showHelp(sender); - return null; - } - - // Command is player-only. - if (!(sender instanceof Player player)) { - lang.sendMessage(sender, "messages.error.consoleUnsupported"); - return null; - } - - // Use fallthrough for no name provided. - if (argument == null) { - if (config.doesNoArgsOpenSelf()) { - return player.getUniqueId().toString(); - } - return (accessInv ? this.openInvHistory : this.openEnderHistory) - .computeIfAbsent(player, localPlayer -> localPlayer.getUniqueId().toString()); - } - - if (!config.doesNoArgsOpenSelf()) { - // History management - (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, argument); - } - - return argument; + // Use fallthrough for no name provided. + if (argument == null) { + if (config.doesNoArgsOpenSelf()) { + return player.getUniqueId().toString(); + } + return (accessInv ? this.openInvHistory : this.openEnderHistory) + .computeIfAbsent(player, localPlayer -> localPlayer.getUniqueId().toString()); } - private void showHelp(@NotNull CommandSender sender) { - // Get registered commands - for (String commandName : plugin.getDescription().getCommands().keySet()) { - PluginCommand command = plugin.getCommand(commandName); + if (!config.doesNoArgsOpenSelf()) { + // History management + (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, argument); + } - // Ensure command is successfully registered and sender can use it - if (command == null || !command.testPermissionSilent(sender)) { - continue; - } + return argument; + } - // Send usage - sender.sendMessage(command.getUsage().replace("", commandName)); + private void showHelp(@NotNull CommandSender sender) { + // Get registered commands + for (String commandName : plugin.getDescription().getCommands().keySet()) { + PluginCommand command = plugin.getCommand(commandName); - List aliases = command.getAliases(); - if (!aliases.isEmpty()) { - // Assemble alias list - StringJoiner aliasJoiner = new StringJoiner(", ", " (aliases: ", ")"); - for (String alias : aliases) { - aliasJoiner.add(alias); - } + // Ensure command is successfully registered and sender can use it + if (command == null || !command.testPermissionSilent(sender)) { + continue; + } - // Send all aliases - sender.sendMessage(aliasJoiner.toString()); - } + // Send usage + sender.sendMessage(command.getUsage().replace("", commandName)); + List aliases = command.getAliases(); + if (!aliases.isEmpty()) { + // Assemble alias list + StringJoiner aliasJoiner = new StringJoiner(", ", " (aliases: ", ")"); + for (String alias : aliases) { + aliasJoiner.add(alias); } - } - @Override - protected @Nullable OfflinePlayer getTarget(@NotNull String identifier) { - return playerLoader.match(identifier); - } - - @Override - protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv) { - if (onlineTarget.equals(sender)) { - // Permission for opening own inventory. - if (!(accessInv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(sender)) { - lang.sendMessage(sender, "messages.error.permissionOpenSelf"); - return true; - - } - } else { - // Permission for opening others' inventories. - if (!(accessInv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(sender)) { - lang.sendMessage(sender, "messages.error.permissionOpenOther"); - return true; - } - } + // Send all aliases + sender.sendMessage(aliasJoiner.toString()); + } - return false; + } + } + + @Override + protected @Nullable OfflinePlayer getTarget(@NotNull String identifier) { + return playerLoader.match(identifier); + } + + @Override + protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv) { + if (onlineTarget.equals(sender)) { + // Permission for opening own inventory. + if (!(accessInv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(sender)) { + lang.sendMessage(sender, "messages.error.permissionOpenSelf"); + return true; + + } + } else { + // Permission for opening others' inventories. + if (!(accessInv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(sender)) { + lang.sendMessage(sender, "messages.error.permissionOpenOther"); + return true; + } } - @Override - protected void handle( - @NotNull CommandSender sender, - @NotNull Player target, - boolean accessInv, - @NotNull String @NotNull [] args - ) { - Player player = (Player) sender; - if (!config.doesNoArgsOpenSelf()) { - // Record the target - (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); - } - - // Create the inventory - final ISpecialInventory inv; - try { - inv = accessInv ? manager.getInventory(target) : manager.getEnderChest(target); - } catch (Exception e) { - lang.sendMessage(player, "messages.error.commandException"); - plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); - return; - } + return false; + } + + @Override + protected void handle( + @NotNull CommandSender sender, + @NotNull Player target, + boolean accessInv, + @NotNull String @NotNull [] args + ) { + Player player = (Player) sender; + if (!config.doesNoArgsOpenSelf()) { + // Record the target + (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); + } - // Open the inventory - plugin.openInventory(player, inv); + // Create the inventory + final ISpecialInventory inv; + try { + inv = accessInv ? manager.getInventory(target) : manager.getEnderChest(target); + } catch (Exception e) { + lang.sendMessage(player, "messages.error.commandException"); + plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e); + return; } + // Open the inventory + plugin.openInventory(player, inv); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java index 5244d7f5..f4901893 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java @@ -34,7 +34,8 @@ public PlayerLookupCommand( @NotNull OpenInv plugin, @NotNull LanguageManager lang, @NotNull Config config, - @NotNull PlayerLoader playerLoader) { + @NotNull PlayerLoader playerLoader + ) { this.plugin = plugin; this.lang = lang; this.config = config; @@ -46,7 +47,8 @@ public boolean onCommand( @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, - @NotNull String @NotNull [] args) { + @NotNull String @NotNull [] args + ) { // Inventory or ender chest? boolean accessInv = isAccessInventory(command); @@ -110,7 +112,11 @@ public void run() { * @param accessInv {@code true} if an inventory is being accessed, {@code false} for ender chest * @return an updated target identifier or {@code null} if no target is available */ - protected abstract @Nullable String getTargetIdentifer(@NotNull CommandSender sender, @Nullable String argument, boolean accessInv); + protected abstract @Nullable String getTargetIdentifer( + @NotNull CommandSender sender, + @Nullable String argument, + boolean accessInv + ); /** * Get an {@link OfflinePlayer} by identifier. @@ -189,7 +195,11 @@ public void run() { * @param accessInv {@code true} to use inventory permissions, {@code false} for ender chest * @return {@code true} if the sender does not have the correct execution-specific permission */ - protected abstract boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player onlineTarget, boolean accessInv); + protected abstract boolean deniedCommand( + @NotNull CommandSender sender, + @NotNull Player onlineTarget, + boolean accessInv + ); /** * Check for a lack of generalized permissions for accessing the target. @@ -212,7 +222,8 @@ protected boolean deniedAccess(@NotNull CommandSender sender, @NotNull Player on lang.sendMessage( sender, "messages.error.permissionExempt", - new Replacement("%target%", onlineTarget.getDisplayName())); + new Replacement("%target%", onlineTarget.getDisplayName()) + ); return true; } } @@ -224,7 +235,8 @@ protected boolean deniedAccess(@NotNull CommandSender sender, @NotNull Player on lang.sendMessage( sender, "messages.error.permissionCrossWorld", - new Replacement("%target%", onlineTarget.getDisplayName())); + new Replacement("%target%", onlineTarget.getDisplayName()) + ); return true; } @@ -243,14 +255,16 @@ protected abstract void handle( @NotNull CommandSender sender, @NotNull Player target, boolean accessInv, - @NotNull String @NotNull [] args); + @NotNull String @NotNull [] args + ); @Override public List onTabComplete( @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, - @NotNull String[] args) { + @NotNull String[] args + ) { if (!command.testPermissionSilent(sender) || args.length != 1) { return Collections.emptyList(); } diff --git a/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java index e0a7a56d..bf9dd3eb 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java @@ -40,106 +40,119 @@ */ public class SearchContainerCommand implements TabExecutor { - private final @NotNull Plugin plugin; - private final @NotNull LanguageManager lang; + private final @NotNull Plugin plugin; + private final @NotNull LanguageManager lang; + + public SearchContainerCommand(@NotNull Plugin plugin, @NotNull LanguageManager lang) { + this.plugin = plugin; + this.lang = lang; + } + + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (!(sender instanceof Player senderPlayer)) { + lang.sendMessage(sender, "messages.error.consoleUnsupported"); + return true; + } - public SearchContainerCommand(@NotNull Plugin plugin, @NotNull LanguageManager lang) { - this.plugin = plugin; - this.lang = lang; + if (args.length < 1) { + // Must supply material + return false; } - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!(sender instanceof Player senderPlayer)) { - lang.sendMessage(sender, "messages.error.consoleUnsupported"); - return true; - } + Material material = Material.matchMaterial(args[0]); - if (args.length < 1) { - // Must supply material - return false; - } + if (material == null) { + lang.sendMessage( + sender, + "messages.error.invalidMaterial", + new Replacement("%target%", args[0]) + ); + return false; + } - Material material = Material.matchMaterial(args[0]); + int radius = 5; - if (material == null) { - lang.sendMessage( - sender, - "messages.error.invalidMaterial", - new Replacement("%target%", args[0])); - return false; - } + if (args.length > 1) { + try { + radius = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + // Invalid radius supplied + return false; + } + } - int radius = 5; + // Clamp radius. + int configMax = plugin.getConfig().getInt("settings.command.searchcontainer.max-radius", 10); + radius = Math.max(0, Math.min(radius, configMax)); - if (args.length > 1) { - try { - radius = Integer.parseInt(args[1]); - } catch (NumberFormatException e) { - // Invalid radius supplied - return false; - } - } + World world = senderPlayer.getWorld(); + Chunk centerChunk = senderPlayer.getLocation().getChunk(); + StringBuilder locations = new StringBuilder(); - // Clamp radius. - int configMax = plugin.getConfig().getInt("settings.command.searchcontainer.max-radius", 10); - radius = Math.max(0, Math.min(radius, configMax)); - - World world = senderPlayer.getWorld(); - Chunk centerChunk = senderPlayer.getLocation().getChunk(); - StringBuilder locations = new StringBuilder(); - - for (int dX = -radius; dX <= radius; ++dX) { - for (int dZ = -radius; dZ <= radius; ++dZ) { - if (!world.loadChunk(centerChunk.getX() + dX, centerChunk.getZ() + dZ, false)) { - continue; - } - Chunk chunk = world.getChunkAt(centerChunk.getX() + dX, centerChunk.getZ() + dZ); - for (BlockState tileEntity : chunk.getTileEntities()) { - if (!(tileEntity instanceof InventoryHolder holder)) { - continue; - } - if (!holder.getInventory().contains(material)) { - continue; - } - locations.append(holder.getInventory().getType().name().toLowerCase(Locale.ENGLISH)).append(" (") - .append(tileEntity.getX()).append(',').append(tileEntity.getY()).append(',') - .append(tileEntity.getZ()).append("), "); - } - } + for (int dX = -radius; dX <= radius; ++dX) { + for (int dZ = -radius; dZ <= radius; ++dZ) { + if (!world.loadChunk(centerChunk.getX() + dX, centerChunk.getZ() + dZ, false)) { + continue; } - - // Matches found, delete trailing comma and space - if (!locations.isEmpty()) { - locations.delete(locations.length() - 2, locations.length()); - } else { - lang.sendMessage( - sender, - "messages.info.container.noMatches", - new Replacement("%target%", material.name())); - return true; + Chunk chunk = world.getChunkAt(centerChunk.getX() + dX, centerChunk.getZ() + dZ); + for (BlockState tileEntity : chunk.getTileEntities()) { + if (!(tileEntity instanceof InventoryHolder holder)) { + continue; + } + if (!holder.getInventory().contains(material)) { + continue; + } + locations.append(holder.getInventory().getType().name().toLowerCase(Locale.ENGLISH)).append(" (") + .append(tileEntity.getX()).append(',').append(tileEntity.getY()).append(',') + .append(tileEntity.getZ()).append("), "); } + } + } - lang.sendMessage( - sender, - "messages.info.container.matches", - new Replacement("%target%", material.name()), - new Replacement("%detail%", locations.toString())); - return true; + // Matches found, delete trailing comma and space + if (!locations.isEmpty()) { + locations.delete(locations.length() - 2, locations.length()); + } else { + lang.sendMessage( + sender, + "messages.info.container.noMatches", + new Replacement("%target%", material.name()) + ); + return true; } - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { - if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) { - return Collections.emptyList(); - } + lang.sendMessage( + sender, + "messages.info.container.matches", + new Replacement("%target%", material.name()), + new Replacement("%detail%", locations.toString()) + ); + return true; + } + + @Override + public List onTabComplete( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + String[] args + ) { + if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) { + return Collections.emptyList(); + } - String argument = args[args.length - 1]; - if (args.length == 1) { - return TabCompleter.completeEnum(argument, Material.class); - } else { - return TabCompleter.completeInteger(argument); - } + String argument = args[args.length - 1]; + if (args.length == 1) { + return TabCompleter.completeEnum(argument, Material.class); + } else { + return TabCompleter.completeInteger(argument); } + } } diff --git a/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java index 5dcf1b69..6a93f524 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java @@ -46,127 +46,139 @@ */ public class SearchEnchantCommand implements TabExecutor { - private final @NotNull LanguageManager lang; + private final @NotNull LanguageManager lang; + + public SearchEnchantCommand(@NotNull LanguageManager lang) { + this.lang = lang; + } + + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (args.length == 0) { + return false; + } - public SearchEnchantCommand(@NotNull LanguageManager lang) { - this.lang = lang; + Enchantment enchant = null; + int level = 0; + + for (String argument : args) { + try { + level = Integer.parseInt(argument); + continue; + } catch (NumberFormatException ignored) { + // Not a level being specified. + } + + argument = argument.toLowerCase(Locale.ENGLISH); + NamespacedKey key = NamespacedKey.fromString(argument); + if (key == null) { + continue; + } + + Enchantment localEnchant = Registry.ENCHANTMENT.get(key); + if (localEnchant != null) { + enchant = localEnchant; + } } - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (args.length == 0) { - return false; - } + // Arguments not set correctly + if (level == 0 && enchant == null) { + return false; + } - Enchantment enchant = null; - int level = 0; - - for (String argument : args) { - try { - level = Integer.parseInt(argument); - continue; - } catch (NumberFormatException ignored) { - // Not a level being specified. - } - - argument = argument.toLowerCase(Locale.ENGLISH); - NamespacedKey key = NamespacedKey.fromString(argument); - if (key == null) { - continue; - } - - Enchantment localEnchant = Registry.ENCHANTMENT.get(key); - if (localEnchant != null) { - enchant = localEnchant; - } + StringBuilder players = new StringBuilder(); + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + boolean flagInventory = containsEnchantment(player.getInventory(), enchant, level); + boolean flagEnder = containsEnchantment(player.getEnderChest(), enchant, level); + + // No matches, continue + if (!flagInventory && !flagEnder) { + continue; + } + + // Matches, append details + players.append(player.getName()).append(" ("); + if (flagInventory) { + players.append("inv"); + } + if (flagEnder) { + if (flagInventory) { + players.append(','); } + players.append("ender"); + } + players.append("), "); + } - // Arguments not set correctly - if (level == 0 && enchant == null) { - return false; - } + if (!players.isEmpty()) { + // Matches found, delete trailing comma and space + players.delete(players.length() - 2, players.length()); + } else { + lang.sendMessage( + sender, + "messages.info.player.noMatches", + new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level) + ); + return true; + } - StringBuilder players = new StringBuilder(); - for (Player player : Bukkit.getServer().getOnlinePlayers()) { - boolean flagInventory = containsEnchantment(player.getInventory(), enchant, level); - boolean flagEnder = containsEnchantment(player.getEnderChest(), enchant, level); - - // No matches, continue - if (!flagInventory && !flagEnder) { - continue; - } - - // Matches, append details - players.append(player.getName()).append(" ("); - if (flagInventory) { - players.append("inv"); - } - if (flagEnder) { - if (flagInventory) { - players.append(','); - } - players.append("ender"); - } - players.append("), "); + lang.sendMessage( + sender, + "messages.info.player.matches", + new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level), + new Replacement("%detail%", players.toString()) + ); + return true; + } + + private boolean containsEnchantment(Inventory inventory, @Nullable Enchantment enchant, int minLevel) { + for (ItemStack item : inventory.getContents()) { + if (item == null || item.getType() == Material.AIR) { + continue; + } + if (enchant != null) { + if (item.containsEnchantment(enchant) && item.getEnchantmentLevel(enchant) >= minLevel) { + return true; } - - if (!players.isEmpty()) { - // Matches found, delete trailing comma and space - players.delete(players.length() - 2, players.length()); - } else { - lang.sendMessage( - sender, - "messages.info.player.noMatches", - new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level)); + } else { + if (!item.hasItemMeta()) { + continue; + } + ItemMeta meta = item.getItemMeta(); + if (meta == null || !meta.hasEnchants()) { + continue; + } + for (int enchLevel : meta.getEnchants().values()) { + if (enchLevel >= minLevel) { return true; + } } - - lang.sendMessage( - sender, - "messages.info.player.matches", - new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level), - new Replacement("%detail%", players.toString())); - return true; + } } - - private boolean containsEnchantment(Inventory inventory, @Nullable Enchantment enchant, int minLevel) { - for (ItemStack item : inventory.getContents()) { - if (item == null || item.getType() == Material.AIR) { - continue; - } - if (enchant != null) { - if (item.containsEnchantment(enchant) && item.getEnchantmentLevel(enchant) >= minLevel) { - return true; - } - } else { - if (!item.hasItemMeta()) { - continue; - } - ItemMeta meta = item.getItemMeta(); - if (meta == null || !meta.hasEnchants()) { - continue; - } - for (int enchLevel : meta.getEnchants().values()) { - if (enchLevel >= minLevel) { - return true; - } - } - } - } - return false; + return false; + } + + @Override + public List onTabComplete( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (!command.testPermissionSilent(sender) || args.length < 1 || args.length > 2) { + return Collections.emptyList(); } - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!command.testPermissionSilent(sender) || args.length < 1 || args.length > 2) { - return Collections.emptyList(); - } - - if (args.length == 1) { - return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Registry.ENCHANTMENT.stream().toArray(Enchantment[]::new)); - } else { - return TabCompleter.completeInteger(args[1]); - } + if (args.length == 1) { + return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Registry.ENCHANTMENT.stream().toArray(Enchantment[]::new)); + } else { + return TabCompleter.completeInteger(args[1]); } + } } diff --git a/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java index f45034bc..da5660af 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java @@ -34,90 +34,102 @@ public class SearchInvCommand implements TabExecutor { - private final @NotNull LanguageManager lang; + private final @NotNull LanguageManager lang; - public SearchInvCommand(@NotNull LanguageManager lang) { - this.lang = lang; - } + public SearchInvCommand(@NotNull LanguageManager lang) { + this.lang = lang; + } - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { - Material material = null; + Material material = null; - if (args.length >= 1) { - material = Material.matchMaterial(args[0]); - } + if (args.length >= 1) { + material = Material.matchMaterial(args[0]); + } - if (material == null) { - lang.sendMessage( - sender, - "messages.error.invalidMaterial", - new Replacement("%target%", args.length > 0 ? args[0] : "null")); - return false; - } + if (material == null) { + lang.sendMessage( + sender, + "messages.error.invalidMaterial", + new Replacement("%target%", args.length > 0 ? args[0] : "null") + ); + return false; + } - int count = 1; - - if (args.length >= 2) { - try { - count = Integer.parseInt(args[1]); - } catch (NumberFormatException ex) { - lang.sendMessage( - sender, - "messages.error.invalidNumber", - new Replacement("%target%", args[1])); - return false; - } - } + int count = 1; - StringBuilder players = new StringBuilder(); - boolean searchInv = command.getName().equals("searchinv"); - for (Player player : Bukkit.getServer().getOnlinePlayers()) { - Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest(); - int total = 0; - for (ItemStack itemStack : inventory.getContents()) { - if (itemStack != null && itemStack.getType() == material) { - total += itemStack.getAmount(); - if (total >= count) { - players.append(player.getName()).append(", "); - break; - } - } - } - } + if (args.length >= 2) { + try { + count = Integer.parseInt(args[1]); + } catch (NumberFormatException ex) { + lang.sendMessage( + sender, + "messages.error.invalidNumber", + new Replacement("%target%", args[1]) + ); + return false; + } + } - // Matches found, delete trailing comma and space - if (!players.isEmpty()) { - players.delete(players.length() - 2, players.length()); - } else { - lang.sendMessage( - sender, - "messages.info.player.noMatches", - new Replacement("%target%", material.name())); - return true; + StringBuilder players = new StringBuilder(); + boolean searchInv = command.getName().equals("searchinv"); + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest(); + int total = 0; + for (ItemStack itemStack : inventory.getContents()) { + if (itemStack != null && itemStack.getType() == material) { + total += itemStack.getAmount(); + if (total >= count) { + players.append(player.getName()).append(", "); + break; + } } + } + } - lang.sendMessage( - sender, - "messages.info.player.matches", - new Replacement("%target%", material.name()), - new Replacement("%detail%", players.toString())); - return true; + // Matches found, delete trailing comma and space + if (!players.isEmpty()) { + players.delete(players.length() - 2, players.length()); + } else { + lang.sendMessage( + sender, + "messages.info.player.noMatches", + new Replacement("%target%", material.name()) + ); + return true; } - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) { - return Collections.emptyList(); - } + lang.sendMessage( + sender, + "messages.info.player.matches", + new Replacement("%target%", material.name()), + new Replacement("%detail%", players.toString()) + ); + return true; + } + + @Override + public List onTabComplete( + @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, + @NotNull String[] args + ) { + if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) { + return Collections.emptyList(); + } - String argument = args[args.length - 1]; - if (args.length == 1) { - return TabCompleter.completeEnum(argument, Material.class); - } else { - return TabCompleter.completeInteger(argument); - } + String argument = args[args.length - 1]; + if (args.length == 1) { + return TabCompleter.completeEnum(argument, Material.class); + } else { + return TabCompleter.completeInteger(argument); } + } } diff --git a/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java b/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java index d93e7f54..ae3dcc1e 100644 --- a/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listener/ContainerListener.java @@ -20,8 +20,8 @@ import com.lishid.openinv.internal.ViewOnly; import com.lishid.openinv.util.InternalAccessor; import com.lishid.openinv.util.Permissions; -import com.lishid.openinv.util.setting.PlayerToggles; import com.lishid.openinv.util.lang.LanguageManager; +import com.lishid.openinv.util.setting.PlayerToggles; import org.bukkit.GameMode; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 64f1d898..7fe286f2 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -35,236 +35,240 @@ public class InternalAccessor { - private static final boolean PAPER; + private static final boolean PAPER; - static { - boolean paper = false; - try { - Class.forName("io.papermc.paper.configuration.GlobalConfiguration"); - paper = true; - } catch (ClassNotFoundException ignored) { - // Expect remapped server. - } - PAPER = paper; + static { + boolean paper = false; + try { + Class.forName("io.papermc.paper.configuration.GlobalConfiguration"); + paper = true; + } catch (ClassNotFoundException ignored) { + // Expect remapped server. } + PAPER = paper; + } - private @Nullable Accessor internal; + private @Nullable Accessor internal; - public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - try { - internal = getAccessor(logger, lang); + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + try { + internal = getAccessor(logger, lang); - if (internal != null) { - InventoryAccess.setProvider(internal::get); - } - } catch (NoClassDefFoundError | Exception e) { - internal = null; - InventoryAccess.setProvider(null); - } + if (internal != null) { + InventoryAccess.setProvider(internal::get); + } + } catch (NoClassDefFoundError | Exception e) { + internal = null; + InventoryAccess.setProvider(null); } + } - private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - Version maxSupported = Version.of(1, 21, 5); - Version minSupported = Version.of(1, 21, 1); + private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + Version maxSupported = Version.of(1, 21, 5); + Version minSupported = Version.of(1, 21, 1); - // Ensure version is in supported range. - if (BukkitVersions.MINECRAFT.greaterThan(maxSupported) || BukkitVersions.MINECRAFT.lessThan(minSupported)) { - return null; - } - - // Load Spigot accessor. - if (!PAPER) { - if (BukkitVersions.MINECRAFT.equals(maxSupported)) { - // Current Spigot, remapped internals are available. - return new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); - } else { - // Older Spigot; unsupported. - return null; - } - } - - // Paper or a Paper fork, can use Mojang-mapped internals. - if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.5 - return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) { // 1.21.4 - return new com.lishid.openinv.internal.paper1_21_4.InternalAccessor(logger, lang); - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21, 2))) { - // 1.21.1-1.21.2 placeholder format - return new com.lishid.openinv.internal.paper1_21_1.InternalAccessor(logger, lang); - } - - // 1.21.2, 1.21.3 - return new com.lishid.openinv.internal.paper1_21_3.InternalAccessor(logger, lang); + // Ensure version is in supported range. + if (BukkitVersions.MINECRAFT.greaterThan(maxSupported) || BukkitVersions.MINECRAFT.lessThan(minSupported)) { + return null; } - public String getReleasesLink() { - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck. - return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 8, 8))) { // 1.8.8 - return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 13))) { // 1.4.4+ had versioned packages. - return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 13))) { // 1.13 - return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 14))) { // 1.13.1, 1.13.2 - return "https://github.com/lishid/OpenInv/releases/tag/4.0.7"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14))) { // 1.14 to 1.14.1 had no revision bump. - return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14, 1))) { // 1.14.1 to 1.14.2 had no revision bump. - return "https://github.com/lishid/OpenInv/releases/tag/4.0.1"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 15))) { // 1.14.2 - return "https://github.com/lishid/OpenInv/releases/tag/4.1.1"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 15, 1))) { // 1.15, 1.15.1 - return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 16))) { // 1.15.2 - return "https://github.com/Jikoo/OpenInv/commit/502f661be39ee85d300851dd571f3da226f12345 (never released)"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 1))) { // 1.16, 1.16.1 - return "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 3))) { // 1.16.2, 1.16.3 - return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 17))) { // 1.16.4, 1.16.5 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 18, 1))) { // 1.17, 1.18, 1.18.1 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 19))) { // 1.18.2 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19))) { // 1.19 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.0"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19, 1))) { // 1.19.1 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 19, 3))) { // 1.19.2, 1.19.3 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; - } - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20))) { // 1.19.4 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 1))) { // 1.20, 1.20.1 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3 - return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; - } - if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 - return "Unsupported; upgrade to 1.20.6: https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; - } - if (PAPER && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 1))) { // Paper 1.21.1-1.21.3 - return "https://github.com/Jikoo/OpenInv/releases"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21 - return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) { - return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3"; - } - if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) { - return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.6"; - } - return "https://github.com/Jikoo/OpenInv/releases"; + // Load Spigot accessor. + if (!PAPER) { + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { + // Current Spigot, remapped internals are available. + return new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); + } else { + // Older Spigot; unsupported. + return null; + } } - /** - * Reload internal features. - */ - public void reload(ConfigurationSection config) { - if (internal != null) { - internal.reload(config); - } + // Paper or a Paper fork, can use Mojang-mapped internals. + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.5 + return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) { // 1.21.4 + return new com.lishid.openinv.internal.paper1_21_4.InternalAccessor(logger, lang); + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21, 2))) { + // 1.21.1-1.21.2 placeholder format + return new com.lishid.openinv.internal.paper1_21_1.InternalAccessor(logger, lang); } - /** - * Gets the server implementation version. - * - * @return the version - */ - public @NotNull String getVersion() { - return BukkitVersions.MINECRAFT.toString(); + // 1.21.2, 1.21.3 + return new com.lishid.openinv.internal.paper1_21_3.InternalAccessor(logger, lang); + } + + public String getReleasesLink() { + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck. + return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 8, 8))) { // 1.8.8 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 13))) { // 1.4.4+ had versioned packages. + return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 13))) { // 1.13 + return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 14))) { // 1.13.1, 1.13.2 + return "https://github.com/lishid/OpenInv/releases/tag/4.0.7"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14))) { // 1.14 to 1.14.1 had no revision bump. + return "https://github.com/lishid/OpenInv/releases/tag/4.0.0"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14, 1))) { // 1.14.1 to 1.14.2 had no revision bump. + return "https://github.com/lishid/OpenInv/releases/tag/4.0.1"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 15))) { // 1.14.2 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.1"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 15, 1))) { // 1.15, 1.15.1 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 16))) { // 1.15.2 + return "https://github.com/Jikoo/OpenInv/commit/502f661be39ee85d300851dd571f3da226f12345 (never released)"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 1))) { // 1.16, 1.16.1 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.4"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 3))) { // 1.16.2, 1.16.3 + return "https://github.com/lishid/OpenInv/releases/tag/4.1.5"; } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 17))) { // 1.16.4, 1.16.5 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 18, 1))) { // 1.17, 1.18, 1.18.1 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 19))) { // 1.18.2 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19))) { // 1.19 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.0"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19, 1))) { // 1.19.1 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 19, 3))) { // 1.19.2, 1.19.3 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0"; + } + if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20))) { // 1.19.4 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 1))) { // 1.20, 1.20.1 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3 + return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3"; + } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 + return "Unsupported; upgrade to 1.20.6: https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; + } + if (PAPER && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 1))) { // Paper 1.21.1-1.21.3 + return "https://github.com/Jikoo/OpenInv/releases"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21 + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.6"; + } + return "https://github.com/Jikoo/OpenInv/releases"; + } - /** - * Checks if the server implementation is supported. - * - * @return true if initialized for a supported server version - */ - public boolean isSupported() { - return internal != null; + /** + * Reload internal features. + */ + public void reload(ConfigurationSection config) { + if (internal != null) { + internal.reload(config); } + } + + /** + * Gets the server implementation version. + * + * @return the version + */ + public @NotNull String getVersion() { + return BukkitVersions.MINECRAFT.toString(); + } + + /** + * Checks if the server implementation is supported. + * + * @return true if initialized for a supported server version + */ + public boolean isSupported() { + return internal != null; + } - /** - * Get the instance of the IAnySilentContainer implementation for the current server version. - * - * @return the IAnySilentContainer - * @throws IllegalStateException if server version is unsupported - */ - public @NotNull IAnySilentContainer getAnySilentContainer() { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.getAnySilentContainer(); + /** + * Get the instance of the IAnySilentContainer implementation for the current server version. + * + * @return the IAnySilentContainer + * @throws IllegalStateException if server version is unsupported + */ + public @NotNull IAnySilentContainer getAnySilentContainer() { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } + return internal.getAnySilentContainer(); + } - public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.getPlayerManager().openInventory(player, inventory, viewOnly); + public @Nullable InventoryView openInventory( + @NotNull Player player, + @NotNull ISpecialInventory inventory, + boolean viewOnly + ) { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } + return internal.getPlayerManager().openInventory(player, inventory, viewOnly); + } - /** - * Get the instance of the IPlayerDataManager implementation for the current server version. - * - * @return the IPlayerDataManager - * @throws IllegalStateException if server version is unsupported - */ - @NotNull PlayerManager getPlayerDataManager() { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.getPlayerManager(); + /** + * Get the instance of the IPlayerDataManager implementation for the current server version. + * + * @return the IPlayerDataManager + * @throws IllegalStateException if server version is unsupported + */ + @NotNull PlayerManager getPlayerDataManager() { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } + return internal.getPlayerManager(); + } - /** - * Creates an instance of the ISpecialEnderChest implementation for the given Player. - * - * @param player the Player - * @return the ISpecialEnderChest created - */ - ISpecialEnderChest createEnderChest(final Player player) { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.createEnderChest(player); + /** + * Creates an instance of the ISpecialEnderChest implementation for the given Player. + * + * @param player the Player + * @return the ISpecialEnderChest created + */ + ISpecialEnderChest createEnderChest(final Player player) { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } + return internal.createEnderChest(player); + } - /** - * Creates an instance of the ISpecialPlayerInventory implementation for the given Player. - * - * @param player the Player - * @return the ISpecialPlayerInventory created - */ - ISpecialPlayerInventory createInventory(final Player player) { - if (internal == null) { - throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); - } - return internal.createPlayerInventory(player); + /** + * Creates an instance of the ISpecialPlayerInventory implementation for the given Player. + * + * @param player the Player + * @return the ISpecialPlayerInventory created + */ + ISpecialPlayerInventory createInventory(final Player player) { + if (internal == null) { + throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT)); } + return internal.createPlayerInventory(player); + } } diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index 2214af81..f5282f2e 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -115,19 +115,25 @@ public void save(@NotNull UUID uuid) { @Keep @EventHandler(priority = EventPriority.LOWEST) private void onPlayerJoin(@NotNull PlayerJoinEvent event) { - consumeLoaded(event.getPlayer().getUniqueId(), inventory -> { - inventory.setPlayerOnline(event.getPlayer()); - checkViewerAccess(inventory, true); - }); + consumeLoaded( + event.getPlayer().getUniqueId(), + inventory -> { + inventory.setPlayerOnline(event.getPlayer()); + checkViewerAccess(inventory, true); + } + ); } @Keep @EventHandler(priority = EventPriority.MONITOR) private void onPlayerQuit(@NotNull PlayerQuitEvent event) { - consumeLoaded(event.getPlayer().getUniqueId(), inventory -> { - inventory.setPlayerOffline(); - checkViewerAccess(inventory, false); - }); + consumeLoaded( + event.getPlayer().getUniqueId(), + inventory -> { + inventory.setPlayerOffline(); + checkViewerAccess(inventory, false); + } + ); } @Keep @@ -199,7 +205,8 @@ private void onInventoryOpen(@NotNull InventoryOpenEvent event) { plugin.getLogger().log( Level.WARNING, "Prevented a plugin from opening an untracked ISpecialInventory!", - new Throwable("Untracked ISpecialInventory")); + new Throwable("Untracked ISpecialInventory") + ); } } @@ -240,7 +247,8 @@ private boolean consumeLoaded( @NotNull Map map, @NotNull UUID key, boolean saved, - @NotNull Consumer<@NotNull ISpecialInventory> consumer) { + @NotNull Consumer<@NotNull ISpecialInventory> consumer + ) { T inventory = map.get(key); if (inventory == null) { diff --git a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java index 66dd9d33..0930c589 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java +++ b/plugin/src/main/java/com/lishid/openinv/util/PlayerLoader.java @@ -11,7 +11,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.plugin.Plugin; import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,7 +20,6 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,7 +40,8 @@ public PlayerLoader( @NotNull Config config, @NotNull InventoryManager inventoryManager, @NotNull InternalAccessor internalAccessor, - @NotNull Logger logger) { + @NotNull Logger logger + ) { this.plugin = plugin; this.config = config; this.inventoryManager = inventoryManager; @@ -207,28 +206,31 @@ private void updateMatches(@NotNull PlayerJoinEvent event) { return; } - plugin.getScheduler().runTaskLaterAsynchronously(() -> { - Iterator> iterator = lookupCache.asMap().entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - String oldMatch = entry.getValue().getName(); - - // Shouldn't be possible - all profiles should be complete. - if (oldMatch == null) { - iterator.remove(); - continue; - } - - String lookup = entry.getKey(); - float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch); - float newMatchScore = StringMetric.compareJaroWinkler(lookup, name); - - // If new match exceeds old match, delete old match. - if (newMatchScore > oldMatchScore) { - iterator.remove(); - } - } - }, 7L); + plugin.getScheduler().runTaskLaterAsynchronously( + () -> { + Iterator> iterator = lookupCache.asMap().entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String oldMatch = entry.getValue().getName(); + + // Shouldn't be possible - all profiles should be complete. + if (oldMatch == null) { + iterator.remove(); + continue; + } + + String lookup = entry.getKey(); + float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch); + float newMatchScore = StringMetric.compareJaroWinkler(lookup, name); + + // If new match exceeds old match, delete old match. + if (newMatchScore > oldMatchScore) { + iterator.remove(); + } + } + }, + 7L + ); } } diff --git a/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java b/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java index b1afaed2..8feaca81 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java +++ b/plugin/src/main/java/com/lishid/openinv/util/StringMetric.java @@ -23,133 +23,142 @@ public class StringMetric { - public static float compareJaroWinkler(String a, String b) { - final float jaroScore = compareJaro(a, b); + public static float compareJaroWinkler(String a, String b) { + final float jaroScore = compareJaro(a, b); - if (jaroScore < (float) 0.7) { - return jaroScore; - } + if (jaroScore < (float) 0.7) { + return jaroScore; + } + + String prefix = commonPrefix(a, b); + int prefixLength = Math.min(prefix.codePointCount(0, prefix.length()), 4); - String prefix = commonPrefix(a, b); - int prefixLength = Math.min(prefix.codePointCount(0, prefix.length()), 4); + return jaroScore + (prefixLength * (float) 0.1 * (1.0f - jaroScore)); - return jaroScore + (prefixLength * (float) 0.1 * (1.0f - jaroScore)); + } + private static float compareJaro(String a, String b) { + if (a.isEmpty() && b.isEmpty()) { + return 1.0f; } - private static float compareJaro(String a, String b) { - if (a.isEmpty() && b.isEmpty()) { - return 1.0f; - } - - if (a.isEmpty() || b.isEmpty()) { - return 0.0f; - } - - final int[] charsA = a.codePoints().toArray(); - final int[] charsB = b.codePoints().toArray(); - - // Intentional integer division to round down. - final int halfLength = Math.max(0, Math.max(charsA.length, charsB.length) / 2 - 1); - - final int[] commonA = getCommonCodePoints(charsA, charsB, halfLength); - final int[] commonB = getCommonCodePoints(charsB, charsA, halfLength); - - // commonA and commonB will always contain the same multi-set of - // characters. Because getCommonCharacters has been optimized, commonA - // and commonB are -1-padded. So in this loop we count transposition - // and use commonCharacters to determine the length of the multi-set. - float transpositions = 0; - int commonCharacters = 0; - for (int length = commonA.length; commonCharacters < length - && commonA[commonCharacters] > -1; commonCharacters++) { - if (commonA[commonCharacters] != commonB[commonCharacters]) { - transpositions++; - } - } - - if (commonCharacters == 0) { - return 0.0f; - } - - float aCommonRatio = commonCharacters / (float) charsA.length; - float bCommonRatio = commonCharacters / (float) charsB.length; - float transpositionRatio = (commonCharacters - transpositions / 2.0f) / commonCharacters; - - return (aCommonRatio + bCommonRatio + transpositionRatio) / 3.0f; + if (a.isEmpty() || b.isEmpty()) { + return 0.0f; } - /* - * Returns an array of code points from a within b. A character in b is - * counted as common when it is within separation distance from the position - * in a. - */ - private static int[] getCommonCodePoints(final int[] charsA, final int[] charsB, final int separation) { - final int[] common = new int[Math.min(charsA.length, charsB.length)]; - final boolean[] matched = new boolean[charsB.length]; - - // Iterate of string a and find all characters that occur in b within - // the separation distance. Mark any matches found to avoid - // duplicate matchings. - int commonIndex = 0; - for (int i = 0, length = charsA.length; i < length; i++) { - final int character = charsA[i]; - final int index = indexOf(character, charsB, i - separation, i - + separation + 1, matched); - if (index > -1) { - common[commonIndex++] = character; - matched[index] = true; - } - } - - if (commonIndex < common.length) { - common[commonIndex] = -1; - } - - // Both invocations will yield the same multi-set terminated by -1, so - // they can be compared for transposition without making a copy. - return common; + final int[] charsA = a.codePoints().toArray(); + final int[] charsB = b.codePoints().toArray(); + + // Intentional integer division to round down. + final int halfLength = Math.max(0, Math.max(charsA.length, charsB.length) / 2 - 1); + + final int[] commonA = getCommonCodePoints(charsA, charsB, halfLength); + final int[] commonB = getCommonCodePoints(charsB, charsA, halfLength); + + // commonA and commonB will always contain the same multi-set of + // characters. Because getCommonCharacters has been optimized, commonA + // and commonB are -1-padded. So in this loop we count transposition + // and use commonCharacters to determine the length of the multi-set. + float transpositions = 0; + int commonCharacters = 0; + for ( + int length = commonA.length; + commonCharacters < length && commonA[commonCharacters] > -1; + commonCharacters++ + ) { + if (commonA[commonCharacters] != commonB[commonCharacters]) { + transpositions++; + } } - /* - * Search for code point in buffer starting at fromIndex to toIndex - 1. - * - * Returns -1 when not found. - */ - private static int indexOf(int character, int[] buffer, int fromIndex, int toIndex, boolean[] matched) { - - // compare char with range of characters to either side - for (int j = Math.max(0, fromIndex), length = Math.min(toIndex, buffer.length); j < length; j++) { - // check if found - if (buffer[j] == character && !matched[j]) { - return j; - } - } - - return -1; + if (commonCharacters == 0) { + return 0.0f; } - private static String commonPrefix(CharSequence a, CharSequence b) { - int maxPrefixLength = Math.min(a.length(), b.length()); + float aCommonRatio = commonCharacters / (float) charsA.length; + float bCommonRatio = commonCharacters / (float) charsB.length; + float transpositionRatio = (commonCharacters - transpositions / 2.0f) / commonCharacters; + + return (aCommonRatio + bCommonRatio + transpositionRatio) / 3.0f; + } + + /* + * Returns an array of code points from a within b. A character in b is + * counted as common when it is within separation distance from the position + * in a. + */ + private static int[] getCommonCodePoints(final int[] charsA, final int[] charsB, final int separation) { + final int[] common = new int[Math.min(charsA.length, charsB.length)]; + final boolean[] matched = new boolean[charsB.length]; + + // Iterate of string a and find all characters that occur in b within + // the separation distance. Mark any matches found to avoid + // duplicate matchings. + int commonIndex = 0; + for (int i = 0, length = charsA.length; i < length; i++) { + final int character = charsA[i]; + final int index = indexOf( + character, + charsB, + i - separation, + i + separation + 1, + matched + ); + if (index > -1) { + common[commonIndex++] = character; + matched[index] = true; + } + } - int p; + if (commonIndex < common.length) { + common[commonIndex] = -1; + } - p = 0; - while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) { - ++p; - } + // Both invocations will yield the same multi-set terminated by -1, so + // they can be compared for transposition without making a copy. + return common; + } + + /* + * Search for code point in buffer starting at fromIndex to toIndex - 1. + * + * Returns -1 when not found. + */ + private static int indexOf(int character, int[] buffer, int fromIndex, int toIndex, boolean[] matched) { + + // compare char with range of characters to either side + for (int j = Math.max(0, fromIndex), length = Math.min(toIndex, buffer.length); j < length; j++) { + // check if found + if (buffer[j] == character && !matched[j]) { + return j; + } + } - if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) { - --p; - } + return -1; + } - return a.subSequence(0, p).toString(); + private static String commonPrefix(CharSequence a, CharSequence b) { + int maxPrefixLength = Math.min(a.length(), b.length()); + + int p; + + p = 0; + while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) { + ++p; } - private static boolean validSurrogatePairAt(CharSequence string, int index) { - return index >= 0 && index <= string.length() - 2 && Character.isHighSurrogate(string.charAt(index)) && Character.isLowSurrogate(string.charAt(index + 1)); + if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) { + --p; } - private StringMetric(){} + return a.subSequence(0, p).toString(); + } + + private static boolean validSurrogatePairAt(CharSequence string, int index) { + return index >= 0 && index <= string.length() - 2 && Character.isHighSurrogate(string.charAt(index)) && Character.isLowSurrogate(string.charAt(index + 1)); + } + + private StringMetric() { + } } diff --git a/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java b/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java index f61be773..83ed9a5b 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java +++ b/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java @@ -32,119 +32,119 @@ */ public final class TabCompleter { - /** - * Offer tab completions for whole numbers. - * - * @param argument the argument to complete - * @return integer options - */ - public static List completeInteger(String argument) { - // Ensure existing argument is actually a number - if (!argument.isEmpty()) { - try { - Integer.parseInt(argument); - } catch (NumberFormatException e) { - return Collections.emptyList(); - } - } - - List completions = new ArrayList<>(10); - for (int i = 0; i < 10; ++i) { - completions.add(argument + i); - } - - return completions; + /** + * Offer tab completions for whole numbers. + * + * @param argument the argument to complete + * @return integer options + */ + public static List completeInteger(String argument) { + // Ensure existing argument is actually a number + if (!argument.isEmpty()) { + try { + Integer.parseInt(argument); + } catch (NumberFormatException e) { + return Collections.emptyList(); + } } - /** - * Offer tab completions for a given Enum. - * - * @param argument the argument to complete - * @param enumClazz the Enum to complete for - * @return the matching Enum values - */ - public static List completeEnum(String argument, Class> enumClazz) { - argument = argument.toLowerCase(Locale.ENGLISH); - List completions = new ArrayList<>(); - - for (Enum enumConstant : enumClazz.getEnumConstants()) { - String name = enumConstant.name().toLowerCase(Locale.ENGLISH); - if (name.startsWith(argument)) { - completions.add(name); - } - } - - return completions; + List completions = new ArrayList<>(10); + for (int i = 0; i < 10; ++i) { + completions.add(argument + i); } - /** - * Offer tab completions for a given array of Strings. - * - * @param argument the argument to complete - * @param options the Strings which may be completed - * @return the matching Strings - */ - public static List completeString(String argument, String[] options) { - argument = argument.toLowerCase(Locale.ENGLISH); - List completions = new ArrayList<>(); - - for (String option : options) { - if (option.startsWith(argument)) { - completions.add(option); - } - } - - return completions; + return completions; + } + + /** + * Offer tab completions for a given Enum. + * + * @param argument the argument to complete + * @param enumClazz the Enum to complete for + * @return the matching Enum values + */ + public static List completeEnum(String argument, Class> enumClazz) { + argument = argument.toLowerCase(Locale.ENGLISH); + List completions = new ArrayList<>(); + + for (Enum enumConstant : enumClazz.getEnumConstants()) { + String name = enumConstant.name().toLowerCase(Locale.ENGLISH); + if (name.startsWith(argument)) { + completions.add(name); + } } - /** - * Offer tab completions for visible online Players' names. - * - * @param sender the command's sender - * @param argument the argument to complete - * @return the matching Players' names - */ - public static List completeOnlinePlayer(CommandSender sender, String argument) { - List completions = new ArrayList<>(); - Player senderPlayer = sender instanceof Player ? (Player) sender : null; - - for (Player player : Bukkit.getOnlinePlayers()) { - if (senderPlayer != null && !senderPlayer.canSee(player)) { - continue; - } - - if (StringUtil.startsWithIgnoreCase(player.getName(), argument)) { - completions.add(player.getName()); - } - } - - return completions; + return completions; + } + + /** + * Offer tab completions for a given array of Strings. + * + * @param argument the argument to complete + * @param options the Strings which may be completed + * @return the matching Strings + */ + public static List completeString(String argument, String[] options) { + argument = argument.toLowerCase(Locale.ENGLISH); + List completions = new ArrayList<>(); + + for (String option : options) { + if (option.startsWith(argument)) { + completions.add(option); + } } - /** - * Offer tab completions for a given array of Objects. - * - * @param argument the argument to complete - * @param converter the Function for converting the Object into a comparable String - * @param options the Objects which may be completed - * @return the matching Strings - */ - public static List completeObject(String argument, Function converter, T[] options) { - argument = argument.toLowerCase(Locale.ENGLISH); - List completions = new ArrayList<>(); - - for (T option : options) { - String optionString = converter.apply(option).toLowerCase(Locale.ENGLISH); - if (optionString.startsWith(argument)) { - completions.add(optionString); - } - } - - return completions; + return completions; + } + + /** + * Offer tab completions for visible online Players' names. + * + * @param sender the command's sender + * @param argument the argument to complete + * @return the matching Players' names + */ + public static List completeOnlinePlayer(CommandSender sender, String argument) { + List completions = new ArrayList<>(); + Player senderPlayer = sender instanceof Player ? (Player) sender : null; + + for (Player player : Bukkit.getOnlinePlayers()) { + if (senderPlayer != null && !senderPlayer.canSee(player)) { + continue; + } + + if (StringUtil.startsWithIgnoreCase(player.getName(), argument)) { + completions.add(player.getName()); + } } - private TabCompleter() { - throw new IllegalStateException("Cannot create instance of utility class."); + return completions; + } + + /** + * Offer tab completions for a given array of Objects. + * + * @param argument the argument to complete + * @param converter the Function for converting the Object into a comparable String + * @param options the Objects which may be completed + * @return the matching Strings + */ + public static List completeObject(String argument, Function converter, T[] options) { + argument = argument.toLowerCase(Locale.ENGLISH); + List completions = new ArrayList<>(); + + for (T option : options) { + String optionString = converter.apply(option).toLowerCase(Locale.ENGLISH); + if (optionString.startsWith(argument)) { + completions.add(optionString); + } } + return completions; + } + + private TabCompleter() { + throw new IllegalStateException("Cannot create instance of utility class."); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java index a4b15147..228cfd07 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/config/ConfigUpdater.java @@ -26,104 +26,104 @@ public record ConfigUpdater(@NotNull Plugin plugin) { - public void checkForUpdates() { - final int version = plugin.getConfig().getInt("config-version", 1); - ConfigurationSection defaults = plugin.getConfig().getDefaults(); - if (defaults == null || version >= defaults.getInt("config-version")) { - return; - } - - plugin.getLogger().info("Configuration update found! Performing update..."); - - // Backup the old config file - try { - plugin.getConfig().save(new File(plugin.getDataFolder(), "config_old.yml")); - plugin.getLogger().info("Backed up config.yml to config_old.yml before updating."); - } catch (IOException e) { - plugin.getLogger().warning("Could not back up config.yml before updating!"); - } - - if (version < 2) { - updateConfig1To2(); - } - if (version < 3) { - updateConfig2To3(); - } - if (version < 4) { - updateConfig3To4(); - } - if (version < 5) { - updateConfig4To5(); - } - if (version < 6) { - updateConfig5To6(); - } - if (version < 7) { - updateConfig6To7(); - } - if (version < 8) { - updateConfig7To8(); - } - - plugin.saveConfig(); - plugin.getLogger().info("Configuration update complete!"); + public void checkForUpdates() { + final int version = plugin.getConfig().getInt("config-version", 1); + ConfigurationSection defaults = plugin.getConfig().getDefaults(); + if (defaults == null || version >= defaults.getInt("config-version")) { + return; } - private void updateConfig7To8() { - FileConfiguration config = plugin.getConfig(); - config.set("settings.equal-access", "view"); - config.set("config-version", 8); - } + plugin.getLogger().info("Configuration update found! Performing update..."); - private void updateConfig6To7() { - FileConfiguration config = plugin.getConfig(); - config.set("toggles", null); - String consoleLocale = config.getString("settings.locale", "en"); - if (consoleLocale.isBlank() || consoleLocale.equalsIgnoreCase("en_us")) { - consoleLocale = "en"; - } - config.set("settings.console-locale", consoleLocale); - config.set("settings.locale", null); - config.set("config-version", 7); + // Backup the old config file + try { + plugin.getConfig().save(new File(plugin.getDataFolder(), "config_old.yml")); + plugin.getLogger().info("Backed up config.yml to config_old.yml before updating."); + } catch (IOException e) { + plugin.getLogger().warning("Could not back up config.yml before updating!"); } - private void updateConfig5To6() { - FileConfiguration config = plugin.getConfig(); - config.set("settings.command.open.no-args-opens-self", false); - config.set("settings.command.searchcontainer.max-radius", 10); - config.set("config-version", 6); + if (version < 2) { + updateConfig1To2(); } - - private void updateConfig4To5() { - FileConfiguration config = plugin.getConfig(); - config.set("settings.disable-offline-access", false); - config.set("config-version", 5); + if (version < 3) { + updateConfig2To3(); } - - private void updateConfig3To4() { - FileConfiguration config = plugin.getConfig(); - config.set("notify", null); - config.set("config-version", 4); + if (version < 4) { + updateConfig3To4(); } - - private void updateConfig2To3() { - FileConfiguration config = plugin.getConfig(); - config.set("items", null); - config.set("ItemOpenInv", null); - config.set("toggles", null); - config.set("settings.disable-saving", config.getBoolean("DisableSaving", false)); - config.set("DisableSaving", null); - config.set("config-version", 3); + if (version < 5) { + updateConfig4To5(); + } + if (version < 6) { + updateConfig5To6(); + } + if (version < 7) { + updateConfig6To7(); + } + if (version < 8) { + updateConfig7To8(); } - private void updateConfig1To2() { - FileConfiguration config = plugin.getConfig(); - config.set("ItemOpenInvItemID", null); - config.set("NotifySilentChest", null); - config.set("NotifyAnyChest", null); - config.set("AnyChest", null); - config.set("SilentChest", null); - config.set("config-version", 2); + plugin.saveConfig(); + plugin.getLogger().info("Configuration update complete!"); + } + + private void updateConfig7To8() { + FileConfiguration config = plugin.getConfig(); + config.set("settings.equal-access", "view"); + config.set("config-version", 8); + } + + private void updateConfig6To7() { + FileConfiguration config = plugin.getConfig(); + config.set("toggles", null); + String consoleLocale = config.getString("settings.locale", "en"); + if (consoleLocale.isBlank() || consoleLocale.equalsIgnoreCase("en_us")) { + consoleLocale = "en"; } + config.set("settings.console-locale", consoleLocale); + config.set("settings.locale", null); + config.set("config-version", 7); + } + + private void updateConfig5To6() { + FileConfiguration config = plugin.getConfig(); + config.set("settings.command.open.no-args-opens-self", false); + config.set("settings.command.searchcontainer.max-radius", 10); + config.set("config-version", 6); + } + + private void updateConfig4To5() { + FileConfiguration config = plugin.getConfig(); + config.set("settings.disable-offline-access", false); + config.set("config-version", 5); + } + + private void updateConfig3To4() { + FileConfiguration config = plugin.getConfig(); + config.set("notify", null); + config.set("config-version", 4); + } + + private void updateConfig2To3() { + FileConfiguration config = plugin.getConfig(); + config.set("items", null); + config.set("ItemOpenInv", null); + config.set("toggles", null); + config.set("settings.disable-saving", config.getBoolean("DisableSaving", false)); + config.set("DisableSaving", null); + config.set("config-version", 3); + } + + private void updateConfig1To2() { + FileConfiguration config = plugin.getConfig(); + config.set("ItemOpenInvItemID", null); + config.set("NotifySilentChest", null); + config.set("NotifyAnyChest", null); + config.set("AnyChest", null); + config.set("SilentChest", null); + config.set("config-version", 2); + } } diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index a50fad34..b0c78a65 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -2,7 +2,7 @@ name: OpenInv main: com.lishid.openinv.OpenInv version: ${version} author: lishid -authors: [Jikoo, ShadowRanger] +authors: [ Jikoo, ShadowRanger ] description: Open a player's inventory as a chest and interact with it in real time. api-version: "1.13" folia-supported: true @@ -83,59 +83,59 @@ permissions: commands: openinv: - aliases: [oi, inv, open] + aliases: [ oi, inv, open ] description: Open a player's inventory permission: openinv.inventory.open.self;openinv.inventory.open.other usage: |- - / [Player] - Open a player's inventory + / [Player] - Open a player's inventory clearinv: description: Clear a player's inventory permission: openinv.clear.self;openinv.clear.other usage: |- - / [Player] - Clear a player's inventory + / [Player] - Clear a player's inventory openender: - aliases: [oe] + aliases: [ oe ] description: Open a player's ender chest permission: openinv.enderchest.open.self;openinv.enderchest.open.other usage: |- - / [Player] - Open a player's ender chest + / [Player] - Open a player's ender chest clearender: description: Clear a player's ender chest permission: openinv.clear.self;openinv.clear.other usage: |- - / [Player] - Clear a player's ender chest + / [Player] - Clear a player's ender chest searchinv: - aliases: [si] + aliases: [ si ] description: Search and list players having a specific item permission: openinv.search.inventory usage: |- - / [MinAmount] - MinAmount is optional, the minimum amount required + / [MinAmount] - MinAmount is optional, the minimum amount required searchender: - aliases: [se] + aliases: [ se ] permission: openinv.search.inventory description: Search and list players having a specific item in their ender chest usage: |- - / [MinAmount] - MinAmount is optional, the minimum amount required + / [MinAmount] - MinAmount is optional, the minimum amount required silentcontainer: - aliases: [sc, silent, silentchest] + aliases: [ sc, silent, silentchest ] description: SilentContainer stops sounds and animations when using containers. permission: openinv.container.silent usage: |- - / [check|on|off] - Check, toggle, or set SilentContainer + / [check|on|off] - Check, toggle, or set SilentContainer anycontainer: - aliases: [ac, anychest] + aliases: [ ac, anychest ] description: AnyContainer allows using blocked containers. permission: openinv.container.any usage: |- - / [check|on|off] - Check, toggle, or set AnyContainer + / [check|on|off] - Check, toggle, or set AnyContainer searchenchant: - aliases: [searchenchants] + aliases: [ searchenchants ] description: Search and list players with a specific enchantment. permission: openinv.search.inventory usage: |- - / <[Enchantment] [MinLevel]> - Enchantment is the enchantment type, MinLevel is the minimum level. One is optional + / <[Enchantment] [MinLevel]> - Enchantment is the enchantment type, MinLevel is the minimum level. One is optional searchcontainer: - aliases: [searchchest] + aliases: [ searchchest ] description: Search and list containers with a specific material. permission: openinv.search.container usage: / [ChunkRadius] - ChunkRadius is optional, the length that will be searched for matching items. Default 5 diff --git a/resource-pack/build.gradle.kts b/resource-pack/build.gradle.kts index a5e0996f..f94f30ea 100644 --- a/resource-pack/build.gradle.kts +++ b/resource-pack/build.gradle.kts @@ -7,7 +7,7 @@ tasks.register("buildResourcePack") { destinationDirectory = rootProject.layout.projectDirectory.dir("dist") from("openinv-legibility-pack") - with (copySpec { + with(copySpec { include("**/*.json", "**/*.png", "pack.mcmeta") }) } From 7a9bb11133123d7308b3d798a3b0d1df03c47241 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 19 May 2025 05:40:43 -0400 Subject: [PATCH 292/340] Re-add errorprone Dropped during swap to Gradle --- build.gradle.kts | 1 + buildSrc/build.gradle.kts | 1 + buildSrc/src/main/kotlin/openinv-base.gradle.kts | 2 ++ gradle/libs.versions.toml | 7 ++++++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3c7f2fc0..2aa1d9f4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { `java-library` alias(libs.plugins.paperweight) apply false alias(libs.plugins.shadow) apply false + id(libs.plugins.errorprone.gradle.get().pluginId) apply false } // Set by Spigot module, used by Paper module to convert to Spigot's version of Mojang mappings. diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 2fbab782..aef4e143 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,4 +10,5 @@ repositories { dependencies { val libs = project.extensions.getByType(VersionCatalogsExtension::class.java).named("libs") implementation(libs.findLibrary("specialsource").orElseThrow()) + implementation(libs.findLibrary("errorprone-gradle").orElseThrow()) } diff --git a/buildSrc/src/main/kotlin/openinv-base.gradle.kts b/buildSrc/src/main/kotlin/openinv-base.gradle.kts index 2369a9e0..be1979a3 100644 --- a/buildSrc/src/main/kotlin/openinv-base.gradle.kts +++ b/buildSrc/src/main/kotlin/openinv-base.gradle.kts @@ -1,5 +1,6 @@ plugins { `java-library` + id("net.ltgt.errorprone") } java { @@ -16,6 +17,7 @@ dependencies { val libs = versionCatalogs.named("libs") compileOnly(libs.findLibrary("annotations").orElseThrow()) compileOnly(libs.findLibrary("spigotapi").orElseThrow()) + errorprone(libs.findLibrary("errorprone-core").orElseThrow()) } tasks { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7e9d5f3e..af636ac9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,8 @@ annotations = "26.0.2" paperweight = "2.0.0-beta.16" shadow = "8.3.6" folia-scheduler-wrapper = "v0.0.3" +errorprone-core = "2.38.0" +errorprone-gradle = "4.2.0" [libraries] spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } @@ -13,7 +15,10 @@ specialsource = { module = "net.md-5:SpecialSource", version.ref = "specialsourc planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" } annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } folia-scheduler-wrapper = { module = "com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper", version.ref = "folia-scheduler-wrapper" } +errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone-core" } +errorprone-gradle = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorprone-gradle" } [plugins] paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" } -shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } \ No newline at end of file +shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } +errorprone-gradle = { id = "net.ltgt.errorprone", version.ref = "errorprone-gradle" } \ No newline at end of file From 9ca5b2d82672c63756543b593e256c64b5e052a3 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Mon, 19 May 2025 06:20:03 -0400 Subject: [PATCH 293/340] Apply errorprone suggestions --- .../openinv/util/lang/LanguageManager.java | 4 +-- .../bukkit/OpenDummyPlayerInventory.java | 1 + .../container/bukkit/OpenPlayerInventory.java | 2 ++ .../common/container/menu/OpenChestMenu.java | 1 + .../placeholder/PlaceholderLoaderBase.java | 35 +++++++------------ .../internal/common/player/OpenPlayer.java | 6 +++- .../internal/common/player/PlayerManager.java | 8 ++--- .../paper1_21_4/container/OpenEnderChest.java | 1 + .../paper1_21_4/container/OpenInventory.java | 1 + .../container/bukkit/OpenPlayerInventory.java | 2 ++ .../container/menu/OpenChestMenu.java | 1 + .../paper1_21_4/player/PlayerManager.java | 2 ++ .../container/bukkit/OpenPlayerInventory.java | 2 ++ .../internal/reobf/player/PlayerManager.java | 8 ++--- .../main/java/com/lishid/openinv/OpenInv.java | 2 +- .../com/lishid/openinv/util/TabCompleter.java | 32 ++++++++++++----- 16 files changed, 65 insertions(+), 43 deletions(-) diff --git a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java index ff1b1bbe..01b22025 100644 --- a/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java +++ b/common/src/main/java/com/lishid/openinv/util/lang/LanguageManager.java @@ -278,8 +278,8 @@ private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull } private @NotNull String getLocale(@NotNull CommandSender sender) { - if (sender instanceof Player) { - return ((Player) sender).getLocale(); + if (sender instanceof Player player) { + return player.getLocale(); } else { return plugin.getConfig().getString("settings.locale", "en"); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java index c6bb8f10..2c482710 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenDummyPlayerInventory.java @@ -111,6 +111,7 @@ public void setItemInOffHand(@Nullable ItemStack item) { } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public @NotNull ItemStack getItemInHand() { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java index 92958618..d57b1a4b 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java @@ -174,12 +174,14 @@ public void setItemInOffHand(@Nullable ItemStack item) { .set(EquipmentSlot.OFFHAND, CraftItemStack.asNMSCopy(item)); } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public @NotNull ItemStack getItemInHand() { return getItemInMainHand(); } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public void setItemInHand(@Nullable ItemStack stack) { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index 4c319825..76a48f60 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -37,6 +37,7 @@ /** * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. */ +@SuppressWarnings("HidingField") // Revisit when removing 1.21.4 support public abstract class OpenChestMenu> extends AbstractContainerMenu { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java index 02d8338c..d6f75bb0 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java @@ -25,7 +25,18 @@ public abstract class PlaceholderLoaderBase { - protected PlaceholderLoaderBase() { + public void load(@Nullable ConfigurationSection section) throws Exception { + Placeholders.craftingOutput = parse(section, "crafting-output", defaultCraftingOutput()); + Placeholders.cursor = parse(section, "cursor", defaultCursor()); + Placeholders.drop = parse(section, "drop", defaultDrop()); + Placeholders.emptyHelmet = parse(section, "empty-helmet", getEmptyArmor(Items.LEATHER_HELMET)); + Placeholders.emptyChestplate = parse(section, "empty-chestplate", getEmptyArmor(Items.LEATHER_CHESTPLATE)); + Placeholders.emptyLeggings = parse(section, "empty-leggings", getEmptyArmor(Items.LEATHER_LEGGINGS)); + Placeholders.emptyBoots = parse(section, "empty-boots", getEmptyArmor(Items.LEATHER_BOOTS)); + Placeholders.emptyOffHand = parse(section, "empty-off-hand", defaultShield()); + Placeholders.notSlot = parse(section, "not-a-slot", defaultNotSlot()); + Placeholders.blockedOffline = parse(section, "blocked.offline", defaultBlockedOffline()); + for (GameType type : GameType.values()) { // Barrier: "Not available - Creative" etc. ItemStack typeItem = new ItemStack(Items.BARRIER); @@ -35,29 +46,7 @@ protected PlaceholderLoaderBase() { ); Placeholders.BLOCKED_GAME_TYPE.put(type, typeItem); } - Placeholders.craftingOutput = defaultCraftingOutput(); - Placeholders.cursor = defaultCursor(); - Placeholders.drop = defaultDrop(); - Placeholders.emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET); - Placeholders.emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE); - Placeholders.emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS); - Placeholders.emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS); - Placeholders.emptyOffHand = defaultShield(); - Placeholders.notSlot = defaultNotSlot(); - Placeholders.blockedOffline = defaultBlockedOffline(); - } - public void load(@Nullable ConfigurationSection section) throws Exception { - Placeholders.craftingOutput = parse(section, "crafting-output", Placeholders.craftingOutput); - Placeholders.cursor = parse(section, "cursor", Placeholders.cursor); - Placeholders.drop = parse(section, "drop", Placeholders.drop); - Placeholders.emptyHelmet = parse(section, "empty-helmet", Placeholders.emptyHelmet); - Placeholders.emptyChestplate = parse(section, "empty-chestplate", Placeholders.emptyChestplate); - Placeholders.emptyLeggings = parse(section, "empty-leggings", Placeholders.emptyLeggings); - Placeholders.emptyBoots = parse(section, "empty-boots", Placeholders.emptyBoots); - Placeholders.emptyOffHand = parse(section, "empty-off-hand", Placeholders.emptyOffHand); - Placeholders.notSlot = parse(section, "not-a-slot", Placeholders.notSlot); - Placeholders.blockedOffline = parse(section, "blocked.offline", Placeholders.blockedOffline); Placeholders.BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", Placeholders.BLOCKED_GAME_TYPE.get(GameType.CREATIVE))); Placeholders.BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", Placeholders.BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 267d9335..61a6a501 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -128,7 +128,11 @@ public void saveData() { oldData = oldData.copy(); // Remove vanilla/server data that is not written every time. - oldData.keySet().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit") || key.startsWith("Paper") && key.length() > 5); + oldData.keySet().removeIf( + key -> RESET_TAGS.contains(key) + || key.startsWith("Bukkit") + || (key.startsWith("Paper") && key.length() > 5) + ); return oldData; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index 9d67d448..ec14f7ef 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -55,15 +55,15 @@ public PlayerManager(@NotNull Logger logger) { } public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); + if (player instanceof CraftPlayer craftPlayer) { + return craftPlayer.getHandle(); } Server server = player.getServer(); ServerPlayer nmsPlayer = null; - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + if (server instanceof CraftServer craftServer) { + nmsPlayer = craftServer.getHandle().getPlayer(player.getUniqueId()); } if (nmsPlayer == null) { diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java index a10b6c58..401b6083 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java @@ -13,6 +13,7 @@ public OpenEnderChest(@NotNull Player player) { super(player); } + @Override public @Nullable AbstractContainerMenu createMenu( net.minecraft.world.entity.player.Player player, int i, diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java index 64ac4255..a935bd22 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java @@ -180,6 +180,7 @@ public ItemStack getOrDefault() { return bukkitEntity; } + @Override public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java index b5989e5c..9cd8fbc0 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/bukkit/OpenPlayerInventory.java @@ -170,12 +170,14 @@ public void setItemInOffHand(@Nullable ItemStack item) { getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item)); } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public @NotNull ItemStack getItemInHand() { return getItemInMainHand(); } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public void setItemInHand(@Nullable ItemStack stack) { diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java index f82a63e6..273ad9b1 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java @@ -35,6 +35,7 @@ /** * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. */ +@SuppressWarnings("HidingField") public abstract class OpenChestMenu> extends AbstractContainerMenu { diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java index 81d0cc8d..1a6c671b 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java @@ -21,6 +21,7 @@ public PlayerManager(@NotNull Logger logger) { super(logger); } + @Override protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { // See PlayerList#placeNewPlayer World bukkitWorld; @@ -58,6 +59,7 @@ protected void spawnInDefaultWorld(ServerPlayer player) { } } + @Override protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java index fd6d60f9..a74b16e2 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java @@ -151,12 +151,14 @@ public void setItemInOffHand(@Nullable ItemStack item) { getInventory().getOwnerHandle().getBukkitEntity().getInventory().setItemInOffHand(item); } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public @NotNull ItemStack getItemInHand() { return getItemInMainHand(); } + @SuppressWarnings("InlineMeSuggester") @Deprecated @Override public void setItemInHand(@Nullable ItemStack stack) { diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 3b964549..3205da34 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -47,15 +47,15 @@ public PlayerManager(@NotNull Logger logger) { } public static @NotNull ServerPlayer getHandle(final Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); + if (player instanceof CraftPlayer craftPlayer) { + return craftPlayer.getHandle(); } Server server = player.getServer(); ServerPlayer nmsPlayer = null; - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId()); + if (server instanceof CraftServer craftServer) { + nmsPlayer = craftServer.getHandle().getPlayer(player.getUniqueId()); } if (nmsPlayer == null) { diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 570452ac..593bff86 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -256,7 +256,7 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean viewOnly = edit != null && !edit.hasPermission(player); - if (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) { + if (ownContainer || (viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY)) { return this.accessor.openInventory(player, inventory, viewOnly); } diff --git a/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java b/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java index 83ed9a5b..f55ae192 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java +++ b/plugin/src/main/java/com/lishid/openinv/util/TabCompleter.java @@ -20,6 +20,9 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import java.util.ArrayList; import java.util.Collections; @@ -38,13 +41,13 @@ public final class TabCompleter { * @param argument the argument to complete * @return integer options */ - public static List completeInteger(String argument) { + public static @NotNull @Unmodifiable List completeInteger(@NotNull String argument) { // Ensure existing argument is actually a number if (!argument.isEmpty()) { try { Integer.parseInt(argument); } catch (NumberFormatException e) { - return Collections.emptyList(); + return List.of(); } } @@ -53,7 +56,7 @@ public static List completeInteger(String argument) { completions.add(argument + i); } - return completions; + return Collections.unmodifiableList(completions); } /** @@ -63,7 +66,10 @@ public static List completeInteger(String argument) { * @param enumClazz the Enum to complete for * @return the matching Enum values */ - public static List completeEnum(String argument, Class> enumClazz) { + public static @NotNull List completeEnum( + @NotNull String argument, + @NotNull Class> enumClazz + ) { argument = argument.toLowerCase(Locale.ENGLISH); List completions = new ArrayList<>(); @@ -84,7 +90,10 @@ public static List completeEnum(String argument, Class * @param options the Strings which may be completed * @return the matching Strings */ - public static List completeString(String argument, String[] options) { + public static @NotNull List completeString( + @NotNull String argument, + @NotNull String @NotNull [] options + ) { argument = argument.toLowerCase(Locale.ENGLISH); List completions = new ArrayList<>(); @@ -104,9 +113,12 @@ public static List completeString(String argument, String[] options) { * @param argument the argument to complete * @return the matching Players' names */ - public static List completeOnlinePlayer(CommandSender sender, String argument) { + public static List completeOnlinePlayer( + @Nullable CommandSender sender, + @NotNull String argument + ) { List completions = new ArrayList<>(); - Player senderPlayer = sender instanceof Player ? (Player) sender : null; + Player senderPlayer = sender instanceof Player player ? player : null; for (Player player : Bukkit.getOnlinePlayers()) { if (senderPlayer != null && !senderPlayer.canSee(player)) { @@ -129,7 +141,11 @@ public static List completeOnlinePlayer(CommandSender sender, String arg * @param options the Objects which may be completed * @return the matching Strings */ - public static List completeObject(String argument, Function converter, T[] options) { + public static List completeObject( + @NotNull String argument, + @NotNull Function<@NotNull T, @NotNull String> converter, + @NotNull T @NotNull[] options + ) { argument = argument.toLowerCase(Locale.ENGLISH); List completions = new ArrayList<>(); From 03c0fea7ab3f70554eb017905d3425f2362b04bf Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 19 May 2025 11:31:49 -0400 Subject: [PATCH 294/340] Search shulker boxes and bundles (#311) --- .../command/SearchContainerCommand.java | 3 +- .../openinv/command/SearchEnchantCommand.java | 50 +++++++------- .../openinv/command/SearchInvCommand.java | 28 +++++--- .../com/lishid/openinv/util/SearchHelper.java | 66 +++++++++++++++++++ 4 files changed, 112 insertions(+), 35 deletions(-) create mode 100644 plugin/src/main/java/com/lishid/openinv/util/SearchHelper.java diff --git a/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java index bf9dd3eb..1b8eb1b2 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchContainerCommand.java @@ -19,6 +19,7 @@ import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import com.lishid.openinv.util.SearchHelper; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; @@ -105,7 +106,7 @@ public boolean onCommand( if (!(tileEntity instanceof InventoryHolder holder)) { continue; } - if (!holder.getInventory().contains(material)) { + if (!SearchHelper.findMatch(holder.getInventory(), itemStack -> itemStack.getType() == material)) { continue; } locations.append(holder.getInventory().getType().name().toLowerCase(Locale.ENGLISH)).append(" (") diff --git a/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java index 6a93f524..1844c4ea 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchEnchantCommand.java @@ -19,8 +19,8 @@ import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import com.lishid.openinv.util.SearchHelper; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.command.Command; @@ -29,7 +29,6 @@ import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -137,30 +136,33 @@ public boolean onCommand( } private boolean containsEnchantment(Inventory inventory, @Nullable Enchantment enchant, int minLevel) { - for (ItemStack item : inventory.getContents()) { - if (item == null || item.getType() == Material.AIR) { - continue; - } - if (enchant != null) { - if (item.containsEnchantment(enchant) && item.getEnchantmentLevel(enchant) >= minLevel) { - return true; - } - } else { - if (!item.hasItemMeta()) { - continue; - } - ItemMeta meta = item.getItemMeta(); - if (meta == null || !meta.hasEnchants()) { - continue; - } - for (int enchLevel : meta.getEnchants().values()) { - if (enchLevel >= minLevel) { - return true; + return SearchHelper.findMatch( + inventory, + itemStack -> { + // Ensure meta is available and has enchantments. + if (!itemStack.hasItemMeta()) { + return false; + } + ItemMeta meta = itemStack.getItemMeta(); + if (meta == null || !meta.hasEnchants()) { + return false; + } + + // If enchantment is provided, use it. + if (enchant != null) { + return meta.getEnchantLevel(enchant) >= minLevel; + } + + // Otherwise, check all enchantment levels. + for (int enchLevel : meta.getEnchants().values()) { + if (enchLevel >= minLevel) { + return true; + } } + + return false; } - } - } - return false; + ); } @Override diff --git a/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java index da5660af..c2a5a32d 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/SearchInvCommand.java @@ -19,6 +19,7 @@ import com.lishid.openinv.util.TabCompleter; import com.lishid.openinv.util.lang.LanguageManager; import com.lishid.openinv.util.lang.Replacement; +import com.lishid.openinv.util.SearchHelper; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.command.Command; @@ -26,11 +27,11 @@ import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; public class SearchInvCommand implements TabExecutor { @@ -82,15 +83,9 @@ public boolean onCommand( boolean searchInv = command.getName().equals("searchinv"); for (Player player : Bukkit.getServer().getOnlinePlayers()) { Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest(); - int total = 0; - for (ItemStack itemStack : inventory.getContents()) { - if (itemStack != null && itemStack.getType() == material) { - total += itemStack.getAmount(); - if (total >= count) { - players.append(player.getName()).append(", "); - break; - } - } + if (findMatch(inventory, material, count)) { + players.append(player.getName()).append(", "); + break; } } @@ -115,6 +110,19 @@ public boolean onCommand( return true; } + private boolean findMatch(@NotNull Inventory inventory, @NotNull Material material, int count) { + AtomicInteger total = new AtomicInteger(); + return SearchHelper.findMatch( + inventory, + itemStack -> { + if (itemStack.getType() == material && itemStack.getAmount() > 0) { + return total.addAndGet(itemStack.getAmount()) >= count; + } + return false; + } + ); + } + @Override public List onTabComplete( @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, diff --git a/plugin/src/main/java/com/lishid/openinv/util/SearchHelper.java b/plugin/src/main/java/com/lishid/openinv/util/SearchHelper.java new file mode 100644 index 00000000..e62ad300 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/SearchHelper.java @@ -0,0 +1,66 @@ +package com.lishid.openinv.util; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.BundleMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Predicate; + +public final class SearchHelper { + + public static boolean findMatch(@NotNull Inventory inventory, @NotNull Predicate<@NotNull ItemStack> predicate) { + for (ItemStack content : inventory.getContents()) { + if (findMatch(content, predicate)) { + return true; + } + } + return false; + } + + private static boolean findMatch(@Nullable ItemStack itemStack, @NotNull Predicate<@NotNull ItemStack> predicate) { + if (itemStack == null || itemStack.getType().isAir()) { + return false; + } + + // If the item is the search target, done. + if (predicate.test(itemStack)) { + return true; + } + + // If the item doesn't have meta, it cannot contain items. + if (!itemStack.hasItemMeta()) { + return false; + } + + ItemMeta meta = itemStack.getItemMeta(); + + // Container meta with items (primarily shulkers). + if (meta instanceof BlockStateMeta stateMeta) { + if (!stateMeta.hasBlockState() || !(stateMeta.getBlockState() instanceof InventoryHolder holder)) { + return false; + } + Inventory inventory = holder.getInventory(); + return findMatch(inventory, predicate); + } + + // Bundle meta. + if (meta instanceof BundleMeta bundleMeta) { + for (ItemStack subStack : bundleMeta.getItems()) { + if (findMatch(subStack, predicate)) { + return true; + } + } + } + + return false; + } + + private SearchHelper() { + throw new IllegalStateException("Cannot create instance of utility class."); + } +} From b2c13b40e2c7d80d54b2de6471de2541fd8f1cc5 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 20 May 2025 09:22:35 -0400 Subject: [PATCH 295/340] Improve feedback, code health (#312) * Drop legacy translations * Drop legacy viewer handling * Improve lookup feedback * Deprecate ISpecialInventory#setPlayerOffline * Fix default value being provided --- .../openinv/event/OpenPlayerSaveEvent.java | 1 - .../lishid/openinv/event/PlayerSaveEvent.java | 1 - .../openinv/event/PlayerToggledEvent.java | 1 - .../openinv/internal/ISpecialInventory.java | 5 +- .../lishid/openinv/util/InventoryAccess.java | 1 - .../openinv/internal/InventoryViewTitle.java | 50 ------------------- .../common/container/OpenEnderChest.java | 4 -- .../common/container/OpenInventory.java | 4 -- .../openinv/command/ClearInvCommand.java | 9 +++- .../openinv/command/OpenInvCommand.java | 1 + .../openinv/command/PlayerLookupCommand.java | 5 +- .../lishid/openinv/util/InventoryManager.java | 17 +------ plugin/src/main/resources/locale/de.yml | 3 -- plugin/src/main/resources/locale/en.yml | 7 ++- plugin/src/main/resources/locale/es.yml | 4 -- plugin/src/main/resources/locale/pt.yml | 3 -- plugin/src/main/resources/locale/zh_cn.yml | 3 -- plugin/src/main/resources/locale/zh_tw.yml | 3 -- 18 files changed, 20 insertions(+), 102 deletions(-) delete mode 100644 common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java diff --git a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java index f38457b5..7f0e16c6 100644 --- a/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/OpenPlayerSaveEvent.java @@ -26,7 +26,6 @@ public class OpenPlayerSaveEvent extends PlayerSaveEvent { */ @RestrictedApi( explanation = "Constructor is not considered part of the API and may be subject to change.", - link = "", allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java" ) @ApiStatus.Internal diff --git a/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java b/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java index a70f222a..3fd98039 100644 --- a/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/PlayerSaveEvent.java @@ -27,7 +27,6 @@ public class PlayerSaveEvent extends PlayerEvent implements Cancellable { */ @RestrictedApi( explanation = "Constructor is not considered part of the API and may be subject to change.", - link = "", allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java" ) @ApiStatus.Internal diff --git a/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java b/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java index 2072ecfa..ed00a5ee 100644 --- a/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java +++ b/api/src/main/java/com/lishid/openinv/event/PlayerToggledEvent.java @@ -22,7 +22,6 @@ public class PlayerToggledEvent extends Event { @RestrictedApi( explanation = "Constructor is not considered part of the API and may be subject to change.", - link = "", allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java" ) @ApiStatus.Internal diff --git a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java index 3930165c..2793526d 100644 --- a/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java +++ b/api/src/main/java/com/lishid/openinv/internal/ISpecialInventory.java @@ -50,8 +50,11 @@ public interface ISpecialInventory { /** * Mark the owner of the inventory offline. + * + * @deprecated No longer used by implementations. */ - void setPlayerOffline(); + @Deprecated(forRemoval = true, since = "5.1.11") + default void setPlayerOffline() {} /** * Get whether the inventory is being viewed by any users. diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index e3a0182c..6b3aa005 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -90,7 +90,6 @@ public static boolean isEnderChest(@NotNull Inventory inventory) { @RestrictedApi( explanation = "Not part of the API.", - link = "", allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java" ) @ApiStatus.Internal diff --git a/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java b/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java deleted file mode 100644 index d0f9a23e..00000000 --- a/common/src/main/java/com/lishid/openinv/internal/InventoryViewTitle.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.lishid.openinv.internal; - -import com.lishid.openinv.util.lang.LanguageManager; -import com.lishid.openinv.util.lang.Replacement; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - -public enum InventoryViewTitle { - - PLAYER_INVENTORY("container.player", "'s Inventory"), - ENDER_CHEST("container.enderchest", "'s Ender Chest"); - - private final String localizationKey; - private final String defaultSuffix; - - InventoryViewTitle(String localizationKey, String defaultSuffix) { - this.localizationKey = localizationKey; - this.defaultSuffix = defaultSuffix; - } - - public @NotNull String getTitle( - @NotNull LanguageManager lang, - @NotNull Player viewer, - @NotNull ISpecialInventory inventory - ) { - HumanEntity owner = inventory.getPlayer(); - - String localTitle = lang.getLocalizedMessage( - viewer, - localizationKey, - new Replacement("%player%", owner.getName()) - ); - return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + defaultSuffix); - } - - public static @Nullable InventoryViewTitle of(@NotNull ISpecialInventory inventory) { - if (inventory instanceof ISpecialPlayerInventory) { - return PLAYER_INVENTORY; - } else if (inventory instanceof ISpecialEnderChest) { - return ENDER_CHEST; - } else { - return null; - } - } - -} diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java index d62606cf..f3160570 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java @@ -71,10 +71,6 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { items = activeItems; } - @Override - public void setPlayerOffline() { - } - @Override public @NotNull org.bukkit.entity.Player getPlayer() { return owner.getBukkitEntity(); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 36217a75..d93e7479 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -248,10 +248,6 @@ public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { slots.forEach(slot -> slot.setHolder(newOwner)); } - @Override - public void setPlayerOffline() { - } - @Override public boolean isInUse() { return !transaction.isEmpty(); diff --git a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java index 8cd585d3..5927bc99 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java @@ -40,15 +40,22 @@ protected boolean isAccessInventory(@NotNull Command command) { @Override protected @Nullable String getTargetIdentifer( @NotNull CommandSender sender, + @NotNull Command command, @Nullable String argument, boolean accessInv ) { + // If argument is provided, use it. if (argument != null) { return argument; } + + // For players, default to self. if (sender instanceof Player player) { return player.getUniqueId().toString(); } + + // For console, argument is required. Send usage. + sender.sendMessage(command.getUsage()); return null; } @@ -87,7 +94,7 @@ protected void handle( manager.save(onlineTarget.getUniqueId()); lang.sendMessage( sender, - "messages.info.inventoryCleared", + accessInv ? "messages.info.clear.inventory" : "messages.info.clear.enderchest", new Replacement("%target%", onlineTarget.getDisplayName()) ); } diff --git a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index 14a5bdf9..9cc9f63b 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -62,6 +62,7 @@ protected boolean isAccessInventory(@NotNull Command command) { @Override protected @Nullable String getTargetIdentifer( @NotNull CommandSender sender, + @NotNull Command command, @Nullable String argument, boolean accessInv ) { diff --git a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java index f4901893..268188c0 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java @@ -54,7 +54,7 @@ public boolean onCommand( boolean accessInv = isAccessInventory(command); // Get target identifier from parameters. - String targetId = getTargetIdentifer(sender, args.length > 0 ? args[0] : null, accessInv); + String targetId = getTargetIdentifer(sender, command, args.length > 0 ? args[0] : null, accessInv); if (targetId == null) { return true; } @@ -90,7 +90,7 @@ public void run() { } }.runTaskAsynchronously(this.plugin); - return false; + return true; } /** @@ -114,6 +114,7 @@ public void run() { */ protected abstract @Nullable String getTargetIdentifer( @NotNull CommandSender sender, + @NotNull Command command, @Nullable String argument, boolean accessInv ); diff --git a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java index f5282f2e..d6460197 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InventoryManager.java @@ -1,7 +1,5 @@ package com.lishid.openinv.util; -import com.github.jikoo.planarwrappers.util.version.BukkitVersions; -import com.github.jikoo.planarwrappers.util.version.Version; import com.google.errorprone.annotations.Keep; import com.lishid.openinv.OpenInv; import com.lishid.openinv.event.OpenEvents; @@ -19,7 +17,6 @@ import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -129,10 +126,7 @@ private void onPlayerJoin(@NotNull PlayerJoinEvent event) { private void onPlayerQuit(@NotNull PlayerQuitEvent event) { consumeLoaded( event.getPlayer().getUniqueId(), - inventory -> { - inventory.setPlayerOffline(); - checkViewerAccess(inventory, false); - } + inventory -> checkViewerAccess(inventory, false) ); } @@ -218,15 +212,6 @@ private void checkViewerAccess(@NotNull T inventor // Copy viewers so we don't modify the list we're iterating over when closing inventories. List viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers()); - // Legacy: Owner is always a viewer of own inventory. - if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) { - if (inventory instanceof ISpecialPlayerInventory) { - Inventory active = owner.getOpenInventory().getTopInventory(); - if (!active.equals(inventory.getBukkitInventory())) { - viewers.remove(owner); - } - } - } for (HumanEntity viewer : viewers) { if (alwaysDenied diff --git a/plugin/src/main/resources/locale/de.yml b/plugin/src/main/resources/locale/de.yml index 9a00b1db..e866e3f9 100644 --- a/plugin/src/main/resources/locale/de.yml +++ b/plugin/src/main/resources/locale/de.yml @@ -25,6 +25,3 @@ messages: matches: 'Container hat %target%: %detail%' 'on': 'an' 'off': 'aus' -container: - player: '%player%''s Inventar' - enderchest: '%player%''s Endertruhe' diff --git a/plugin/src/main/resources/locale/en.yml b/plugin/src/main/resources/locale/en.yml index 2cdf06e2..287c83da 100644 --- a/plugin/src/main/resources/locale/en.yml +++ b/plugin/src/main/resources/locale/en.yml @@ -16,7 +16,9 @@ messages: containerBlocked: 'You are opening a blocked container.' containerBlockedSilent: 'You are opening a blocked container silently.' containerSilent: 'You are opening a container silently.' - inventoryCleared: 'Cleared %target%''s inventory.' + clear: + inventory: 'Cleared %target%''s inventory.' + enderchest: 'Cleared %target%''s ender chest.' settingState: '%setting%: %state%' player: noMatches: 'No players found with %target%.' @@ -26,6 +28,3 @@ messages: matches: 'Containers holding %target%: %detail%' 'on': 'on' 'off': 'off' -container: - player: '%player%''s Inventory' - enderchest: '%player%''s Ender Chest' diff --git a/plugin/src/main/resources/locale/es.yml b/plugin/src/main/resources/locale/es.yml index 05d45dcf..d4a2b69c 100644 --- a/plugin/src/main/resources/locale/es.yml +++ b/plugin/src/main/resources/locale/es.yml @@ -25,7 +25,3 @@ messages: matches: 'Contenedores con %target%: %detail%' 'on': 'activado' 'off': 'desactivado' -container: - player: 'Inventario de %player%' - enderchest: 'Cofre de Ender de %player%' - \ No newline at end of file diff --git a/plugin/src/main/resources/locale/pt.yml b/plugin/src/main/resources/locale/pt.yml index 4d64c43e..d0cd5d89 100644 --- a/plugin/src/main/resources/locale/pt.yml +++ b/plugin/src/main/resources/locale/pt.yml @@ -25,6 +25,3 @@ messages: matches: 'Recipientes contendo %target%: %detail%' 'on': 'ligado' 'off': 'desligado' -container: - player: 'Inventario de %player%' - enderchest: 'Bau de Ender de %player%' diff --git a/plugin/src/main/resources/locale/zh_cn.yml b/plugin/src/main/resources/locale/zh_cn.yml index aa696f19..ab0d34cb 100644 --- a/plugin/src/main/resources/locale/zh_cn.yml +++ b/plugin/src/main/resources/locale/zh_cn.yml @@ -26,6 +26,3 @@ messages: matches: '找到放有 %target% 的储物箱 : %detail%' 'on': '开启' 'off': '关闭' -container: - player: '%player% 的物品栏' - enderchest: '%player% 的末影箱' diff --git a/plugin/src/main/resources/locale/zh_tw.yml b/plugin/src/main/resources/locale/zh_tw.yml index 2a798241..b6a16d26 100644 --- a/plugin/src/main/resources/locale/zh_tw.yml +++ b/plugin/src/main/resources/locale/zh_tw.yml @@ -26,6 +26,3 @@ messages: matches: '找到放有 %target% 的儲物箱 : %detail%' 'on': '開啟' 'off': '關閉' -container: - player: '%player% 的物品欄' - enderchest: '%player% 的終界箱' From b5599450f5e0079ffa4df4a7aedea7227b370f59 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 20 May 2025 09:23:01 -0400 Subject: [PATCH 296/340] Bump version to 5.1.11 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d120e1af..25fa07ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.11-SNAPSHOT +version=5.1.11 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 17dec41feabbf17c52e06834dd4d9105fb8c0040 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 20 May 2025 09:23:04 -0400 Subject: [PATCH 297/340] Bump version to 5.1.12-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 25fa07ea..2d8ed732 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.11 +version=5.1.12-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 19d030dd4d8fc0f51f87657c164e399c50796639 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 21 May 2025 09:14:53 -0400 Subject: [PATCH 298/340] Improve clear parameter handling Don't tab complete parameter for those who can't target others Don't load players for those who can't target others --- .../openinv/command/ClearInvCommand.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java index 5927bc99..1960ec88 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java @@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; import java.util.logging.Level; public class ClearInvCommand extends PlayerLookupCommand { @@ -44,6 +45,17 @@ protected boolean isAccessInventory(@NotNull Command command) { @Nullable String argument, boolean accessInv ) { + if (!Permissions.CLEAR_OTHER.hasPermission(sender)) { + // If the sender does not have permissions to clear others, use self. + if (sender instanceof Player player) { + return player.getUniqueId().toString(); + } + + // Console can't target itself. Send error. + lang.sendMessage(sender, "messages.error.permissionOpenOther"); + return null; + } + // If argument is provided, use it. if (argument != null) { return argument; @@ -99,4 +111,18 @@ protected void handle( ); } + @Override + public List onTabComplete( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args + ) { + if (!Permissions.CLEAR_OTHER.hasPermission(sender)) { + return List.of(); + } + + return super.onTabComplete(sender, command, label, args); + } + } From f742cf5b77079dd23482f75d0f522fca0062e038 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 21 May 2025 09:28:46 -0400 Subject: [PATCH 299/340] Update paperweight-userdev for new bundle version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index af636ac9..a401f82a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ spigotapi = "1.21.5-R0.1-SNAPSHOT" specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2" -paperweight = "2.0.0-beta.16" +paperweight = "2.0.0-beta.17" shadow = "8.3.6" folia-scheduler-wrapper = "v0.0.3" errorprone-core = "2.38.0" From b589f79e5752fee0fead236e1d16d994c2fb17ea Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 21 May 2025 10:02:38 -0400 Subject: [PATCH 300/340] Update gradle wrapper to 8.14 --- gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++- gradlew | 43 ++++++++++++++++------- gradlew.bat | 37 ++++++++++--------- 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch literal 43583 zcma&N1CXTcmMvW9vTb(Rwr$&4wr$(C?dmSu>@vG-+vuvg^_??!{yS%8zW-#zn-LkA z5&1^$^{lnmUON?}LBF8_K|(?T0Ra(xUH{($5eN!MR#ZihR#HxkUPe+_R8Cn`RRs(P z_^*#_XlXmGv7!4;*Y%p4nw?{bNp@UZHv1?Um8r6)Fei3p@ClJn0ECfg1hkeuUU@Or zDaPa;U3fE=3L}DooL;8f;P0ipPt0Z~9P0)lbStMS)ag54=uL9ia-Lm3nh|@(Y?B`; zx_#arJIpXH!U{fbCbI^17}6Ri*H<>OLR%c|^mh8+)*h~K8Z!9)DPf zR2h?lbDZQ`p9P;&DQ4F0sur@TMa!Y}S8irn(%d-gi0*WxxCSk*A?3lGh=gcYN?FGl z7D=Js!i~0=u3rox^eO3i@$0=n{K1lPNU zwmfjRVmLOCRfe=seV&P*1Iq=^i`502keY8Uy-WNPwVNNtJFx?IwAyRPZo2Wo1+S(xF37LJZ~%i)kpFQ3Fw=mXfd@>%+)RpYQLnr}B~~zoof(JVm^^&f zxKV^+3D3$A1G;qh4gPVjhrC8e(VYUHv#dy^)(RoUFM?o%W-EHxufuWf(l*@-l+7vt z=l`qmR56K~F|v<^Pd*p~1_y^P0P^aPC##d8+HqX4IR1gu+7w#~TBFphJxF)T$2WEa zxa?H&6=Qe7d(#tha?_1uQys2KtHQ{)Qco)qwGjrdNL7thd^G5i8Os)CHqc>iOidS} z%nFEDdm=GXBw=yXe1W-ShHHFb?Cc70+$W~z_+}nAoHFYI1MV1wZegw*0y^tC*s%3h zhD3tN8b=Gv&rj}!SUM6|ajSPp*58KR7MPpI{oAJCtY~JECm)*m_x>AZEu>DFgUcby z1Qaw8lU4jZpQ_$;*7RME+gq1KySGG#Wql>aL~k9tLrSO()LWn*q&YxHEuzmwd1?aAtI zBJ>P=&$=l1efe1CDU;`Fd+_;&wI07?V0aAIgc(!{a z0Jg6Y=inXc3^n!U0Atk`iCFIQooHqcWhO(qrieUOW8X(x?(RD}iYDLMjSwffH2~tB z)oDgNBLB^AJBM1M^c5HdRx6fBfka`(LD-qrlh5jqH~);#nw|iyp)()xVYak3;Ybik z0j`(+69aK*B>)e_p%=wu8XC&9e{AO4c~O1U`5X9}?0mrd*m$_EUek{R?DNSh(=br# z#Q61gBzEpmy`$pA*6!87 zSDD+=@fTY7<4A?GLqpA?Pb2z$pbCc4B4zL{BeZ?F-8`s$?>*lXXtn*NC61>|*w7J* z$?!iB{6R-0=KFmyp1nnEmLsA-H0a6l+1uaH^g%c(p{iT&YFrbQ$&PRb8Up#X3@Zsk zD^^&LK~111%cqlP%!_gFNa^dTYT?rhkGl}5=fL{a`UViaXWI$k-UcHJwmaH1s=S$4 z%4)PdWJX;hh5UoK?6aWoyLxX&NhNRqKam7tcOkLh{%j3K^4Mgx1@i|Pi&}<^5>hs5 zm8?uOS>%)NzT(%PjVPGa?X%`N2TQCKbeH2l;cTnHiHppPSJ<7y-yEIiC!P*ikl&!B z%+?>VttCOQM@ShFguHVjxX^?mHX^hSaO_;pnyh^v9EumqSZTi+#f&_Vaija0Q-e*| z7ulQj6Fs*bbmsWp{`auM04gGwsYYdNNZcg|ph0OgD>7O}Asn7^Z=eI>`$2*v78;sj-}oMoEj&@)9+ycEOo92xSyY344^ z11Hb8^kdOvbf^GNAK++bYioknrpdN>+u8R?JxG=!2Kd9r=YWCOJYXYuM0cOq^FhEd zBg2puKy__7VT3-r*dG4c62Wgxi52EMCQ`bKgf*#*ou(D4-ZN$+mg&7$u!! z-^+Z%;-3IDwqZ|K=ah85OLwkO zKxNBh+4QHh)u9D?MFtpbl)us}9+V!D%w9jfAMYEb>%$A;u)rrI zuBudh;5PN}_6J_}l55P3l_)&RMlH{m!)ai-i$g)&*M`eN$XQMw{v^r@-125^RRCF0 z^2>|DxhQw(mtNEI2Kj(;KblC7x=JlK$@78`O~>V!`|1Lm-^JR$-5pUANAnb(5}B}JGjBsliK4& zk6y(;$e&h)lh2)L=bvZKbvh@>vLlreBdH8No2>$#%_Wp1U0N7Ank!6$dFSi#xzh|( zRi{Uw%-4W!{IXZ)fWx@XX6;&(m_F%c6~X8hx=BN1&q}*( zoaNjWabE{oUPb!Bt$eyd#$5j9rItB-h*5JiNi(v^e|XKAj*8(k<5-2$&ZBR5fF|JA z9&m4fbzNQnAU}r8ab>fFV%J0z5awe#UZ|bz?Ur)U9bCIKWEzi2%A+5CLqh?}K4JHi z4vtM;+uPsVz{Lfr;78W78gC;z*yTch~4YkLr&m-7%-xc ztw6Mh2d>_iO*$Rd8(-Cr1_V8EO1f*^@wRoSozS) zy1UoC@pruAaC8Z_7~_w4Q6n*&B0AjOmMWa;sIav&gu z|J5&|{=a@vR!~k-OjKEgPFCzcJ>#A1uL&7xTDn;{XBdeM}V=l3B8fE1--DHjSaxoSjNKEM9|U9#m2<3>n{Iuo`r3UZp;>GkT2YBNAh|b z^jTq-hJp(ebZh#Lk8hVBP%qXwv-@vbvoREX$TqRGTgEi$%_F9tZES@z8Bx}$#5eeG zk^UsLBH{bc2VBW)*EdS({yw=?qmevwi?BL6*=12k9zM5gJv1>y#ML4!)iiPzVaH9% zgSImetD@dam~e>{LvVh!phhzpW+iFvWpGT#CVE5TQ40n%F|p(sP5mXxna+Ev7PDwA zamaV4m*^~*xV+&p;W749xhb_X=$|LD;FHuB&JL5?*Y2-oIT(wYY2;73<^#46S~Gx| z^cez%V7x$81}UWqS13Gz80379Rj;6~WdiXWOSsdmzY39L;Hg3MH43o*y8ibNBBH`(av4|u;YPq%{R;IuYow<+GEsf@R?=@tT@!}?#>zIIn0CoyV!hq3mw zHj>OOjfJM3F{RG#6ujzo?y32m^tgSXf@v=J$ELdJ+=5j|=F-~hP$G&}tDZsZE?5rX ztGj`!S>)CFmdkccxM9eGIcGnS2AfK#gXwj%esuIBNJQP1WV~b~+D7PJTmWGTSDrR` zEAu4B8l>NPuhsk5a`rReSya2nfV1EK01+G!x8aBdTs3Io$u5!6n6KX%uv@DxAp3F@{4UYg4SWJtQ-W~0MDb|j-$lwVn znAm*Pl!?Ps&3wO=R115RWKb*JKoexo*)uhhHBncEDMSVa_PyA>k{Zm2(wMQ(5NM3# z)jkza|GoWEQo4^s*wE(gHz?Xsg4`}HUAcs42cM1-qq_=+=!Gk^y710j=66(cSWqUe zklbm8+zB_syQv5A2rj!Vbw8;|$@C!vfNmNV!yJIWDQ>{+2x zKjuFX`~~HKG~^6h5FntRpnnHt=D&rq0>IJ9#F0eM)Y-)GpRjiN7gkA8wvnG#K=q{q z9dBn8_~wm4J<3J_vl|9H{7q6u2A!cW{bp#r*-f{gOV^e=8S{nc1DxMHFwuM$;aVI^ zz6A*}m8N-&x8;aunp1w7_vtB*pa+OYBw=TMc6QK=mbA-|Cf* zvyh8D4LRJImooUaSb7t*fVfih<97Gf@VE0|z>NcBwBQze);Rh!k3K_sfunToZY;f2 z^HmC4KjHRVg+eKYj;PRN^|E0>Gj_zagfRbrki68I^#~6-HaHg3BUW%+clM1xQEdPYt_g<2K+z!$>*$9nQ>; zf9Bei{?zY^-e{q_*|W#2rJG`2fy@{%6u0i_VEWTq$*(ZN37|8lFFFt)nCG({r!q#9 z5VK_kkSJ3?zOH)OezMT{!YkCuSSn!K#-Rhl$uUM(bq*jY? zi1xbMVthJ`E>d>(f3)~fozjg^@eheMF6<)I`oeJYx4*+M&%c9VArn(OM-wp%M<-`x z7sLP1&3^%Nld9Dhm@$3f2}87!quhI@nwd@3~fZl_3LYW-B?Ia>ui`ELg z&Qfe!7m6ze=mZ`Ia9$z|ARSw|IdMpooY4YiPN8K z4B(ts3p%2i(Td=tgEHX z0UQ_>URBtG+-?0E;E7Ld^dyZ;jjw0}XZ(}-QzC6+NN=40oDb2^v!L1g9xRvE#@IBR zO!b-2N7wVfLV;mhEaXQ9XAU+>=XVA6f&T4Z-@AX!leJ8obP^P^wP0aICND?~w&NykJ#54x3_@r7IDMdRNy4Hh;h*!u(Ol(#0bJdwEo$5437-UBjQ+j=Ic>Q2z` zJNDf0yO6@mr6y1#n3)s(W|$iE_i8r@Gd@!DWDqZ7J&~gAm1#~maIGJ1sls^gxL9LLG_NhU!pTGty!TbhzQnu)I*S^54U6Yu%ZeCg`R>Q zhBv$n5j0v%O_j{QYWG!R9W?5_b&67KB$t}&e2LdMvd(PxN6Ir!H4>PNlerpBL>Zvyy!yw z-SOo8caEpDt(}|gKPBd$qND5#a5nju^O>V&;f890?yEOfkSG^HQVmEbM3Ugzu+UtH zC(INPDdraBN?P%kE;*Ae%Wto&sgw(crfZ#Qy(<4nk;S|hD3j{IQRI6Yq|f^basLY; z-HB&Je%Gg}Jt@={_C{L$!RM;$$|iD6vu#3w?v?*;&()uB|I-XqEKqZPS!reW9JkLewLb!70T7n`i!gNtb1%vN- zySZj{8-1>6E%H&=V}LM#xmt`J3XQoaD|@XygXjdZ1+P77-=;=eYpoEQ01B@L*a(uW zrZeZz?HJsw_4g0vhUgkg@VF8<-X$B8pOqCuWAl28uB|@r`19DTUQQsb^pfqB6QtiT z*`_UZ`fT}vtUY#%sq2{rchyfu*pCg;uec2$-$N_xgjZcoumE5vSI{+s@iLWoz^Mf; zuI8kDP{!XY6OP~q5}%1&L}CtfH^N<3o4L@J@zg1-mt{9L`s^z$Vgb|mr{@WiwAqKg zp#t-lhrU>F8o0s1q_9y`gQNf~Vb!F%70f}$>i7o4ho$`uciNf=xgJ>&!gSt0g;M>*x4-`U)ysFW&Vs^Vk6m%?iuWU+o&m(2Jm26Y(3%TL; zA7T)BP{WS!&xmxNw%J=$MPfn(9*^*TV;$JwRy8Zl*yUZi8jWYF>==j~&S|Xinsb%c z2?B+kpet*muEW7@AzjBA^wAJBY8i|#C{WtO_or&Nj2{=6JTTX05}|H>N2B|Wf!*3_ z7hW*j6p3TvpghEc6-wufFiY!%-GvOx*bZrhZu+7?iSrZL5q9}igiF^*R3%DE4aCHZ zqu>xS8LkW+Auv%z-<1Xs92u23R$nk@Pk}MU5!gT|c7vGlEA%G^2th&Q*zfg%-D^=f z&J_}jskj|Q;73NP4<4k*Y%pXPU2Thoqr+5uH1yEYM|VtBPW6lXaetokD0u z9qVek6Q&wk)tFbQ8(^HGf3Wp16gKmr>G;#G(HRBx?F`9AIRboK+;OfHaLJ(P>IP0w zyTbTkx_THEOs%Q&aPrxbZrJlio+hCC_HK<4%f3ZoSAyG7Dn`=X=&h@m*|UYO-4Hq0 z-Bq&+Ie!S##4A6OGoC~>ZW`Y5J)*ouaFl_e9GA*VSL!O_@xGiBw!AF}1{tB)z(w%c zS1Hmrb9OC8>0a_$BzeiN?rkPLc9%&;1CZW*4}CDDNr2gcl_3z+WC15&H1Zc2{o~i) z)LLW=WQ{?ricmC`G1GfJ0Yp4Dy~Ba;j6ZV4r{8xRs`13{dD!xXmr^Aga|C=iSmor% z8hi|pTXH)5Yf&v~exp3o+sY4B^^b*eYkkCYl*T{*=-0HniSA_1F53eCb{x~1k3*`W zr~};p1A`k{1DV9=UPnLDgz{aJH=-LQo<5%+Em!DNN252xwIf*wF_zS^!(XSm(9eoj z=*dXG&n0>)_)N5oc6v!>-bd(2ragD8O=M|wGW z!xJQS<)u70m&6OmrF0WSsr@I%T*c#Qo#Ha4d3COcX+9}hM5!7JIGF>7<~C(Ear^Sn zm^ZFkV6~Ula6+8S?oOROOA6$C&q&dp`>oR-2Ym3(HT@O7Sd5c~+kjrmM)YmgPH*tL zX+znN>`tv;5eOfX?h{AuX^LK~V#gPCu=)Tigtq9&?7Xh$qN|%A$?V*v=&-2F$zTUv z`C#WyIrChS5|Kgm_GeudCFf;)!WH7FI60j^0o#65o6`w*S7R@)88n$1nrgU(oU0M9 zx+EuMkC>(4j1;m6NoGqEkpJYJ?vc|B zOlwT3t&UgL!pX_P*6g36`ZXQ; z9~Cv}ANFnJGp(;ZhS(@FT;3e)0)Kp;h^x;$*xZn*k0U6-&FwI=uOGaODdrsp-!K$Ac32^c{+FhI-HkYd5v=`PGsg%6I`4d9Jy)uW0y%) zm&j^9WBAp*P8#kGJUhB!L?a%h$hJgQrx!6KCB_TRo%9{t0J7KW8!o1B!NC)VGLM5! zpZy5Jc{`r{1e(jd%jsG7k%I+m#CGS*BPA65ZVW~fLYw0dA-H_}O zrkGFL&P1PG9p2(%QiEWm6x;U-U&I#;Em$nx-_I^wtgw3xUPVVu zqSuKnx&dIT-XT+T10p;yjo1Y)z(x1fb8Dzfn8e yu?e%!_ptzGB|8GrCfu%p?(_ zQccdaaVK$5bz;*rnyK{_SQYM>;aES6Qs^lj9lEs6_J+%nIiuQC*fN;z8md>r_~Mfl zU%p5Dt_YT>gQqfr@`cR!$NWr~+`CZb%dn;WtzrAOI>P_JtsB76PYe*<%H(y>qx-`Kq!X_; z<{RpAqYhE=L1r*M)gNF3B8r(<%8mo*SR2hu zccLRZwGARt)Hlo1euqTyM>^!HK*!Q2P;4UYrysje@;(<|$&%vQekbn|0Ruu_Io(w4#%p6ld2Yp7tlA`Y$cciThP zKzNGIMPXX%&Ud0uQh!uQZz|FB`4KGD?3!ND?wQt6!n*f4EmCoJUh&b?;B{|lxs#F- z31~HQ`SF4x$&v00@(P+j1pAaj5!s`)b2RDBp*PB=2IB>oBF!*6vwr7Dp%zpAx*dPr zb@Zjq^XjN?O4QcZ*O+8>)|HlrR>oD*?WQl5ri3R#2?*W6iJ>>kH%KnnME&TT@ZzrHS$Q%LC?n|e>V+D+8D zYc4)QddFz7I8#}y#Wj6>4P%34dZH~OUDb?uP%-E zwjXM(?Sg~1!|wI(RVuxbu)-rH+O=igSho_pDCw(c6b=P zKk4ATlB?bj9+HHlh<_!&z0rx13K3ZrAR8W)!@Y}o`?a*JJsD+twZIv`W)@Y?Amu_u zz``@-e2X}27$i(2=9rvIu5uTUOVhzwu%mNazS|lZb&PT;XE2|B&W1>=B58#*!~D&) zfVmJGg8UdP*fx(>Cj^?yS^zH#o-$Q-*$SnK(ZVFkw+er=>N^7!)FtP3y~Xxnu^nzY zikgB>Nj0%;WOltWIob|}%lo?_C7<``a5hEkx&1ku$|)i>Rh6@3h*`slY=9U}(Ql_< zaNG*J8vb&@zpdhAvv`?{=zDedJ23TD&Zg__snRAH4eh~^oawdYi6A3w8<Ozh@Kw)#bdktM^GVb zrG08?0bG?|NG+w^&JvD*7LAbjED{_Zkc`3H!My>0u5Q}m!+6VokMLXxl`Mkd=g&Xx z-a>m*#G3SLlhbKB!)tnzfWOBV;u;ftU}S!NdD5+YtOjLg?X}dl>7m^gOpihrf1;PY zvll&>dIuUGs{Qnd- zwIR3oIrct8Va^Tm0t#(bJD7c$Z7DO9*7NnRZorrSm`b`cxz>OIC;jSE3DO8`hX955ui`s%||YQtt2 z5DNA&pG-V+4oI2s*x^>-$6J?p=I>C|9wZF8z;VjR??Icg?1w2v5Me+FgAeGGa8(3S z4vg*$>zC-WIVZtJ7}o9{D-7d>zCe|z#<9>CFve-OPAYsneTb^JH!Enaza#j}^mXy1 z+ULn^10+rWLF6j2>Ya@@Kq?26>AqK{A_| zQKb*~F1>sE*=d?A?W7N2j?L09_7n+HGi{VY;MoTGr_)G9)ot$p!-UY5zZ2Xtbm=t z@dpPSGwgH=QtIcEulQNI>S-#ifbnO5EWkI;$A|pxJd885oM+ zGZ0_0gDvG8q2xebj+fbCHYfAXuZStH2j~|d^sBAzo46(K8n59+T6rzBwK)^rfPT+B zyIFw)9YC-V^rhtK`!3jrhmW-sTmM+tPH+;nwjL#-SjQPUZ53L@A>y*rt(#M(qsiB2 zx6B)dI}6Wlsw%bJ8h|(lhkJVogQZA&n{?Vgs6gNSXzuZpEyu*xySy8ro07QZ7Vk1!3tJphN_5V7qOiyK8p z#@jcDD8nmtYi1^l8ml;AF<#IPK?!pqf9D4moYk>d99Im}Jtwj6c#+A;f)CQ*f-hZ< z=p_T86jog%!p)D&5g9taSwYi&eP z#JuEK%+NULWus;0w32-SYFku#i}d~+{Pkho&^{;RxzP&0!RCm3-9K6`>KZpnzS6?L z^H^V*s!8<>x8bomvD%rh>Zp3>Db%kyin;qtl+jAv8Oo~1g~mqGAC&Qi_wy|xEt2iz zWAJEfTV%cl2Cs<1L&DLRVVH05EDq`pH7Oh7sR`NNkL%wi}8n>IXcO40hp+J+sC!W?!krJf!GJNE8uj zg-y~Ns-<~D?yqbzVRB}G>0A^f0!^N7l=$m0OdZuqAOQqLc zX?AEGr1Ht+inZ-Qiwnl@Z0qukd__a!C*CKuGdy5#nD7VUBM^6OCpxCa2A(X;e0&V4 zM&WR8+wErQ7UIc6LY~Q9x%Sn*Tn>>P`^t&idaOEnOd(Ufw#>NoR^1QdhJ8s`h^|R_ zXX`c5*O~Xdvh%q;7L!_!ohf$NfEBmCde|#uVZvEo>OfEq%+Ns7&_f$OR9xsihRpBb z+cjk8LyDm@U{YN>+r46?nn{7Gh(;WhFw6GAxtcKD+YWV?uge>;+q#Xx4!GpRkVZYu zzsF}1)7$?%s9g9CH=Zs+B%M_)+~*j3L0&Q9u7!|+T`^O{xE6qvAP?XWv9_MrZKdo& z%IyU)$Q95AB4!#hT!_dA>4e@zjOBD*Y=XjtMm)V|+IXzjuM;(l+8aA5#Kaz_$rR6! zj>#&^DidYD$nUY(D$mH`9eb|dtV0b{S>H6FBfq>t5`;OxA4Nn{J(+XihF(stSche7$es&~N$epi&PDM_N`As;*9D^L==2Q7Z2zD+CiU(|+-kL*VG+&9!Yb3LgPy?A zm7Z&^qRG_JIxK7-FBzZI3Q<;{`DIxtc48k> zc|0dmX;Z=W$+)qE)~`yn6MdoJ4co;%!`ddy+FV538Y)j(vg}5*k(WK)KWZ3WaOG!8 z!syGn=s{H$odtpqFrT#JGM*utN7B((abXnpDM6w56nhw}OY}0TiTG1#f*VFZr+^-g zbP10`$LPq_;PvrA1XXlyx2uM^mrjTzX}w{yuLo-cOClE8MMk47T25G8M!9Z5ypOSV zAJUBGEg5L2fY)ZGJb^E34R2zJ?}Vf>{~gB!8=5Z) z9y$>5c)=;o0HeHHSuE4U)#vG&KF|I%-cF6f$~pdYJWk_dD}iOA>iA$O$+4%@>JU08 zS`ep)$XLPJ+n0_i@PkF#ri6T8?ZeAot$6JIYHm&P6EB=BiaNY|aA$W0I+nz*zkz_z zkEru!tj!QUffq%)8y0y`T&`fuus-1p>=^hnBiBqD^hXrPs`PY9tU3m0np~rISY09> z`P3s=-kt_cYcxWd{de@}TwSqg*xVhp;E9zCsnXo6z z?f&Sv^U7n4`xr=mXle94HzOdN!2kB~4=%)u&N!+2;z6UYKUDqi-s6AZ!haB;@&B`? z_TRX0%@suz^TRdCb?!vNJYPY8L_}&07uySH9%W^Tc&1pia6y1q#?*Drf}GjGbPjBS zbOPcUY#*$3sL2x4v_i*Y=N7E$mR}J%|GUI(>WEr+28+V z%v5{#e!UF*6~G&%;l*q*$V?&r$Pp^sE^i-0$+RH3ERUUdQ0>rAq2(2QAbG}$y{de( z>{qD~GGuOk559Y@%$?N^1ApVL_a704>8OD%8Y%8B;FCt%AoPu8*D1 zLB5X>b}Syz81pn;xnB}%0FnwazlWfUV)Z-~rZg6~b z6!9J$EcE&sEbzcy?CI~=boWA&eeIa%z(7SE^qgVLz??1Vbc1*aRvc%Mri)AJaAG!p z$X!_9Ds;Zz)f+;%s&dRcJt2==P{^j3bf0M=nJd&xwUGlUFn?H=2W(*2I2Gdu zv!gYCwM10aeus)`RIZSrCK=&oKaO_Ry~D1B5!y0R=%!i2*KfXGYX&gNv_u+n9wiR5 z*e$Zjju&ODRW3phN925%S(jL+bCHv6rZtc?!*`1TyYXT6%Ju=|X;6D@lq$8T zW{Y|e39ioPez(pBH%k)HzFITXHvnD6hw^lIoUMA;qAJ^CU?top1fo@s7xT13Fvn1H z6JWa-6+FJF#x>~+A;D~;VDs26>^oH0EI`IYT2iagy23?nyJ==i{g4%HrAf1-*v zK1)~@&(KkwR7TL}L(A@C_S0G;-GMDy=MJn2$FP5s<%wC)4jC5PXoxrQBFZ_k0P{{s@sz+gX`-!=T8rcB(=7vW}^K6oLWMmp(rwDh}b zwaGGd>yEy6fHv%jM$yJXo5oMAQ>c9j`**}F?MCry;T@47@r?&sKHgVe$MCqk#Z_3S z1GZI~nOEN*P~+UaFGnj{{Jo@16`(qVNtbU>O0Hf57-P>x8Jikp=`s8xWs^dAJ9lCQ z)GFm+=OV%AMVqVATtN@|vp61VVAHRn87}%PC^RAzJ%JngmZTasWBAWsoAqBU+8L8u z4A&Pe?fmTm0?mK-BL9t+{y7o(7jm+RpOhL9KnY#E&qu^}B6=K_dB}*VlSEiC9fn)+V=J;OnN)Ta5v66ic1rG+dGAJ1 z1%Zb_+!$=tQ~lxQrzv3x#CPb?CekEkA}0MYSgx$Jdd}q8+R=ma$|&1a#)TQ=l$1tQ z=tL9&_^vJ)Pk}EDO-va`UCT1m#Uty1{v^A3P~83_#v^ozH}6*9mIjIr;t3Uv%@VeW zGL6(CwCUp)Jq%G0bIG%?{_*Y#5IHf*5M@wPo6A{$Um++Co$wLC=J1aoG93&T7Ho}P z=mGEPP7GbvoG!uD$k(H3A$Z))+i{Hy?QHdk>3xSBXR0j!11O^mEe9RHmw!pvzv?Ua~2_l2Yh~_!s1qS`|0~0)YsbHSz8!mG)WiJE| z2f($6TQtt6L_f~ApQYQKSb=`053LgrQq7G@98#igV>y#i==-nEjQ!XNu9 z~;mE+gtj4IDDNQJ~JVk5Ux6&LCSFL!y=>79kE9=V}J7tD==Ga+IW zX)r7>VZ9dY=V&}DR))xUoV!u(Z|%3ciQi_2jl}3=$Agc(`RPb z8kEBpvY>1FGQ9W$n>Cq=DIpski};nE)`p3IUw1Oz0|wxll^)4dq3;CCY@RyJgFgc# zKouFh!`?Xuo{IMz^xi-h=StCis_M7yq$u) z?XHvw*HP0VgR+KR6wI)jEMX|ssqYvSf*_3W8zVTQzD?3>H!#>InzpSO)@SC8q*ii- z%%h}_#0{4JG;Jm`4zg};BPTGkYamx$Xo#O~lBirRY)q=5M45n{GCfV7h9qwyu1NxOMoP4)jjZMxmT|IQQh0U7C$EbnMN<3)Kk?fFHYq$d|ICu>KbY_hO zTZM+uKHe(cIZfEqyzyYSUBZa8;Fcut-GN!HSA9ius`ltNebF46ZX_BbZNU}}ZOm{M2&nANL9@0qvih15(|`S~z}m&h!u4x~(%MAO$jHRWNfuxWF#B)E&g3ghSQ9|> z(MFaLQj)NE0lowyjvg8z0#m6FIuKE9lDO~Glg}nSb7`~^&#(Lw{}GVOS>U)m8bF}x zVjbXljBm34Cs-yM6TVusr+3kYFjr28STT3g056y3cH5Tmge~ASxBj z%|yb>$eF;WgrcOZf569sDZOVwoo%8>XO>XQOX1OyN9I-SQgrm;U;+#3OI(zrWyow3 zk==|{lt2xrQ%FIXOTejR>;wv(Pb8u8}BUpx?yd(Abh6? zsoO3VYWkeLnF43&@*#MQ9-i-d0t*xN-UEyNKeyNMHw|A(k(_6QKO=nKMCxD(W(Yop zsRQ)QeL4X3Lxp^L%wzi2-WVSsf61dqliPUM7srDB?Wm6Lzn0&{*}|IsKQW;02(Y&| zaTKv|`U(pSzuvR6Rduu$wzK_W-Y-7>7s?G$)U}&uK;<>vU}^^ns@Z!p+9?St1s)dG zK%y6xkPyyS1$~&6v{kl?Md6gwM|>mt6Upm>oa8RLD^8T{0?HC!Z>;(Bob7el(DV6x zi`I)$&E&ngwFS@bi4^xFLAn`=fzTC;aimE^!cMI2n@Vo%Ae-ne`RF((&5y6xsjjAZ zVguVoQ?Z9uk$2ON;ersE%PU*xGO@T*;j1BO5#TuZKEf(mB7|g7pcEA=nYJ{s3vlbg zd4-DUlD{*6o%Gc^N!Nptgay>j6E5;3psI+C3Q!1ZIbeCubW%w4pq9)MSDyB{HLm|k zxv-{$$A*pS@csolri$Ge<4VZ}e~78JOL-EVyrbxKra^d{?|NnPp86!q>t<&IP07?Z z^>~IK^k#OEKgRH+LjllZXk7iA>2cfH6+(e&9ku5poo~6y{GC5>(bRK7hwjiurqAiZ zg*DmtgY}v83IjE&AbiWgMyFbaRUPZ{lYiz$U^&Zt2YjG<%m((&_JUbZcfJ22(>bi5 z!J?<7AySj0JZ&<-qXX;mcV!f~>G=sB0KnjWca4}vrtunD^1TrpfeS^4dvFr!65knK zZh`d;*VOkPs4*-9kL>$GP0`(M!j~B;#x?Ba~&s6CopvO86oM?-? zOw#dIRc;6A6T?B`Qp%^<U5 z19x(ywSH$_N+Io!6;e?`tWaM$`=Db!gzx|lQ${DG!zb1Zl&|{kX0y6xvO1o z220r<-oaS^^R2pEyY;=Qllqpmue|5yI~D|iI!IGt@iod{Opz@*ml^w2bNs)p`M(Io z|E;;m*Xpjd9l)4G#KaWfV(t8YUn@A;nK^#xgv=LtnArX|vWQVuw3}B${h+frU2>9^ z!l6)!Uo4`5k`<<;E(ido7M6lKTgWezNLq>U*=uz&s=cc$1%>VrAeOoUtA|T6gO4>UNqsdK=NF*8|~*sl&wI=x9-EGiq*aqV!(VVXA57 zw9*o6Ir8Lj1npUXvlevtn(_+^X5rzdR>#(}4YcB9O50q97%rW2me5_L=%ffYPUSRc z!vv?Kv>dH994Qi>U(a<0KF6NH5b16enCp+mw^Hb3Xs1^tThFpz!3QuN#}KBbww`(h z7GO)1olDqy6?T$()R7y%NYx*B0k_2IBiZ14&8|JPFxeMF{vW>HF-Vi3+ZOI=+qP}n zw(+!WcTd~4ZJX1!ZM&y!+uyt=&i!+~d(V%GjH;-NsEEv6nS1TERt|RHh!0>W4+4pp z1-*EzAM~i`+1f(VEHI8So`S`akPfPTfq*`l{Fz`hS%k#JS0cjT2mS0#QLGf=J?1`he3W*;m4)ce8*WFq1sdP=~$5RlH1EdWm|~dCvKOi4*I_96{^95p#B<(n!d?B z=o`0{t+&OMwKcxiBECznJcfH!fL(z3OvmxP#oWd48|mMjpE||zdiTBdWelj8&Qosv zZFp@&UgXuvJw5y=q6*28AtxZzo-UUpkRW%ne+Ylf!V-0+uQXBW=5S1o#6LXNtY5!I z%Rkz#(S8Pjz*P7bqB6L|M#Er{|QLae-Y{KA>`^} z@lPjeX>90X|34S-7}ZVXe{wEei1<{*e8T-Nbj8JmD4iwcE+Hg_zhkPVm#=@b$;)h6 z<<6y`nPa`f3I6`!28d@kdM{uJOgM%`EvlQ5B2bL)Sl=|y@YB3KeOzz=9cUW3clPAU z^sYc}xf9{4Oj?L5MOlYxR{+>w=vJjvbyO5}ptT(o6dR|ygO$)nVCvNGnq(6;bHlBd zl?w-|plD8spjDF03g5ip;W3Z z><0{BCq!Dw;h5~#1BuQilq*TwEu)qy50@+BE4bX28+7erX{BD4H)N+7U`AVEuREE8 z;X?~fyhF-x_sRfHIj~6f(+^@H)D=ngP;mwJjxhQUbUdzk8f94Ab%59-eRIq?ZKrwD z(BFI=)xrUlgu(b|hAysqK<}8bslmNNeD=#JW*}^~Nrswn^xw*nL@Tx!49bfJecV&KC2G4q5a!NSv)06A_5N3Y?veAz;Gv+@U3R% z)~UA8-0LvVE{}8LVDOHzp~2twReqf}ODIyXMM6=W>kL|OHcx9P%+aJGYi_Om)b!xe zF40Vntn0+VP>o<$AtP&JANjXBn7$}C@{+@3I@cqlwR2MdwGhVPxlTIcRVu@Ho-wO` z_~Or~IMG)A_`6-p)KPS@cT9mu9RGA>dVh5wY$NM9-^c@N=hcNaw4ITjm;iWSP^ZX| z)_XpaI61<+La+U&&%2a z0za$)-wZP@mwSELo#3!PGTt$uy0C(nTT@9NX*r3Ctw6J~7A(m#8fE)0RBd`TdKfAT zCf@$MAxjP`O(u9s@c0Fd@|}UQ6qp)O5Q5DPCeE6mSIh|Rj{$cAVIWsA=xPKVKxdhg zLzPZ`3CS+KIO;T}0Ip!fAUaNU>++ZJZRk@I(h<)RsJUhZ&Ru9*!4Ptn;gX^~4E8W^TSR&~3BAZc#HquXn)OW|TJ`CTahk+{qe`5+ixON^zA9IFd8)kc%*!AiLu z>`SFoZ5bW-%7}xZ>gpJcx_hpF$2l+533{gW{a7ce^B9sIdmLrI0)4yivZ^(Vh@-1q zFT!NQK$Iz^xu%|EOK=n>ug;(7J4OnS$;yWmq>A;hsD_0oAbLYhW^1Vdt9>;(JIYjf zdb+&f&D4@4AS?!*XpH>8egQvSVX`36jMd>$+RgI|pEg))^djhGSo&#lhS~9%NuWfX zDDH;3T*GzRT@5=7ibO>N-6_XPBYxno@mD_3I#rDD?iADxX`! zh*v8^i*JEMzyN#bGEBz7;UYXki*Xr(9xXax(_1qVW=Ml)kSuvK$coq2A(5ZGhs_pF z$*w}FbN6+QDseuB9=fdp_MTs)nQf!2SlROQ!gBJBCXD&@-VurqHj0wm@LWX-TDmS= z71M__vAok|@!qgi#H&H%Vg-((ZfxPAL8AI{x|VV!9)ZE}_l>iWk8UPTGHs*?u7RfP z5MC&=c6X;XlUzrz5q?(!eO@~* zoh2I*%J7dF!!_!vXoSIn5o|wj1#_>K*&CIn{qSaRc&iFVxt*^20ngCL;QonIS>I5^ zMw8HXm>W0PGd*}Ko)f|~dDd%;Wu_RWI_d;&2g6R3S63Uzjd7dn%Svu-OKpx*o|N>F zZg=-~qLb~VRLpv`k zWSdfHh@?dp=s_X`{yxOlxE$4iuyS;Z-x!*E6eqmEm*j2bE@=ZI0YZ5%Yj29!5+J$4h{s($nakA`xgbO8w zi=*r}PWz#lTL_DSAu1?f%-2OjD}NHXp4pXOsCW;DS@BC3h-q4_l`<))8WgzkdXg3! zs1WMt32kS2E#L0p_|x+x**TFV=gn`m9BWlzF{b%6j-odf4{7a4y4Uaef@YaeuPhU8 zHBvRqN^;$Jizy+ z=zW{E5<>2gp$pH{M@S*!sJVQU)b*J5*bX4h>5VJve#Q6ga}cQ&iL#=(u+KroWrxa%8&~p{WEUF0il=db;-$=A;&9M{Rq`ouZ5m%BHT6%st%saGsD6)fQgLN}x@d3q>FC;=f%O3Cyg=Ke@Gh`XW za@RajqOE9UB6eE=zhG%|dYS)IW)&y&Id2n7r)6p_)vlRP7NJL(x4UbhlcFXWT8?K=%s7;z?Vjts?y2+r|uk8Wt(DM*73^W%pAkZa1Jd zNoE)8FvQA>Z`eR5Z@Ig6kS5?0h;`Y&OL2D&xnnAUzQz{YSdh0k zB3exx%A2TyI)M*EM6htrxSlep!Kk(P(VP`$p0G~f$smld6W1r_Z+o?=IB@^weq>5VYsYZZR@` z&XJFxd5{|KPZmVOSxc@^%71C@;z}}WhbF9p!%yLj3j%YOlPL5s>7I3vj25 z@xmf=*z%Wb4;Va6SDk9cv|r*lhZ`(y_*M@>q;wrn)oQx%B(2A$9(74>;$zmQ!4fN; z>XurIk-7@wZys<+7XL@0Fhe-f%*=(weaQEdR9Eh6>Kl-EcI({qoZqyzziGwpg-GM#251sK_ z=3|kitS!j%;fpc@oWn65SEL73^N&t>Ix37xgs= zYG%eQDJc|rqHFia0!_sm7`@lvcv)gfy(+KXA@E{3t1DaZ$DijWAcA)E0@X?2ziJ{v z&KOYZ|DdkM{}t+@{@*6ge}m%xfjIxi%qh`=^2Rwz@w0cCvZ&Tc#UmCDbVwABrON^x zEBK43FO@weA8s7zggCOWhMvGGE`baZ62cC)VHyy!5Zbt%ieH+XN|OLbAFPZWyC6)p z4P3%8sq9HdS3=ih^0OOlqTPbKuzQ?lBEI{w^ReUO{V?@`ARsL|S*%yOS=Z%sF)>-y z(LAQdhgAcuF6LQjRYfdbD1g4o%tV4EiK&ElLB&^VZHbrV1K>tHTO{#XTo>)2UMm`2 z^t4s;vnMQgf-njU-RVBRw0P0-m#d-u`(kq7NL&2T)TjI_@iKuPAK-@oH(J8?%(e!0Ir$yG32@CGUPn5w4)+9@8c&pGx z+K3GKESI4*`tYlmMHt@br;jBWTei&(a=iYslc^c#RU3Q&sYp zSG){)V<(g7+8W!Wxeb5zJb4XE{I|&Y4UrFWr%LHkdQ;~XU zgy^dH-Z3lmY+0G~?DrC_S4@=>0oM8Isw%g(id10gWkoz2Q%7W$bFk@mIzTCcIB(K8 zc<5h&ZzCdT=9n-D>&a8vl+=ZF*`uTvQviG_bLde*k>{^)&0o*b05x$MO3gVLUx`xZ z43j+>!u?XV)Yp@MmG%Y`+COH2?nQcMrQ%k~6#O%PeD_WvFO~Kct za4XoCM_X!c5vhRkIdV=xUB3xI2NNStK*8_Zl!cFjOvp-AY=D;5{uXj}GV{LK1~IE2 z|KffUiBaStRr;10R~K2VVtf{TzM7FaPm;Y(zQjILn+tIPSrJh&EMf6evaBKIvi42-WYU9Vhj~3< zZSM-B;E`g_o8_XTM9IzEL=9Lb^SPhe(f(-`Yh=X6O7+6ALXnTcUFpI>ekl6v)ZQeNCg2 z^H|{SKXHU*%nBQ@I3It0m^h+6tvI@FS=MYS$ZpBaG7j#V@P2ZuYySbp@hA# ze(kc;P4i_-_UDP?%<6>%tTRih6VBgScKU^BV6Aoeg6Uh(W^#J^V$Xo^4#Ekp ztqQVK^g9gKMTHvV7nb64UU7p~!B?>Y0oFH5T7#BSW#YfSB@5PtE~#SCCg3p^o=NkMk$<8- z6PT*yIKGrvne7+y3}_!AC8NNeI?iTY(&nakN>>U-zT0wzZf-RuyZk^X9H-DT_*wk= z;&0}6LsGtfVa1q)CEUPlx#(ED@-?H<1_FrHU#z5^P3lEB|qsxEyn%FOpjx z3S?~gvoXy~L(Q{Jh6*i~=f%9kM1>RGjBzQh_SaIDfSU_9!<>*Pm>l)cJD@wlyxpBV z4Fmhc2q=R_wHCEK69<*wG%}mgD1=FHi4h!98B-*vMu4ZGW~%IrYSLGU{^TuseqVgV zLP<%wirIL`VLyJv9XG_p8w@Q4HzNt-o;U@Au{7%Ji;53!7V8Rv0^Lu^Vf*sL>R(;c zQG_ZuFl)Mh-xEIkGu}?_(HwkB2jS;HdPLSxVU&Jxy9*XRG~^HY(f0g8Q}iqnVmgjI zfd=``2&8GsycjR?M%(zMjn;tn9agcq;&rR!Hp z$B*gzHsQ~aXw8c|a(L^LW(|`yGc!qOnV(ZjU_Q-4z1&0;jG&vAKuNG=F|H?@m5^N@ zq{E!1n;)kNTJ>|Hb2ODt-7U~-MOIFo%9I)_@7fnX+eMMNh>)V$IXesJpBn|uo8f~#aOFytCT zf9&%MCLf8mp4kwHTcojWmM3LU=#|{3L>E}SKwOd?%{HogCZ_Z1BSA}P#O(%H$;z7XyJ^sjGX;j5 zrzp>|Ud;*&VAU3x#f{CKwY7Vc{%TKKqmB@oTHA9;>?!nvMA;8+Jh=cambHz#J18x~ zs!dF>$*AnsQ{{82r5Aw&^7eRCdvcgyxH?*DV5(I$qXh^zS>us*I66_MbL8y4d3ULj z{S(ipo+T3Ag!+5`NU2sc+@*m{_X|&p#O-SAqF&g_n7ObB82~$p%fXA5GLHMC+#qqL zdt`sJC&6C2)=juQ_!NeD>U8lDVpAOkW*khf7MCcs$A(wiIl#B9HM%~GtQ^}yBPjT@ z+E=|A!Z?A(rwzZ;T}o6pOVqHzTr*i;Wrc%&36kc@jXq~+w8kVrs;%=IFdACoLAcCAmhFNpbP8;s`zG|HC2Gv?I~w4ITy=g$`0qMQdkijLSOtX6xW%Z9Nw<;M- zMN`c7=$QxN00DiSjbVt9Mi6-pjv*j(_8PyV-il8Q-&TwBwH1gz1uoxs6~uU}PrgWB zIAE_I-a1EqlIaGQNbcp@iI8W1sm9fBBNOk(k&iLBe%MCo#?xI$%ZmGA?=)M9D=0t7 zc)Q0LnI)kCy{`jCGy9lYX%mUsDWwsY`;jE(;Us@gmWPqjmXL+Hu#^;k%eT>{nMtzj zsV`Iy6leTA8-PndszF;N^X@CJrTw5IIm!GPeu)H2#FQitR{1p;MasQVAG3*+=9FYK zw*k!HT(YQorfQj+1*mCV458(T5=fH`um$gS38hw(OqVMyunQ;rW5aPbF##A3fGH6h z@W)i9Uff?qz`YbK4c}JzQpuxuE3pcQO)%xBRZp{zJ^-*|oryTxJ-rR+MXJ)!f=+pp z10H|DdGd2exhi+hftcYbM0_}C0ZI-2vh+$fU1acsB-YXid7O|=9L!3e@$H*6?G*Zp z%qFB(sgl=FcC=E4CYGp4CN>=M8#5r!RU!u+FJVlH6=gI5xHVD&k;Ta*M28BsxfMV~ zLz+@6TxnfLhF@5=yQo^1&S}cmTN@m!7*c6z;}~*!hNBjuE>NLVl2EwN!F+)0$R1S! zR|lF%n!9fkZ@gPW|x|B={V6x3`=jS*$Pu0+5OWf?wnIy>Y1MbbGSncpKO0qE(qO=ts z!~@&!N`10S593pVQu4FzpOh!tvg}p%zCU(aV5=~K#bKi zHdJ1>tQSrhW%KOky;iW+O_n;`l9~omqM%sdxdLtI`TrJzN6BQz+7xOl*rM>xVI2~# z)7FJ^Dc{DC<%~VS?@WXzuOG$YPLC;>#vUJ^MmtbSL`_yXtNKa$Hk+l-c!aC7gn(Cg ze?YPYZ(2Jw{SF6MiO5(%_pTo7j@&DHNW`|lD`~{iH+_eSTS&OC*2WTT*a`?|9w1dh zh1nh@$a}T#WE5$7Od~NvSEU)T(W$p$s5fe^GpG+7fdJ9=enRT9$wEk+ZaB>G3$KQO zgq?-rZZnIv!p#>Ty~}c*Lb_jxJg$eGM*XwHUwuQ|o^}b3^T6Bxx{!?va8aC@-xK*H ztJBFvFfsSWu89%@b^l3-B~O!CXs)I6Y}y#0C0U0R0WG zybjroj$io0j}3%P7zADXOwHwafT#uu*zfM!oD$6aJx7+WL%t-@6^rD_a_M?S^>c;z zMK580bZXo1f*L$CuMeM4Mp!;P@}b~$cd(s5*q~FP+NHSq;nw3fbWyH)i2)-;gQl{S zZO!T}A}fC}vUdskGSq&{`oxt~0i?0xhr6I47_tBc`fqaSrMOzR4>0H^;A zF)hX1nfHs)%Zb-(YGX;=#2R6C{BG;k=?FfP?9{_uFLri~-~AJ;jw({4MU7e*d)?P@ zXX*GkNY9ItFjhwgAIWq7Y!ksbMzfqpG)IrqKx9q{zu%Mdl+{Dis#p9q`02pr1LG8R z@As?eG!>IoROgS!@J*to<27coFc1zpkh?w=)h9CbYe%^Q!Ui46Y*HO0mr% zEff-*$ndMNw}H2a5@BsGj5oFfd!T(F&0$<{GO!Qdd?McKkorh=5{EIjDTHU`So>8V zBA-fqVLb2;u7UhDV1xMI?y>fe3~4urv3%PX)lDw+HYa;HFkaLqi4c~VtCm&Ca+9C~ zge+67hp#R9`+Euq59WhHX&7~RlXn=--m8$iZ~~1C8cv^2(qO#X0?vl91gzUKBeR1J z^p4!!&7)3#@@X&2aF2-)1Ffcc^F8r|RtdL2X%HgN&XU-KH2SLCbpw?J5xJ*!F-ypZ zMG%AJ!Pr&}`LW?E!K~=(NJxuSVTRCGJ$2a*Ao=uUDSys!OFYu!Vs2IT;xQ6EubLIl z+?+nMGeQQhh~??0!s4iQ#gm3!BpMpnY?04kK375e((Uc7B3RMj;wE?BCoQGu=UlZt!EZ1Q*auI)dj3Jj{Ujgt zW5hd~-HWBLI_3HuO) zNrb^XzPsTIb=*a69wAAA3J6AAZZ1VsYbIG}a`=d6?PjM)3EPaDpW2YP$|GrBX{q*! z$KBHNif)OKMBCFP5>!1d=DK>8u+Upm-{hj5o|Wn$vh1&K!lVfDB&47lw$tJ?d5|=B z^(_9=(1T3Fte)z^>|3**n}mIX;mMN5v2F#l(q*CvU{Ga`@VMp#%rQkDBy7kYbmb-q z<5!4iuB#Q_lLZ8}h|hPODI^U6`gzLJre9u3k3c#%86IKI*^H-@I48Bi*@avYm4v!n0+v zWu{M{&F8#p9cx+gF0yTB_<2QUrjMPo9*7^-uP#~gGW~y3nfPAoV%amgr>PSyVAd@l)}8#X zR5zV6t*uKJZL}?NYvPVK6J0v4iVpwiN|>+t3aYiZSp;m0!(1`bHO}TEtWR1tY%BPB z(W!0DmXbZAsT$iC13p4f>u*ZAy@JoLAkJhzFf1#4;#1deO8#8d&89}en&z!W&A3++^1(;>0SB1*54d@y&9Pn;^IAf3GiXbfT`_>{R+Xv; zQvgL>+0#8-laO!j#-WB~(I>l0NCMt_;@Gp_f0#^c)t?&#Xh1-7RR0@zPyBz!U#0Av zT?}n({(p?p7!4S2ZBw)#KdCG)uPnZe+U|0{BW!m)9 zi_9$F?m<`2!`JNFv+w8MK_K)qJ^aO@7-Ig>cM4-r0bi=>?B_2mFNJ}aE3<+QCzRr*NA!QjHw# z`1OsvcoD0?%jq{*7b!l|L1+Tw0TTAM4XMq7*ntc-Ived>Sj_ZtS|uVdpfg1_I9knY z2{GM_j5sDC7(W&}#s{jqbybqJWyn?{PW*&cQIU|*v8YGOKKlGl@?c#TCnmnAkAzV- zmK={|1G90zz=YUvC}+fMqts0d4vgA%t6Jhjv?d;(Z}(Ep8fTZfHA9``fdUHkA+z3+ zhh{ohP%Bj?T~{i0sYCQ}uC#5BwN`skI7`|c%kqkyWIQ;!ysvA8H`b-t()n6>GJj6xlYDu~8qX{AFo$Cm3d|XFL=4uvc?Keb zzb0ZmMoXca6Mob>JqkNuoP>B2Z>D`Q(TvrG6m`j}-1rGP!g|qoL=$FVQYxJQjFn33lODt3Wb1j8VR zlR++vIT6^DtYxAv_hxupbLLN3e0%A%a+hWTKDV3!Fjr^cWJ{scsAdfhpI)`Bms^M6 zQG$waKgFr=c|p9Piug=fcJvZ1ThMnNhQvBAg-8~b1?6wL*WyqXhtj^g(Ke}mEfZVM zJuLNTUVh#WsE*a6uqiz`b#9ZYg3+2%=C(6AvZGc=u&<6??!slB1a9K)=VL zY9EL^mfyKnD zSJyYBc_>G;5RRnrNgzJz#Rkn3S1`mZgO`(r5;Hw6MveN(URf_XS-r58Cn80K)ArH4 z#Rrd~LG1W&@ttw85cjp8xV&>$b%nSXH_*W}7Ch2pg$$c0BdEo-HWRTZcxngIBJad> z;C>b{jIXjb_9Jis?NZJsdm^EG}e*pR&DAy0EaSGi3XWTa(>C%tz1n$u?5Fb z1qtl?;_yjYo)(gB^iQq?=jusF%kywm?CJP~zEHi0NbZ);$(H$w(Hy@{i>$wcVRD_X|w-~(0Z9BJyh zhNh;+eQ9BEIs;tPz%jSVnfCP!3L&9YtEP;svoj_bNzeGSQIAjd zBss@A;)R^WAu-37RQrM%{DfBNRx>v!G31Z}8-El9IOJlb_MSoMu2}GDYycNaf>uny z+8xykD-7ONCM!APry_Lw6-yT>5!tR}W;W`C)1>pxSs5o1z#j7%m=&=7O4hz+Lsqm` z*>{+xsabZPr&X=}G@obTb{nPTkccJX8w3CG7X+1+t{JcMabv~UNv+G?txRqXib~c^Mo}`q{$`;EBNJ;#F*{gvS12kV?AZ%O0SFB$^ zn+}!HbmEj}w{Vq(G)OGAzH}R~kS^;(-s&=ectz8vN!_)Yl$$U@HNTI-pV`LSj7Opu zTZ5zZ)-S_{GcEQPIQXLQ#oMS`HPu{`SQiAZ)m1at*Hy%3xma|>o`h%E%8BEbi9p0r zVjcsh<{NBKQ4eKlXU|}@XJ#@uQw*$4BxKn6#W~I4T<^f99~(=}a`&3(ur8R9t+|AQ zWkQx7l}wa48-jO@ft2h+7qn%SJtL%~890FG0s5g*kNbL3I&@brh&f6)TlM`K^(bhr zJWM6N6x3flOw$@|C@kPi7yP&SP?bzP-E|HSXQXG>7gk|R9BTj`e=4de9C6+H7H7n# z#GJeVs1mtHhLDmVO?LkYRQc`DVOJ_vdl8VUihO-j#t=0T3%Fc1f9F73ufJz*adn*p zc%&vi(4NqHu^R>sAT_0EDjVR8bc%wTz#$;%NU-kbDyL_dg0%TFafZwZ?5KZpcuaO54Z9hX zD$u>q!-9`U6-D`E#`W~fIfiIF5_m6{fvM)b1NG3xf4Auw;Go~Fu7cth#DlUn{@~yu z=B;RT*dp?bO}o%4x7k9v{r=Y@^YQ^UUm(Qmliw8brO^=NP+UOohLYiaEB3^DB56&V zK?4jV61B|1Uj_5fBKW;8LdwOFZKWp)g{B%7g1~DgO&N& z#lisxf?R~Z@?3E$Mms$$JK8oe@X`5m98V*aV6Ua}8Xs2#A!{x?IP|N(%nxsH?^c{& z@vY&R1QmQs83BW28qAmJfS7MYi=h(YK??@EhjL-t*5W!p z^gYX!Q6-vBqcv~ruw@oMaU&qp0Fb(dbVzm5xJN%0o_^@fWq$oa3X?9s%+b)x4w-q5Koe(@j6Ez7V@~NRFvd zfBH~)U5!ix3isg`6be__wBJp=1@yfsCMw1C@y+9WYD9_C%{Q~7^0AF2KFryfLlUP# zwrtJEcH)jm48!6tUcxiurAMaiD04C&tPe6DI0#aoqz#Bt0_7_*X*TsF7u*zv(iEfA z;$@?XVu~oX#1YXtceQL{dSneL&*nDug^OW$DSLF0M1Im|sSX8R26&)<0Fbh^*l6!5wfSu8MpMoh=2l z^^0Sr$UpZp*9oqa23fcCfm7`ya2<4wzJ`Axt7e4jJrRFVf?nY~2&tRL* zd;6_njcz01c>$IvN=?K}9ie%Z(BO@JG2J}fT#BJQ+f5LFSgup7i!xWRKw6)iITjZU z%l6hPZia>R!`aZjwCp}I zg)%20;}f+&@t;(%5;RHL>K_&7MH^S+7<|(SZH!u zznW|jz$uA`P9@ZWtJgv$EFp>)K&Gt+4C6#*khZQXS*S~6N%JDT$r`aJDs9|uXWdbg zBwho$phWx}x!qy8&}6y5Vr$G{yGSE*r$^r{}pw zVTZKvikRZ`J_IJrjc=X1uw?estdwm&bEahku&D04HD+0Bm~q#YGS6gp!KLf$A{%Qd z&&yX@Hp>~(wU{|(#U&Bf92+1i&Q*-S+=y=3pSZy$#8Uc$#7oiJUuO{cE6=tsPhwPe| zxQpK>`Dbka`V)$}e6_OXKLB%i76~4N*zA?X+PrhH<&)}prET;kel24kW%+9))G^JI zsq7L{P}^#QsZViX%KgxBvEugr>ZmFqe^oAg?{EI=&_O#e)F3V#rc z8$4}0Zr19qd3tE4#$3_f=Bbx9oV6VO!d3(R===i-7p=Vj`520w0D3W6lQfY48}!D* z&)lZMG;~er2qBoI2gsX+Ts-hnpS~NYRDtPd^FPzn!^&yxRy#CSz(b&E*tL|jIkq|l zf%>)7Dtu>jCf`-7R#*GhGn4FkYf;B$+9IxmqH|lf6$4irg{0ept__%)V*R_OK=T06 zyT_m-o@Kp6U{l5h>W1hGq*X#8*y@<;vsOFqEjTQXFEotR+{3}ODDnj;o0@!bB5x=N z394FojuGOtVKBlVRLtHp%EJv_G5q=AgF)SKyRN5=cGBjDWv4LDn$IL`*=~J7u&Dy5 zrMc83y+w^F&{?X(KOOAl-sWZDb{9X9#jrQtmrEXD?;h-}SYT7yM(X_6qksM=K_a;Z z3u0qT0TtaNvDER_8x*rxXw&C^|h{P1qxK|@pS7vdlZ#P z7PdB7MmC2}%sdzAxt>;WM1s0??`1983O4nFK|hVAbHcZ3x{PzytQLkCVk7hA!Lo` zEJH?4qw|}WH{dc4z%aB=0XqsFW?^p=X}4xnCJXK%c#ItOSjdSO`UXJyuc8bh^Cf}8 z@Ht|vXd^6{Fgai8*tmyRGmD_s_nv~r^Fy7j`Bu`6=G)5H$i7Q7lvQnmea&TGvJp9a|qOrUymZ$6G|Ly z#zOCg++$3iB$!6!>215A4!iryregKuUT344X)jQb3|9qY>c0LO{6Vby05n~VFzd?q zgGZv&FGlkiH*`fTurp>B8v&nSxNz)=5IF$=@rgND4d`!AaaX;_lK~)-U8la_Wa8i?NJC@BURO*sUW)E9oyv3RG^YGfN%BmxzjlT)bp*$<| zX3tt?EAy<&K+bhIuMs-g#=d1}N_?isY)6Ay$mDOKRh z4v1asEGWoAp=srraLW^h&_Uw|6O+r;wns=uwYm=JN4Q!quD8SQRSeEcGh|Eb5Jg8m zOT}u;N|x@aq)=&;wufCc^#)5U^VcZw;d_wwaoh9$p@Xrc{DD6GZUqZ ziC6OT^zSq@-lhbgR8B+e;7_Giv;DK5gn^$bs<6~SUadiosfewWDJu`XsBfOd1|p=q zE>m=zF}!lObA%ePey~gqU8S6h-^J2Y?>7)L2+%8kV}Gp=h`Xm_}rlm)SyUS=`=S7msKu zC|T!gPiI1rWGb1z$Md?0YJQ;%>uPLOXf1Z>N~`~JHJ!^@D5kSXQ4ugnFZ>^`zH8CAiZmp z6Ms|#2gcGsQ{{u7+Nb9sA?U>(0e$5V1|WVwY`Kn)rsnnZ4=1u=7u!4WexZD^IQ1Jk zfF#NLe>W$3m&C^ULjdw+5|)-BSHwpegdyt9NYC{3@QtMfd8GrIWDu`gd0nv-3LpGCh@wgBaG z176tikL!_NXM+Bv#7q^cyn9$XSeZR6#!B4JE@GVH zoobHZN_*RF#@_SVYKkQ_igme-Y5U}cV(hkR#k1c{bQNMji zU7aE`?dHyx=1`kOYZo_8U7?3-7vHOp`Qe%Z*i+FX!s?6huNp0iCEW-Z7E&jRWmUW_ z67j>)Ew!yq)hhG4o?^z}HWH-e=es#xJUhDRc4B51M4~E-l5VZ!&zQq`gWe`?}#b~7w1LH4Xa-UCT5LXkXQWheBa2YJYbyQ zl1pXR%b(KCXMO0OsXgl0P0Og<{(@&z1aokU-Pq`eQq*JYgt8xdFQ6S z6Z3IFSua8W&M#`~*L#r>Jfd6*BzJ?JFdBR#bDv$_0N!_5vnmo@!>vULcDm`MFU823 zpG9pqjqz^FE5zMDoGqhs5OMmC{Y3iVcl>F}5Rs24Y5B^mYQ;1T&ks@pIApHOdrzXF z-SdX}Hf{X;TaSxG_T$0~#RhqKISGKNK47}0*x&nRIPtmdwxc&QT3$8&!3fWu1eZ_P zJveQj^hJL#Sn!*4k`3}(d(aasl&7G0j0-*_2xtAnoX1@9+h zO#c>YQg60Z;o{Bi=3i7S`Ic+ZE>K{(u|#)9y}q*j8uKQ1^>+(BI}m%1v3$=4ojGBc zm+o1*!T&b}-lVvZqIUBc8V}QyFEgm#oyIuC{8WqUNV{Toz`oxhYpP!_p2oHHh5P@iB*NVo~2=GQm+8Yrkm2Xjc_VyHg1c0>+o~@>*Qzo zHVBJS>$$}$_4EniTI;b1WShX<5-p#TPB&!;lP!lBVBbLOOxh6FuYloD%m;n{r|;MU3!q4AVkua~fieeWu2 zQAQ$ue(IklX6+V;F1vCu-&V?I3d42FgWgsb_e^29ol}HYft?{SLf>DrmOp9o!t>I^ zY7fBCk+E8n_|apgM|-;^=#B?6RnFKlN`oR)`e$+;D=yO-(U^jV;rft^G_zl`n7qnM zL z*-Y4Phq+ZI1$j$F-f;`CD#|`-T~OM5Q>x}a>B~Gb3-+9i>Lfr|Ca6S^8g*{*?_5!x zH_N!SoRP=gX1?)q%>QTY!r77e2j9W(I!uAz{T`NdNmPBBUzi2{`XMB^zJGGwFWeA9 z{fk33#*9SO0)DjROug+(M)I-pKA!CX;IY(#gE!UxXVsa)X!UftIN98{pt#4MJHOhY zM$_l}-TJlxY?LS6Nuz1T<44m<4i^8k@D$zuCPrkmz@sdv+{ciyFJG2Zwy&%c7;atIeTdh!a(R^QXnu1Oq1b42*OQFWnyQ zWeQrdvP|w_idy53Wa<{QH^lFmEd+VlJkyiC>6B#s)F;w-{c;aKIm;Kp50HnA-o3lY z9B~F$gJ@yYE#g#X&3ADx&tO+P_@mnQTz9gv30_sTsaGXkfNYXY{$(>*PEN3QL>I!k zp)KibPhrfX3%Z$H6SY`rXGYS~143wZrG2;=FLj50+VM6soI~up_>fU(2Wl@{BRsMi zO%sL3x?2l1cXTF)k&moNsHfQrQ+wu(gBt{sk#CU=UhrvJIncy@tJX5klLjgMn>~h= zg|FR&;@eh|C7`>s_9c~0-{IAPV){l|Ts`i=)AW;d9&KPc3fMeoTS%8@V~D8*h;&(^>yjT84MM}=%#LS7shLAuuj(0VAYoozhWjq z4LEr?wUe2^WGwdTIgWBkDUJa>YP@5d9^Rs$kCXmMRxuF*YMVrn?0NFyPl}>`&dqZb z<5eqR=ZG3>n2{6v6BvJ`YBZeeTtB88TAY(x0a58EWyuf>+^|x8Qa6wA|1Nb_p|nA zWWa}|z8a)--Wj`LqyFk_a3gN2>5{Rl_wbW?#by7&i*^hRknK%jwIH6=dQ8*-_{*x0j^DUfMX0`|K@6C<|1cgZ~D(e5vBFFm;HTZF(!vT8=T$K+|F)x3kqzBV4-=p1V(lzi(s7jdu0>LD#N=$Lk#3HkG!a zIF<7>%B7sRNzJ66KrFV76J<2bdYhxll0y2^_rdG=I%AgW4~)1Nvz=$1UkE^J%BxLo z+lUci`UcU062os*=`-j4IfSQA{w@y|3}Vk?i;&SSdh8n+$iHA#%ERL{;EpXl6u&8@ zzg}?hkEOUOJt?ZL=pWZFJ19mI1@P=$U5*Im1e_8Z${JsM>Ov?nh8Z zP5QvI!{Jy@&BP48%P2{Jr_VgzW;P@7)M9n|lDT|Ep#}7C$&ud&6>C^5ZiwKIg2McPU(4jhM!BD@@L(Gd*Nu$ji(ljZ<{FIeW_1Mmf;76{LU z-ywN~=uNN)Xi6$<12A9y)K%X|(W0p|&>>4OXB?IiYr||WKDOJPxiSe01NSV-h24^L z_>m$;|C+q!Mj**-qQ$L-*++en(g|hw;M!^%_h-iDjFHLo-n3JpB;p?+o2;`*jpvJU zLY^lt)Un4joij^^)O(CKs@7E%*!w>!HA4Q?0}oBJ7Nr8NQ7QmY^4~jvf0-`%waOLn zdNjAPaC0_7c|RVhw)+71NWjRi!y>C+Bl;Z`NiL^zn2*0kmj5gyhCLCxts*cWCdRI| zjsd=sT5BVJc^$GxP~YF$-U{-?kW6r@^vHXB%{CqYzU@1>dzf#3SYedJG-Rm6^RB7s zGM5PR(yKPKR)>?~vpUIeTP7A1sc8-knnJk*9)3t^e%izbdm>Y=W{$wm(cy1RB-19i za#828DMBY+ps#7Y8^6t)=Ea@%Nkt)O6JCx|ybC;Ap}Z@Zw~*}3P>MZLPb4Enxz9Wf zssobT^(R@KuShj8>@!1M7tm|2%-pYYDxz-5`rCbaTCG5{;Uxm z*g=+H1X8{NUvFGzz~wXa%Eo};I;~`37*WrRU&K0dPSB$yk(Z*@K&+mFal^?c zurbqB-+|Kb5|sznT;?Pj!+kgFY1#Dr;_%A(GIQC{3ct|{*Bji%FNa6c-thbpBkA;U zURV!Dr&X{0J}iht#-Qp2=xzuh(fM>zRoiGrYl5ttw2#r34gC41CCOC31m~^UPTK@s z6;A@)7O7_%C)>bnAXerYuAHdE93>j2N}H${zEc6&SbZ|-fiG*-qtGuy-qDelH(|u$ zorf8_T6Zqe#Ub!+e3oSyrskt_HyW_^5lrWt#30l)tHk|j$@YyEkXUOV;6B51L;M@=NIWZXU;GrAa(LGxO%|im%7F<-6N;en0Cr zLH>l*y?pMwt`1*cH~LdBPFY_l;~`N!Clyfr;7w<^X;&(ZiVdF1S5e(+Q%60zgh)s4 zn2yj$+mE=miVERP(g8}G4<85^-5f@qxh2ec?n+$A_`?qN=iyT1?U@t?V6DM~BIlBB z>u~eXm-aE>R0sQy!-I4xtCNi!!qh?R1!kKf6BoH2GG{L4%PAz0{Sh6xpuyI%*~u)s z%rLuFl)uQUCBQAtMyN;%)zFMx4loh7uTfKeB2Xif`lN?2gq6NhWhfz0u5WP9J>=V2 zo{mLtSy&BA!mSzs&CrKWq^y40JF5a&GSXIi2= z{EYb59J4}VwikL4P=>+mc6{($FNE@e=VUwG+KV21;<@lrN`mnz5jYGASyvz7BOG_6(p^eTxD-4O#lROgon;R35=|nj#eHIfJBYPWG>H>`dHKCDZ3`R{-?HO0mE~(5_WYcFmp8sU?wr*UkAQiNDGc6T zA%}GOLXlOWqL?WwfHO8MB#8M8*~Y*gz;1rWWoVSXP&IbKxbQ8+s%4Jnt?kDsq7btI zCDr0PZ)b;B%!lu&CT#RJzm{l{2fq|BcY85`w~3LSK<><@(2EdzFLt9Y_`;WXL6x`0 zDoQ?=?I@Hbr;*VVll1Gmd8*%tiXggMK81a+T(5Gx6;eNb8=uYn z5BG-0g>pP21NPn>$ntBh>`*})Fl|38oC^9Qz>~MAazH%3Q~Qb!ALMf$srexgPZ2@&c~+hxRi1;}+)-06)!#Mq<6GhP z-Q?qmgo${aFBApb5p}$1OJKTClfi8%PpnczyVKkoHw7Ml9e7ikrF0d~UB}i3vizos zXW4DN$SiEV9{faLt5bHy2a>33K%7Td-n5C*N;f&ZqAg#2hIqEb(y<&f4u5BWJ>2^4 z414GosL=Aom#m&=x_v<0-fp1r%oVJ{T-(xnomNJ(Dryv zh?vj+%=II_nV+@NR+(!fZZVM&(W6{6%9cm+o+Z6}KqzLw{(>E86uA1`_K$HqINlb1 zKelh3-jr2I9V?ych`{hta9wQ2c9=MM`2cC{m6^MhlL2{DLv7C^j z$xXBCnDl_;l|bPGMX@*tV)B!c|4oZyftUlP*?$YU9C_eAsuVHJ58?)zpbr30P*C`T z7y#ao`uE-SOG(Pi+`$=e^mle~)pRrdwL5)N;o{gpW21of(QE#U6w%*C~`v-z0QqBML!!5EeYA5IQB0 z^l01c;L6E(iytN!LhL}wfwP7W9PNAkb+)Cst?qg#$n;z41O4&v+8-zPs+XNb-q zIeeBCh#ivnFLUCwfS;p{LC0O7tm+Sf9Jn)~b%uwP{%69;QC)Ok0t%*a5M+=;y8j=v z#!*pp$9@!x;UMIs4~hP#pnfVc!%-D<+wsG@R2+J&%73lK|2G!EQC)O05TCV=&3g)C!lT=czLpZ@Sa%TYuoE?v8T8`V;e$#Zf2_Nj6nvBgh1)2 GZ~q4|mN%#X literal 59821 zcma&NV|1p`(k7gaZQHhOJ9%QKV?D8LCmq{1JGRYE(y=?XJw0>InKkE~^UnAEs2gk5 zUVGPCwX3dOb!}xiFmPB95NK!+5D<~S0s;d1zn&lrfAn7 zC?Nb-LFlib|DTEqB8oDS5&$(u1<5;wsY!V`2F7^=IR@I9so5q~=3i_(hqqG<9SbL8Q(LqDrz+aNtGYWGJ2;p*{a-^;C>BfGzkz_@fPsK8{pTT~_VzB$E`P@> z7+V1WF2+tSW=`ZRj3&0m&d#x_lfXq`bb-Y-SC-O{dkN2EVM7@!n|{s+2=xSEMtW7( zz~A!cBpDMpQu{FP=y;sO4Le}Z)I$wuFwpugEY3vEGfVAHGqZ-<{vaMv-5_^uO%a{n zE_Zw46^M|0*dZ`;t%^3C19hr=8FvVdDp1>SY>KvG!UfD`O_@weQH~;~W=fXK_!Yc> z`EY^PDJ&C&7LC;CgQJeXH2 zjfM}2(1i5Syj)Jj4EaRyiIl#@&lC5xD{8hS4Wko7>J)6AYPC-(ROpVE-;|Z&u(o=X z2j!*>XJ|>Lo+8T?PQm;SH_St1wxQPz)b)Z^C(KDEN$|-6{A>P7r4J1R-=R7|FX*@! zmA{Ja?XE;AvisJy6;cr9Q5ovphdXR{gE_7EF`ji;n|RokAJ30Zo5;|v!xtJr+}qbW zY!NI6_Wk#6pWFX~t$rAUWi?bAOv-oL6N#1>C~S|7_e4 zF}b9(&a*gHk+4@J26&xpiWYf2HN>P;4p|TD4f586umA2t@cO1=Fx+qd@1Ae#Le>{-?m!PnbuF->g3u)7(n^llJfVI%Q2rMvetfV5 z6g|sGf}pV)3_`$QiKQnqQ<&ghOWz4_{`rA1+7*M0X{y(+?$|{n zs;FEW>YzUWg{sO*+D2l6&qd+$JJP_1Tm;To<@ZE%5iug8vCN3yH{!6u5Hm=#3HJ6J zmS(4nG@PI^7l6AW+cWAo9sFmE`VRcM`sP7X$^vQY(NBqBYU8B|n-PrZdNv8?K?kUTT3|IE`-A8V*eEM2=u*kDhhKsmVPWGns z8QvBk=BPjvu!QLtlF0qW(k+4i+?H&L*qf262G#fks9}D5-L{yiaD10~a;-j!p!>5K zl@Lh+(9D{ePo_S4F&QXv|q_yT`GIPEWNHDD8KEcF*2DdZD;=J6u z|8ICSoT~5Wd!>g%2ovFh`!lTZhAwpIbtchDc{$N%<~e$E<7GWsD42UdJh1fD($89f2on`W`9XZJmr*7lRjAA8K0!(t8-u>2H*xn5cy1EG{J;w;Q-H8Yyx+WW(qoZZM7p(KQx^2-yI6Sw?k<=lVOVwYn zY*eDm%~=|`c{tUupZ^oNwIr!o9T;H3Fr|>NE#By8SvHb&#;cyBmY1LwdXqZwi;qn8 zK+&z{{95(SOPXAl%EdJ3jC5yV^|^}nOT@M0)|$iOcq8G{#*OH7=DlfOb; z#tRO#tcrc*yQB5!{l5AF3(U4>e}nEvkoE_XCX=a3&A6Atwnr&`r&f2d%lDr8f?hBB zr1dKNypE$CFbT9I?n){q<1zHmY>C=5>9_phi79pLJG)f=#dKdQ7We8emMjwR*qIMF zE_P-T*$hX#FUa%bjv4Vm=;oxxv`B*`weqUn}K=^TXjJG=UxdFMSj-QV6fu~;- z|IsUq`#|73M%Yn;VHJUbt<0UHRzbaF{X@76=8*-IRx~bYgSf*H(t?KH=?D@wk*E{| z2@U%jKlmf~C^YxD=|&H?(g~R9-jzEb^y|N5d`p#2-@?BUcHys({pUz4Zto7XwKq2X zSB~|KQGgv_Mh@M!*{nl~2~VV_te&E7K39|WYH zCxfd|v_4!h$Ps2@atm+gj14Ru)DhivY&(e_`eA)!O1>nkGq|F-#-6oo5|XKEfF4hR z%{U%ar7Z8~B!foCd_VRHr;Z1c0Et~y8>ZyVVo9>LLi(qb^bxVkbq-Jq9IF7!FT`(- zTMrf6I*|SIznJLRtlP)_7tQ>J`Um>@pP=TSfaPB(bto$G1C zx#z0$=zNpP-~R);kM4O)9Mqn@5Myv5MmmXOJln312kq#_94)bpSd%fcEo7cD#&|<` zrcal$(1Xv(nDEquG#`{&9Ci~W)-zd_HbH-@2F6+|a4v}P!w!Q*h$#Zu+EcZeY>u&?hn#DCfC zVuye5@Ygr+T)0O2R1*Hvlt>%rez)P2wS}N-i{~IQItGZkp&aeY^;>^m7JT|O^{`78 z$KaK0quwcajja;LU%N|{`2o&QH@u%jtH+j!haGj;*ZCR*`UgOXWE>qpXqHc?g&vA& zt-?_g8k%ZS|D;()0Lf!>7KzTSo-8hUh%OA~i76HKRLudaNiwo*E9HxmzN4y>YpZNO zUE%Q|H_R_UmX=*f=2g=xyP)l-DP}kB@PX|(Ye$NOGN{h+fI6HVw`~Cd0cKqO;s6aiYLy7sl~%gs`~XaL z^KrZ9QeRA{O*#iNmB7_P!=*^pZiJ5O@iE&X2UmUCPz!)`2G3)5;H?d~3#P|)O(OQ_ zua+ZzwWGkWflk4j^Lb=x56M75_p9M*Q50#(+!aT01y80x#rs9##!;b-BH?2Fu&vx} za%4!~GAEDsB54X9wCF~juV@aU}fp_(a<`Ig0Pip8IjpRe#BR?-niYcz@jI+QY zBU9!8dAfq@%p;FX)X=E7?B=qJJNXlJ&7FBsz;4&|*z{^kEE!XbA)(G_O6I9GVzMAF z8)+Un(6od`W7O!!M=0Z)AJuNyN8q>jNaOdC-zAZ31$Iq%{c_SYZe+(~_R`a@ zOFiE*&*o5XG;~UjsuW*ja-0}}rJdd@^VnQD!z2O~+k-OSF%?hqcFPa4e{mV1UOY#J zTf!PM=KMNAzbf(+|AL%K~$ahX0Ol zbAxKu3;v#P{Qia{_WzHl`!@!8c#62XSegM{tW1nu?Ee{sQq(t{0TSq67YfG;KrZ$n z*$S-+R2G?aa*6kRiTvVxqgUhJ{ASSgtepG3hb<3hlM|r>Hr~v_DQ>|Nc%&)r0A9go z&F3Ao!PWKVq~aWOzLQIy&R*xo>}{UTr}?`)KS&2$3NR@a+>+hqK*6r6Uu-H};ZG^| zfq_Vl%YE1*uGwtJ>H*Y(Q9E6kOfLJRlrDNv`N;jnag&f<4#UErM0ECf$8DASxMFF& zK=mZgu)xBz6lXJ~WZR7OYw;4&?v3Kk-QTs;v1r%XhgzSWVf|`Sre2XGdJb}l1!a~z zP92YjnfI7OnF@4~g*LF>G9IZ5c+tifpcm6#m)+BmnZ1kz+pM8iUhwag`_gqr(bnpy zl-noA2L@2+?*7`ZO{P7&UL~ahldjl`r3=HIdo~Hq#d+&Q;)LHZ4&5zuDNug@9-uk; z<2&m#0Um`s=B}_}9s&70Tv_~Va@WJ$n~s`7tVxi^s&_nPI0`QX=JnItlOu*Tn;T@> zXsVNAHd&K?*u~a@u8MWX17VaWuE0=6B93P2IQ{S$-WmT+Yp!9eA>@n~=s>?uDQ4*X zC(SxlKap@0R^z1p9C(VKM>nX8-|84nvIQJ-;9ei0qs{}X>?f%&E#%-)Bpv_p;s4R+ z;PMpG5*rvN&l;i{^~&wKnEhT!S!LQ>udPzta#Hc9)S8EUHK=%x+z@iq!O{)*XM}aI zBJE)vokFFXTeG<2Pq}5Na+kKnu?Ch|YoxdPb&Z{07nq!yzj0=xjzZj@3XvwLF0}Pa zn;x^HW504NNfLY~w!}5>`z=e{nzGB>t4ntE>R}r7*hJF3OoEx}&6LvZz4``m{AZxC zz6V+^73YbuY>6i9ulu)2`ozP(XBY5n$!kiAE_Vf4}Ih)tlOjgF3HW|DF+q-jI_0p%6Voc^e;g28* z;Sr4X{n(X7eEnACWRGNsHqQ_OfWhAHwnSQ87@PvPcpa!xr9`9+{QRn;bh^jgO8q@v zLekO@-cdc&eOKsvXs-eMCH8Y{*~3Iy!+CANy+(WXYS&6XB$&1+tB?!qcL@@) zS7XQ|5=o1fr8yM7r1AyAD~c@Mo`^i~hjx{N17%pDX?j@2bdBEbxY}YZxz!h#)q^1x zpc_RnoC3`V?L|G2R1QbR6pI{Am?yW?4Gy`G-xBYfebXvZ=(nTD7u?OEw>;vQICdPJBmi~;xhVV zisVvnE!bxI5|@IIlDRolo_^tc1{m)XTbIX^<{TQfsUA1Wv(KjJED^nj`r!JjEA%MaEGqPB z9YVt~ol3%e`PaqjZt&-)Fl^NeGmZ)nbL;92cOeLM2H*r-zA@d->H5T_8_;Jut0Q_G zBM2((-VHy2&eNkztIpHk&1H3M3@&wvvU9+$RO%fSEa_d5-qZ!<`-5?L9lQ1@AEpo* z3}Zz~R6&^i9KfRM8WGc6fTFD%PGdruE}`X$tP_*A)_7(uI5{k|LYc-WY*%GJ6JMmw zNBT%^E#IhekpA(i zcB$!EB}#>{^=G%rQ~2;gbObT9PQ{~aVx_W6?(j@)S$&Ja1s}aLT%A*mP}NiG5G93- z_DaRGP77PzLv0s32{UFm##C2LsU!w{vHdKTM1X)}W%OyZ&{3d^2Zu-zw?fT=+zi*q z^fu6CXQ!i?=ljsqSUzw>g#PMk>(^#ejrYp(C)7+@Z1=Mw$Rw!l8c9}+$Uz;9NUO(kCd#A1DX4Lbis0k; z?~pO(;@I6Ajp}PL;&`3+;OVkr3A^dQ(j?`by@A!qQam@_5(w6fG>PvhO`#P(y~2ue zW1BH_GqUY&>PggMhhi@8kAY;XWmj>y1M@c`0v+l~l0&~Kd8ZSg5#46wTLPo*Aom-5 z>qRXyWl}Yda=e@hJ%`x=?I42(B0lRiR~w>n6p8SHN~B6Y>W(MOxLpv>aB)E<1oEcw z%X;#DJpeDaD;CJRLX%u!t23F|cv0ZaE183LXxMq*uWn)cD_ zp!@i5zsmcxb!5uhp^@>U;K>$B|8U@3$65CmhuLlZ2(lF#hHq-<<+7ZN9m3-hFAPgA zKi;jMBa*59ficc#TRbH_l`2r>z(Bm_XEY}rAwyp~c8L>{A<0@Q)j*uXns^q5z~>KI z)43=nMhcU1ZaF;CaBo>hl6;@(2#9yXZ7_BwS4u>gN%SBS<;j{{+p}tbD8y_DFu1#0 zx)h&?`_`=ti_6L>VDH3>PPAc@?wg=Omdoip5j-2{$T;E9m)o2noyFW$5dXb{9CZ?c z);zf3U526r3Fl+{82!z)aHkZV6GM@%OKJB5mS~JcDjieFaVn}}M5rtPnHQVw0Stn- zEHs_gqfT8(0b-5ZCk1%1{QQaY3%b>wU z7lyE?lYGuPmB6jnMI6s$1uxN{Tf_n7H~nKu+h7=%60WK-C&kEIq_d4`wU(*~rJsW< zo^D$-(b0~uNVgC+$J3MUK)(>6*k?92mLgpod{Pd?{os+yHr&t+9ZgM*9;dCQBzE!V zk6e6)9U6Bq$^_`E1xd}d;5O8^6?@bK>QB&7l{vAy^P6FOEO^l7wK4K=lLA45gQ3$X z=$N{GR1{cxO)j;ZxKI*1kZIT9p>%FhoFbRK;M(m&bL?SaN zzkZS9xMf={o@gpG%wE857u@9dq>UKvbaM1SNtMA9EFOp7$BjJQVkIm$wU?-yOOs{i z1^(E(WwZZG{_#aIzfpGc@g5-AtK^?Q&vY#CtVpfLbW?g0{BEX4Vlk(`AO1{-D@31J zce}#=$?Gq+FZG-SD^z)-;wQg9`qEO}Dvo+S9*PUB*JcU)@S;UVIpN7rOqXmEIerWo zP_lk!@RQvyds&zF$Rt>N#_=!?5{XI`Dbo0<@>fIVgcU*9Y+ z)}K(Y&fdgve3ruT{WCNs$XtParmvV;rjr&R(V&_#?ob1LzO0RW3?8_kSw)bjom#0; zeNllfz(HlOJw012B}rgCUF5o|Xp#HLC~of%lg+!pr(g^n;wCX@Yk~SQOss!j9f(KL zDiI1h#k{po=Irl)8N*KU*6*n)A8&i9Wf#7;HUR^5*6+Bzh;I*1cICa|`&`e{pgrdc zs}ita0AXb$c6{tu&hxmT0faMG0GFc)unG8tssRJd%&?^62!_h_kn^HU_kBgp$bSew zqu)M3jTn;)tipv9Wt4Ll#1bmO2n?^)t^ZPxjveoOuK89$oy4(8Ujw{nd*Rs*<+xFi z{k*9v%sl?wS{aBSMMWdazhs0#gX9Has=pi?DhG&_0|cIyRG7c`OBiVG6W#JjYf7-n zIQU*Jc+SYnI8oG^Q8So9SP_-w;Y00$p5+LZ{l+81>v7|qa#Cn->312n=YQd$PaVz8 zL*s?ZU*t-RxoR~4I7e^c!8TA4g>w@R5F4JnEWJpy>|m5la2b#F4d*uoz!m=i1;`L` zB(f>1fAd~;*wf%GEbE8`EA>IO9o6TdgbIC%+en!}(C5PGYqS0{pa?PD)5?ds=j9{w za9^@WBXMZ|D&(yfc~)tnrDd#*;u;0?8=lh4%b-lFPR3ItwVJp};HMdEw#SXg>f-zU zEiaj5H=jzRSy(sWVd%hnLZE{SUj~$xk&TfheSch#23)YTcjrB+IVe0jJqsdz__n{- zC~7L`DG}-Dgrinzf7Jr)e&^tdQ}8v7F+~eF*<`~Vph=MIB|YxNEtLo1jXt#9#UG5` zQ$OSk`u!US+Z!=>dGL>%i#uV<5*F?pivBH@@1idFrzVAzttp5~>Y?D0LV;8Yv`wAa{hewVjlhhBM z_mJhU9yWz9Jexg@G~dq6EW5^nDXe(sU^5{}qbd0*yW2Xq6G37f8{{X&Z>G~dUGDFu zgmsDDZZ5ZmtiBw58CERFPrEG>*)*`_B75!MDsOoK`T1aJ4GZ1avI?Z3OX|Hg?P(xy zSPgO$alKZuXd=pHP6UZy0G>#BFm(np+dekv0l6gd=36FijlT8^kI5; zw?Z*FPsibF2d9T$_L@uX9iw*>y_w9HSh8c=Rm}f>%W+8OS=Hj_wsH-^actull3c@!z@R4NQ4qpytnwMaY z)>!;FUeY?h2N9tD(othc7Q=(dF zZAX&Y1ac1~0n(z}!9{J2kPPnru1?qteJPvA2m!@3Zh%+f1VQt~@leK^$&ZudOpS!+ zw#L0usf!?Df1tB?9=zPZ@q2sG!A#9 zKZL`2cs%|Jf}wG=_rJkwh|5Idb;&}z)JQuMVCZSH9kkG%zvQO01wBN)c4Q`*xnto3 zi7TscilQ>t_SLij{@Fepen*a(`upw#RJAx|JYYXvP1v8f)dTHv9pc3ZUwx!0tOH?c z^Hn=gfjUyo!;+3vZhxNE?LJgP`qYJ`J)umMXT@b z{nU(a^xFfofcxfHN-!Jn*{Dp5NZ&i9#9r{)s^lUFCzs5LQL9~HgxvmU#W|iNs0<3O z%Y2FEgvts4t({%lfX1uJ$w{JwfpV|HsO{ZDl2|Q$-Q?UJd`@SLBsMKGjFFrJ(s?t^ z2Llf`deAe@YaGJf)k2e&ryg*m8R|pcjct@rOXa=64#V9!sp=6tC#~QvYh&M~zmJ;% zr*A}V)Ka^3JE!1pcF5G}b&jdrt;bM^+J;G^#R08x@{|ZWy|547&L|k6)HLG|sN<~o z?y`%kbfRN_vc}pwS!Zr}*q6DG7;be0qmxn)eOcD%s3Wk`=@GM>U3ojhAW&WRppi0e zudTj{ufwO~H7izZJmLJD3uPHtjAJvo6H=)&SJ_2%qRRECN#HEU_RGa(Pefk*HIvOH zW7{=Tt(Q(LZ6&WX_Z9vpen}jqge|wCCaLYpiw@f_%9+-!l{kYi&gT@Cj#D*&rz1%e z@*b1W13bN8^j7IpAi$>`_0c!aVzLe*01DY-AcvwE;kW}=Z{3RJLR|O~^iOS(dNEnL zJJ?Dv^ab++s2v!4Oa_WFDLc4fMspglkh;+vzg)4;LS{%CR*>VwyP4>1Tly+!fA-k? z6$bg!*>wKtg!qGO6GQ=cAmM_RC&hKg$~(m2LdP{{*M+*OVf07P$OHp*4SSj9H;)1p z^b1_4p4@C;8G7cBCB6XC{i@vTB3#55iRBZiml^jc4sYnepCKUD+~k}TiuA;HWC6V3 zV{L5uUAU9CdoU+qsFszEwp;@d^!6XnX~KI|!o|=r?qhs`(-Y{GfO4^d6?8BC0xonf zKtZc1C@dNu$~+p#m%JW*J7alfz^$x`U~)1{c7svkIgQ3~RK2LZ5;2TAx=H<4AjC8{ z;)}8OfkZy7pSzVsdX|wzLe=SLg$W1+`Isf=o&}npxWdVR(i8Rr{uzE516a@28VhVr zVgZ3L&X(Q}J0R2{V(}bbNwCDD5K)<5h9CLM*~!xmGTl{Mq$@;~+|U*O#nc^oHnFOy z9Kz%AS*=iTBY_bSZAAY6wXCI?EaE>8^}WF@|}O@I#i69ljjWQPBJVk zQ_rt#J56_wGXiyItvAShJpLEMtW_)V5JZAuK#BAp6bV3K;IkS zK0AL(3ia99!vUPL#j>?<>mA~Q!mC@F-9I$9Z!96ZCSJO8FDz1SP3gF~m`1c#y!efq8QN}eHd+BHwtm%M5586jlU8&e!CmOC z^N_{YV$1`II$~cTxt*dV{-yp61nUuX5z?N8GNBuZZR}Uy_Y3_~@Y3db#~-&0TX644OuG^D3w_`?Yci{gTaPWST8`LdE)HK5OYv>a=6B%R zw|}>ngvSTE1rh`#1Rey0?LXTq;bCIy>TKm^CTV4BCSqdpx1pzC3^ca*S3fUBbKMzF z6X%OSdtt50)yJw*V_HE`hnBA)1yVN3Ruq3l@lY;%Bu+Q&hYLf_Z@fCUVQY-h4M3)- zE_G|moU)Ne0TMjhg?tscN7#ME6!Rb+y#Kd&-`!9gZ06o3I-VX1d4b1O=bpRG-tDK0 zSEa9y46s7QI%LmhbU3P`RO?w#FDM(}k8T`&>OCU3xD=s5N7}w$GntXF;?jdVfg5w9OR8VPxp5{uw zD+_;Gb}@7Vo_d3UV7PS65%_pBUeEwX_Hwfe2e6Qmyq$%0i8Ewn%F7i%=CNEV)Qg`r|&+$ zP6^Vl(MmgvFq`Zb715wYD>a#si;o+b4j^VuhuN>+sNOq6Qc~Y;Y=T&!Q4>(&^>Z6* zwliz!_16EDLTT;v$@W(s7s0s zi*%p>q#t)`S4j=Ox_IcjcllyT38C4hr&mlr6qX-c;qVa~k$MG;UqdnzKX0wo0Xe-_)b zrHu1&21O$y5828UIHI@N;}J@-9cpxob}zqO#!U%Q*ybZ?BH#~^fOT_|8&xAs_rX24 z^nqn{UWqR?MlY~klh)#Rz-*%&e~9agOg*fIN`P&v!@gcO25Mec23}PhzImkdwVT|@ zFR9dYYmf&HiUF4xO9@t#u=uTBS@k*97Z!&hu@|xQnQDkLd!*N`!0JN7{EUoH%OD85 z@aQ2(w-N)1_M{;FV)C#(a4p!ofIA3XG(XZ2E#%j_(=`IWlJAHWkYM2&(+yY|^2TB0 z>wfC-+I}`)LFOJ%KeBb1?eNxGKeq?AI_eBE!M~$wYR~bB)J3=WvVlT8ZlF2EzIFZt zkaeyj#vmBTGkIL9mM3cEz@Yf>j=82+KgvJ-u_{bBOxE5zoRNQW3+Ahx+eMGem|8xo zL3ORKxY_R{k=f~M5oi-Z>5fgqjEtzC&xJEDQ@`<)*Gh3UsftBJno-y5Je^!D?Im{j za*I>RQ=IvU@5WKsIr?kC$DT+2bgR>8rOf3mtXeMVB~sm%X7W5`s=Tp>FR544tuQ>9qLt|aUSv^io&z93luW$_OYE^sf8DB?gx z4&k;dHMWph>Z{iuhhFJr+PCZ#SiZ9e5xM$A#0yPtVC>yk&_b9I676n|oAH?VeTe*1 z@tDK}QM-%J^3Ns6=_vh*I8hE?+=6n9nUU`}EX|;Mkr?6@NXy8&B0i6h?7%D=%M*Er zivG61Wk7e=v;<%t*G+HKBqz{;0Biv7F+WxGirONRxJij zon5~(a`UR%uUzfEma99QGbIxD(d}~oa|exU5Y27#4k@N|=hE%Y?Y3H%rcT zHmNO#ZJ7nPHRG#y-(-FSzaZ2S{`itkdYY^ZUvyw<7yMBkNG+>$Rfm{iN!gz7eASN9-B3g%LIEyRev|3)kSl;JL zX7MaUL_@~4ot3$woD0UA49)wUeu7#lj77M4ar8+myvO$B5LZS$!-ZXw3w;l#0anYz zDc_RQ0Ome}_i+o~H=CkzEa&r~M$1GC!-~WBiHiDq9Sdg{m|G?o7g`R%f(Zvby5q4; z=cvn`M>RFO%i_S@h3^#3wImmWI4}2x4skPNL9Am{c!WxR_spQX3+;fo!y(&~Palyjt~Xo0uy6d%sX&I`e>zv6CRSm)rc^w!;Y6iVBb3x@Y=`hl9jft zXm5vilB4IhImY5b->x{!MIdCermpyLbsalx8;hIUia%*+WEo4<2yZ6`OyG1Wp%1s$ zh<|KrHMv~XJ9dC8&EXJ`t3ETz>a|zLMx|MyJE54RU(@?K&p2d#x?eJC*WKO9^d17# zdTTKx-Os3k%^=58Sz|J28aCJ}X2-?YV3T7ee?*FoDLOC214J4|^*EX`?cy%+7Kb3(@0@!Q?p zk>>6dWjF~y(eyRPqjXqDOT`4^Qv-%G#Zb2G?&LS-EmO|ixxt79JZlMgd^~j)7XYQ; z62rGGXA=gLfgy{M-%1gR87hbhxq-fL)GSfEAm{yLQP!~m-{4i_jG*JsvUdqAkoc#q6Yd&>=;4udAh#?xa2L z7mFvCjz(hN7eV&cyFb%(U*30H@bQ8-b7mkm!=wh2|;+_4vo=tyHPQ0hL=NR`jbsSiBWtG ztMPPBgHj(JTK#0VcP36Z`?P|AN~ybm=jNbU=^3dK=|rLE+40>w+MWQW%4gJ`>K!^- zx4kM*XZLd(E4WsolMCRsdvTGC=37FofIyCZCj{v3{wqy4OXX-dZl@g`Dv>p2`l|H^ zS_@(8)7gA62{Qfft>vx71stILMuyV4uKb7BbCstG@|e*KWl{P1$=1xg(7E8MRRCWQ1g)>|QPAZot~|FYz_J0T+r zTWTB3AatKyUsTXR7{Uu) z$1J5SSqoJWt(@@L5a)#Q6bj$KvuC->J-q1!nYS6K5&e7vNdtj- zj9;qwbODLgIcObqNRGs1l{8>&7W?BbDd!87=@YD75B2ep?IY|gE~t)$`?XJ45MG@2 zz|H}f?qtEb_p^Xs$4{?nA=Qko3Lc~WrAS`M%9N60FKqL7XI+v_5H-UDiCbRm`fEmv z$pMVH*#@wQqml~MZe+)e4Ts3Gl^!Z0W3y$;|9hI?9(iw29b7en0>Kt2pjFXk@!@-g zTb4}Kw!@u|V!wzk0|qM*zj$*-*}e*ZXs#Y<6E_!BR}3^YtjI_byo{F+w9H9?f%mnBh(uE~!Um7)tgp2Ye;XYdVD95qt1I-fc@X zXHM)BfJ?^g(s3K|{N8B^hamrWAW|zis$`6|iA>M-`0f+vq(FLWgC&KnBDsM)_ez1# zPCTfN8{s^K`_bum2i5SWOn)B7JB0tzH5blC?|x;N{|@ch(8Uy-O{B2)OsfB$q0@FR z27m3YkcVi$KL;;4I*S;Z#6VfZcZFn!D2Npv5pio)sz-`_H*#}ROd7*y4i(y(YlH<4 zh4MmqBe^QV_$)VvzWgMXFy`M(vzyR2u!xx&%&{^*AcVLrGa8J9ycbynjKR~G6zC0e zlEU>zt7yQtMhz>XMnz>ewXS#{Bulz$6HETn?qD5v3td>`qGD;Y8&RmkvN=24=^6Q@DYY zxMt}uh2cSToMkkIWo1_Lp^FOn$+47JXJ*#q=JaeiIBUHEw#IiXz8cStEsw{UYCA5v_%cF@#m^Y!=+qttuH4u}r6gMvO4EAvjBURtLf& z6k!C|OU@hv_!*qear3KJ?VzVXDKqvKRtugefa7^^MSWl0fXXZR$Xb!b6`eY4A1#pk zAVoZvb_4dZ{f~M8fk3o?{xno^znH1t;;E6K#9?erW~7cs%EV|h^K>@&3Im}c7nm%Y zbLozFrwM&tSNp|46)OhP%MJ(5PydzR>8)X%i3!^L%3HCoCF#Y0#9vPI5l&MK*_ z6G8Y>$`~c)VvQle_4L_AewDGh@!bKkJeEs_NTz(yilnM!t}7jz>fmJb89jQo6~)%% z@GNIJ@AShd&K%UdQ5vR#yT<-goR+D@Tg;PuvcZ*2AzSWN&wW$Xc+~vW)pww~O|6hL zBxX?hOyA~S;3rAEfI&jmMT4f!-eVm%n^KF_QT=>!A<5tgXgi~VNBXqsFI(iI$Tu3x0L{<_-%|HMG4Cn?Xs zq~fvBhu;SDOCD7K5(l&i7Py-;Czx5byV*3y%#-Of9rtz?M_owXc2}$OIY~)EZ&2?r zLQ(onz~I7U!w?B%LtfDz)*X=CscqH!UE=mO?d&oYvtj|(u)^yomS;Cd>Men|#2yuD zg&tf(*iSHyo;^A03p&_j*QXay9d}qZ0CgU@rnFNDIT5xLhC5_tlugv()+w%`7;ICf z>;<#L4m@{1}Og76*e zHWFm~;n@B1GqO8s%=qu)+^MR|jp(ULUOi~v;wE8SB6^mK@adSb=o+A_>Itjn13AF& zDZe+wUF9G!JFv|dpj1#d+}BO~s*QTe3381TxA%Q>P*J#z%( z5*8N^QWxgF73^cTKkkvgvIzf*cLEyyKw)Wf{#$n{uS#(rAA~>TS#!asqQ2m_izXe3 z7$Oh=rR;sdmVx3G)s}eImsb<@r2~5?vcw*Q4LU~FFh!y4r*>~S7slAE6)W3Up2OHr z2R)+O<0kKo<3+5vB}v!lB*`%}gFldc+79iahqEx#&Im@NCQU$@PyCZbcTt?K{;o@4 z312O9GB)?X&wAB}*-NEU zn@6`)G`FhT8O^=Cz3y+XtbwO{5+{4-&?z!esFts-C zypwgI^4#tZ74KC+_IW|E@kMI=1pSJkvg$9G3Va(!reMnJ$kcMiZ=30dTJ%(Ws>eUf z;|l--TFDqL!PZbLc_O(XP0QornpP;!)hdT#Ts7tZ9fcQeH&rhP_1L|Z_ha#JOroe^qcsLi`+AoBWHPM7}gD z+mHuPXd14M?nkp|nu9G8hPk;3=JXE-a204Fg!BK|$MX`k-qPeD$2OOqvF;C(l8wm13?>i(pz7kRyYm zM$IEzf`$}B%ezr!$(UO#uWExn%nTCTIZzq&8@i8sP#6r8 z*QMUzZV(LEWZb)wbmf|Li;UpiP;PlTQ(X4zreD`|`RG!7_wc6J^MFD!A=#K*ze>Jg z?9v?p(M=fg_VB0+c?!M$L>5FIfD(KD5ku*djwCp+5GVIs9^=}kM2RFsxx0_5DE%BF zykxwjWvs=rbi4xKIt!z$&v(`msFrl4n>a%NO_4`iSyb!UiAE&mDa+apc zPe)#!ToRW~rqi2e1bdO1RLN5*uUM@{S`KLJhhY-@TvC&5D(c?a(2$mW-&N%h5IfEM zdFI6`6KJiJQIHvFiG-34^BtO3%*$(-Ht_JU*(KddiUYoM{coadlG&LVvke&*p>Cac z^BPy2Zteiq1@ulw0e)e*ot7@A$RJui0$l^{lsCt%R;$){>zuRv9#w@;m=#d%%TJmm zC#%eFOoy$V)|3*d<OC1iP+4R7D z8FE$E8l2Y?(o-i6wG=BKBh0-I?i3WF%hqdD7VCd;vpk|LFP!Et8$@voH>l>U8BY`Q zC*G;&y6|!p=7`G$*+hxCv!@^#+QD3m>^azyZoLS^;o_|plQaj-wx^ zRV&$HcY~p)2|Zqp0SYU?W3zV87s6JP-@D~$t0 zvd;-YL~JWc*8mtHz_s(cXus#XYJc5zdC=&!4MeZ;N3TQ>^I|Pd=HPjVP*j^45rs(n zzB{U4-44=oQ4rNN6@>qYVMH4|GmMIz#z@3UW-1_y#eNa+Q%(41oJ5i(DzvMO^%|?L z^r_+MZtw0DZ0=BT-@?hUtA)Ijk~Kh-N8?~X5%KnRH7cb!?Yrd8gtiEo!v{sGrQk{X zvV>h{8-DqTyuAxIE(hb}jMVtga$;FIrrKm>ye5t%M;p!jcH1(Bbux>4D#MVhgZGd> z=c=nVb%^9T?iDgM&9G(mV5xShc-lBLi*6RShenDqB%`-2;I*;IHg6>#ovKQ$M}dDb z<$USN%LMqa5_5DR7g7@(oAoQ%!~<1KSQr$rmS{UFQJs5&qBhgTEM_Y7|0Wv?fbP`z z)`8~=v;B)+>Jh`V*|$dTxKe`HTBkho^-!!K#@i{9FLn-XqX&fQcGsEAXp)BV7(`Lk zC{4&+Pe-0&<)C0kAa(MTnb|L;ZB5i|b#L1o;J)+?SV8T*U9$Vxhy}dm3%!A}SK9l_6(#5(e*>8|;4gNKk7o_%m_ zEaS=Z(ewk}hBJ>v`jtR=$pm_Wq3d&DU+6`BACU4%qdhH1o^m8hT2&j<4Z8!v=rMCk z-I*?48{2H*&+r<{2?wp$kh@L@=rj8c`EaS~J>W?)trc?zP&4bsNagS4yafuDoXpi5`!{BVqJ1$ZC3`pf$`LIZ(`0&Ik+!_Xa=NJW`R2 zd#Ntgwz`JVwC4A61$FZ&kP)-{T|rGO59`h#1enAa`cWxRR8bKVvvN6jBzAYePrc&5 z+*zr3en|LYB2>qJp479rEALk5d*X-dfKn6|kuNm;2-U2+P3_rma!nWjZQ-y*q3JS? zBE}zE-!1ZBR~G%v!$l#dZ*$UV4$7q}xct}=on+Ba8{b>Y9h*f-GW0D0o#vJ0%ALg( ztG2+AjWlG#d;myA(i&dh8Gp?y9HD@`CTaDAy?c&0unZ%*LbLIg4;m{Kc?)ws3^>M+ zt5>R)%KIJV*MRUg{0$#nW=Lj{#8?dD$yhjBOrAeR#4$H_Dc(eyA4dNjZEz1Xk+Bqt zB&pPl+?R{w8GPv%VI`x`IFOj320F1=cV4aq0(*()Tx!VVxCjua;)t}gTr=b?zY+U! zkb}xjXZ?hMJN{Hjw?w&?gz8Ow`htX z@}WG*_4<%ff8(!S6bf3)p+8h2!Rory>@aob$gY#fYJ=LiW0`+~l7GI%EX_=8 z{(;0&lJ%9)M9{;wty=XvHbIx|-$g4HFij`J$-z~`mW)*IK^MWVN+*>uTNqaDmi!M8 zurj6DGd)g1g(f`A-K^v)3KSOEoZXImXT06apJum-dO_%oR)z6Bam-QC&CNWh7kLOE zcxLdVjYLNO2V?IXWa-ys30Jbxw(Xm?U1{4kDs9`gZQHh8X{*w9=H&Zz&-6RL?uq#R zxN+k~JaL|gdsdvY_u6}}MHC?a@ElFeipA1Lud#M~)pp2SnG#K{a@tSpvXM;A8gz9> zRVDV5T1%%!LsNRDOw~LIuiAiKcj<%7WpgjP7G6mMU1#pFo6a-1>0I5ZdhxnkMX&#L z=Vm}?SDlb_LArobqpnU!WLQE*yVGWgs^4RRy4rrJwoUUWoA~ZJUx$mK>J6}7{CyC4 zv=8W)kKl7TmAnM%m;anEDPv5tzT{A{ON9#FPYF6c=QIc*OrPp96tiY&^Qs+#A1H>Y z<{XtWt2eDwuqM zQ_BI#UIP;2-olOL4LsZ`vTPv-eILtuB7oWosoSefWdM}BcP>iH^HmimR`G`|+9waCO z&M375o@;_My(qYvPNz;N8FBZaoaw3$b#x`yTBJLc8iIP z--la{bzK>YPP|@Mke!{Km{vT8Z4|#An*f=EmL34?!GJfHaDS#41j~8c5KGKmj!GTh&QIH+DjEI*BdbSS2~6VTt}t zhAwNQNT6%c{G`If3?|~Fp7iwee(LaUS)X9@I29cIb61} z$@YBq4hSplr&liE@ye!y&7+7n$fb+8nS~co#^n@oCjCwuKD61x$5|0ShDxhQES5MP z(gH|FO-s6#$++AxnkQR!3YMgKcF)!&aqr^a3^{gAVT`(tY9@tqgY7@ z>>ul3LYy`R({OY7*^Mf}UgJl(N7yyo$ag;RIpYHa_^HKx?DD`%Vf1D0s^ zjk#OCM5oSzuEz(7X`5u~C-Y~n4B}_3*`5B&8tEdND@&h;H{R`o%IFpIJ4~Kw!kUjehGT8W!CD7?d8sg_$KKp%@*dW)#fI1#R<}kvzBVpaog_2&W%c_jJfP` z6)wE+$3+Hdn^4G}(ymPyasc1<*a7s2yL%=3LgtZLXGuA^jdM^{`KDb%%}lr|ONDsl zy~~jEuK|XJ2y<`R{^F)Gx7DJVMvpT>gF<4O%$cbsJqK1;v@GKXm*9l3*~8^_xj*Gs z=Z#2VQ6`H@^~#5Pv##@CddHfm;lbxiQnqy7AYEH(35pTg^;u&J2xs-F#jGLuDw2%z z`a>=0sVMM+oKx4%OnC9zWdbpq*#5^yM;og*EQKpv`^n~-mO_vj=EgFxYnga(7jO?G z`^C87B4-jfB_RgN2FP|IrjOi;W9AM1qS}9W@&1a9Us>PKFQ9~YE!I~wTbl!m3$Th? z)~GjFxmhyyGxN}t*G#1^KGVXm#o(K0xJyverPe}mS=QgJ$#D}emQDw+dHyPu^&Uv> z4O=3gK*HLFZPBY|!VGq60Of6QrAdj`nj1h!$?&a;Hgaj{oo{l0P3TzpJK_q_eW8Ng zP6QF}1{V;xlolCs?pGegPoCSxx@bshb#3ng4Fkp4!7B0=&+1%187izf@}tvsjZ6{m z4;K>sR5rm97HJrJ`w}Y`-MZN$Wv2N%X4KW(N$v2@R1RkRJH2q1Ozs0H`@ zd5)X-{!{<+4Nyd=hQ8Wm3CCd}ujm*a?L79ztfT7@&(?B|!pU5&%9Rl!`i;suAg0+A zxb&UYpo-z}u6CLIndtH~C|yz&!OV_I*L;H#C7ie_5uB1fNRyH*<^d=ww=gxvE%P$p zRHKI{^{nQlB9nLhp9yj-so1is{4^`{Xd>Jl&;dX;J)#- z=fmE5GiV?-&3kcjM1+XG7&tSq;q9Oi4NUuRrIpoyp*Fn&nVNFdUuGQ_g)g>VzXGdneB7`;!aTUE$t* z5iH+8XPxrYl)vFo~+vmcU-2) zq!6R(T0SsoDnB>Mmvr^k*{34_BAK+I=DAGu){p)(ndZqOFT%%^_y;X(w3q-L``N<6 zw9=M zoQ8Lyp>L_j$T20UUUCzYn2-xdN}{e@$8-3vLDN?GbfJ>7*qky{n!wC#1NcYQr~d51 zy;H!am=EI#*S&TCuP{FA3CO)b0AAiN*tLnDbvKwxtMw-l;G2T@EGH)YU?-B`+Y=!$ zypvDn@5V1Tr~y~U0s$ee2+CL3xm_BmxD3w}d_Pd@S%ft#v~_j;6sC6cy%E|dJy@wj z`+(YSh2CrXMxI;yVy*=O@DE2~i5$>nuzZ$wYHs$y`TAtB-ck4fQ!B8a;M=CxY^Nf{ z+UQhn0jopOzvbl(uZZ1R-(IFaprC$9hYK~b=57@ zAJ8*pH%|Tjotzu5(oxZyCQ{5MAw+6L4)NI!9H&XM$Eui-DIoDa@GpNI=I4}m>Hr^r zZjT?xDOea}7cq+TP#wK1p3}sbMK{BV%(h`?R#zNGIP+7u@dV5#zyMau+w}VC1uQ@p zrFUjrJAx6+9%pMhv(IOT52}Dq{B9njh_R`>&j&5Sbub&r*hf4es)_^FTYdDX$8NRk zMi=%I`)hN@N9>X&Gu2RmjKVsUbU>TRUM`gwd?CrL*0zxu-g#uNNnnicYw=kZ{7Vz3 zULaFQ)H=7%Lm5|Z#k?<{ux{o4T{v-e zTLj?F(_qp{FXUzOfJxEyKO15Nr!LQYHF&^jMMBs z`P-}WCyUYIv>K`~)oP$Z85zZr4gw>%aug1V1A)1H(r!8l&5J?ia1x_}Wh)FXTxZUE zs=kI}Ix2cK%Bi_Hc4?mF^m`sr6m8M(n?E+k7Tm^Gn}Kf= zfnqoyVU^*yLypz?s+-XV5(*oOBwn-uhwco5b(@B(hD|vtT8y7#W{>RomA_KchB&Cd zcFNAD9mmqR<341sq+j+2Ra}N5-3wx5IZqg6Wmi6CNO#pLvYPGNER}Q8+PjvIJ42|n zc5r@T*p)R^U=d{cT2AszQcC6SkWiE|hdK)m{7ul^mU+ED1R8G#)#X}A9JSP_ubF5p z8Xxcl;jlGjPwow^p+-f_-a~S;$lztguPE6SceeUCfmRo=Qg zKHTY*O_ z;pXl@z&7hniVYVbGgp+Nj#XP^Aln2T!D*{(Td8h{8Dc?C)KFfjPybiC`Va?Rf)X>y z;5?B{bAhPtbmOMUsAy2Y0RNDQ3K`v`gq)#ns_C&ec-)6cq)d^{5938T`Sr@|7nLl; zcyewuiSUh7Z}q8iIJ@$)L3)m)(D|MbJm_h&tj^;iNk%7K-YR}+J|S?KR|29K?z-$c z<+C4uA43yfSWBv*%z=-0lI{ev`C6JxJ};A5N;lmoR(g{4cjCEn33 z-ef#x^uc%cM-f^_+*dzE?U;5EtEe;&8EOK^K}xITa?GH`tz2F9N$O5;)`Uof4~l+t z#n_M(KkcVP*yMYlk_~5h89o zlf#^qjYG8Wovx+f%x7M7_>@r7xaXa2uXb?_*=QOEe_>ErS(v5-i)mrT3&^`Oqr4c9 zDjP_6T&NQMD`{l#K&sHTm@;}ed_sQ88X3y`ON<=$<8Qq{dOPA&WAc2>EQ+U8%>yWR zK%(whl8tB;{C)yRw|@Gn4%RhT=bbpgMZ6erACc>l5^p)9tR`(2W-D*?Ph6;2=Fr|G- zdF^R&aCqyxqWy#P7#G8>+aUG`pP*ow93N=A?pA=aW0^^+?~#zRWcf_zlKL8q8-80n zqGUm=S8+%4_LA7qrV4Eq{FHm9#9X15%ld`@UKyR7uc1X*>Ebr0+2yCye6b?i=r{MPoqnTnYnq z^?HWgl+G&@OcVx4$(y;{m^TkB5Tnhx2O%yPI=r*4H2f_6Gfyasq&PN^W{#)_Gu7e= zVHBQ8R5W6j;N6P3O(jsRU;hkmLG(Xs_8=F&xh@`*|l{~0OjUVlgm z7opltSHg7Mb%mYamGs*v1-#iW^QMT**f+Nq*AzIvFT~Ur3KTD26OhIw1WQsL(6nGg znHUo-4e15cXBIiyqN};5ydNYJ6zznECVVR44%(P0oW!yQ!YH)FPY?^k{IrtrLo7Zo`?sg%%oMP9E^+H@JLXicr zi?eoI?LODRPcMLl90MH32rf8btf69)ZE~&4d%(&D{C45egC6bF-XQ;6QKkbmqW>_H z{86XDZvjiN2wr&ZPfi;^SM6W+IP0);50m>qBhzx+docpBkkiY@2bSvtPVj~E`CfEu zhQG5G>~J@dni5M5Jmv7GD&@%UR`k3ru-W$$onI259jM&nZ)*d3QFF?Mu?{`+nVzkx z=R*_VH=;yeU?9TzQ3dP)q;P)4sAo&k;{*Eky1+Z!10J<(cJC3zY9>bP=znA=<-0RR zMnt#<9^X7BQ0wKVBV{}oaV=?JA=>R0$az^XE%4WZcA^Em>`m_obQyKbmf-GA;!S-z zK5+y5{xbkdA?2NgZ0MQYF-cfOwV0?3Tzh8tcBE{u%Uy?Ky4^tn^>X}p>4&S(L7amF zpWEio8VBNeZ=l!%RY>oVGOtZh7<>v3?`NcHlYDPUBRzgg z0OXEivCkw<>F(>1x@Zk=IbSOn+frQ^+jI*&qdtf4bbydk-jgVmLAd?5ImK+Sigh?X zgaGUlbf^b-MH2@QbqCawa$H1Vb+uhu{zUG9268pa{5>O&Vq8__Xk5LXDaR1z$g;s~;+Ae82wq#l;wo08tX(9uUX6NJWq1vZLh3QbP$# zL`udY|Qp*4ER`_;$%)2 zmcJLj|FD`(;ts0bD{}Ghq6UAVpEm#>j`S$wHi0-D_|)bEZ}#6) zIiqH7Co;TB`<6KrZi1SF9=lO+>-_3=Hm%Rr7|Zu-EzWLSF{9d(H1v*|UZDWiiqX3} zmx~oQ6%9~$=KjPV_ejzz7aPSvTo+3@-a(OCCoF_u#2dHY&I?`nk zQ@t8#epxAv@t=RUM09u?qnPr6=Y5Pj;^4=7GJ`2)Oq~H)2V)M1sC^S;w?hOB|0zXT zQdf8$)jslO>Q}(4RQ$DPUF#QUJm-k9ysZFEGi9xN*_KqCs9Ng(&<;XONBDe1Joku? z*W!lx(i&gvfXZ4U(AE@)c0FI2UqrFLOO$&Yic|`L;Vyy-kcm49hJ^Mj^H9uY8Fdm2 z?=U1U_5GE_JT;Tx$2#I3rAAs(q@oebIK=19a$N?HNQ4jw0ljtyGJ#D}z3^^Y=hf^Bb--297h6LQxi0-`TB|QY2QPg92TAq$cEQdWE ze)ltSTVMYe0K4wte6;^tE+^>|a>Hit_3QDlFo!3Jd`GQYTwlR#{<^MzG zK!vW&))~RTKq4u29bc<+VOcg7fdorq-kwHaaCQe6tLB{|gW1_W_KtgOD0^$^|`V4C# z*D_S9Dt_DIxpjk3my5cBFdiYaq||#0&0&%_LEN}BOxkb3v*d$4L|S|z z!cZZmfe~_Y`46v=zul=aixZTQCOzb(jx>8&a%S%!(;x{M2!*$od2!Pwfs>RZ-a%GOZdO88rS)ZW~{$656GgW)$Q=@!x;&Nn~!K)lr4gF*%qVO=hlodHA@2)keS2 zC}7O=_64#g&=zY?(zhzFO3)f5=+`dpuyM!Q)zS&otpYB@hhn$lm*iK2DRt+#1n|L%zjM}nB*$uAY^2JIw zV_P)*HCVq%F))^)iaZD#R9n^{sAxBZ?Yvi1SVc*`;8|F2X%bz^+s=yS&AXjysDny)YaU5RMotF-tt~FndTK ziRve_5b!``^ZRLG_ks}y_ye0PKyKQSsQCJuK5()b2ThnKPFU?An4;dK>)T^4J+XjD zEUsW~H?Q&l%K4<1f5^?|?lyCQe(O3?!~OU{_Wxs#|Ff8?a_WPQUKvP7?>1()Cy6oLeA zjEF^d#$6Wb${opCc^%%DjOjll%N2=GeS6D-w=Ap$Ux2+0v#s#Z&s6K*)_h{KFfgKjzO17@p1nKcC4NIgt+3t}&}F z@cV; zZ1r#~?R@ZdSwbFNV(fFl2lWI(Zf#nxa<6f!nBZD>*K)nI&Fun@ngq@Ge!N$O< zySt*mY&0moUXNPe~Fg=%gIu)tJ;asscQ!-AujR@VJBRoNZNk;z4hs4T>Ud!y=1NwGs-k zlTNeBOe}=)Epw=}+dfX;kZ32h$t&7q%Xqdt-&tlYEWc>>c3(hVylsG{Ybh_M8>Cz0ZT_6B|3!_(RwEJus9{;u-mq zW|!`{BCtnao4;kCT8cr@yeV~#rf76=%QQs(J{>Mj?>aISwp3{^BjBO zLV>XSRK+o=oVDBnbv?Y@iK)MiFSl{5HLN@k%SQZ}yhPiu_2jrnI?Kk?HtCv>wN$OM zSe#}2@He9bDZ27hX_fZey=64#SNU#1~=icK`D>a;V-&Km>V6ZdVNj7d2 z-NmAoOQm_aIZ2lXpJhlUeJ95eZt~4_S zIfrDs)S$4UjyxKSaTi#9KGs2P zfSD>(y~r+bU4*#|r`q+be_dopJzKK5JNJ#rR978ikHyJKD>SD@^Bk$~D0*U38Y*IpYcH>aaMdZq|YzQ-Ixd(_KZK!+VL@MWGl zG!k=<%Y-KeqK%``uhx}0#X^@wS+mX@6Ul@90#nmYaKh}?uw>U;GS4fn3|X%AcV@iY z8v+ePk)HxSQ7ZYDtlYj#zJ?5uJ8CeCg3efmc#|a%2=u>+vrGGRg$S@^mk~0f;mIu! zWMA13H1<@hSOVE*o0S5D8y=}RiL#jQpUq42D}vW$z*)VB*FB%C?wl%(3>ANaY)bO@ zW$VFutemwy5Q*&*9HJ603;mJJkB$qp6yxNOY0o_4*y?2`qbN{m&*l{)YMG_QHXXa2 z+hTmlA;=mYwg{Bfusl zyF&}ib2J;#q5tN^e)D62fWW*Lv;Rnb3GO-JVtYG0CgR4jGujFo$Waw zSNLhc{>P~>{KVZE1Vl1!z)|HFuN@J7{`xIp_)6>*5Z27BHg6QIgqLqDJTmKDM+ON* zK0Fh=EG`q13l z+m--9UH0{ZGQ%j=OLO8G2WM*tgfY}bV~>3Grcrpehjj z6Xe<$gNJyD8td3EhkHjpKk}7?k55Tu7?#;5`Qcm~ki;BeOlNr+#PK{kjV>qfE?1No zMA07}b>}Dv!uaS8Hym0TgzxBxh$*RX+Fab6Gm02!mr6u}f$_G4C|^GSXJMniy^b`G z74OC=83m0G7L_dS99qv3a0BU({t$zHQsB-RI_jn1^uK9ka_%aQuE2+~J2o!7`735Z zb?+sTe}Gd??VEkz|KAPMfj(1b{om89p5GIJ^#Aics_6DD%WnNGWAW`I<7jT|Af|8g zZA0^)`p8i#oBvX2|I&`HC8Pn&0>jRuMF4i0s=}2NYLmgkZb=0w9tvpnGiU-gTUQhJ zR6o4W6ZWONuBZAiN77#7;TR1^RKE(>>OL>YU`Yy_;5oj<*}ac99DI(qGCtn6`949f ziMpY4k>$aVfffm{dNH=-=rMg|u?&GIToq-u;@1-W&B2(UOhC-O2N5_px&cF-C^tWp zXvChm9@GXEcxd;+Q6}u;TKy}$JF$B`Ty?|Y3tP$N@Rtoy(*05Wj-Ks32|2y2ZM>bM zi8v8E1os!yorR!FSeP)QxtjIKh=F1ElfR8U7StE#Ika;h{q?b?Q+>%78z^>gTU5+> zxQ$a^rECmETF@Jl8fg>MApu>btHGJ*Q99(tMqsZcG+dZ6Yikx7@V09jWCiQH&nnAv zY)4iR$Ro223F+c3Q%KPyP9^iyzZsP%R%-i^MKxmXQHnW6#6n7%VD{gG$E;7*g86G< zu$h=RN_L2(YHO3@`B<^L(q@^W_0#U%mLC9Q^XEo3LTp*~(I%?P_klu-c~WJxY1zTI z^PqntLIEmdtK~E-v8yc&%U+jVxW5VuA{VMA4Ru1sk#*Srj0Pk#tZuXxkS=5H9?8eb z)t38?JNdP@#xb*yn=<*_pK9^lx%;&yH6XkD6-JXgdddZty8@Mfr9UpGE!I<37ZHUe z_Rd+LKsNH^O)+NW8Ni-V%`@J_QGKA9ZCAMSnsN>Ych9VW zCE7R_1FVy}r@MlkbxZ*TRIGXu`ema##OkqCM9{wkWQJg^%3H${!vUT&vv2250jAWN zw=h)C!b2s`QbWhBMSIYmWqZ_~ReRW;)U#@C&ThctSd_V!=HA=kdGO-Hl57an|M1XC?~3f0{7pyjWY}0mChU z2Fj2(B*r(UpCKm-#(2(ZJD#Y|Or*Vc5VyLpJ8gO1;fCm@EM~{DqpJS5FaZ5%|ALw) zyumBl!i@T57I4ITCFmdbxhaOYud}i!0YkdiNRaQ%5$T5>*HRBhyB~<%-5nj*b8=i= z(8g(LA50%0Zi_eQe}Xypk|bt5e6X{aI^jU2*c?!p*$bGk=?t z+17R){lx~Z{!B34Zip~|A;8l@%*Gc}kT|kC0*Ny$&fI3@%M! zqk_zvN}7bM`x@jqFOtaxI?*^Im5ix@=`QEv;__i;Tek-&7kGm6yP17QANVL>*d0B=4>i^;HKb$k8?DYFMr38IX4azK zBbwjF%$>PqXhJh=*7{zH5=+gi$!nc%SqFZlwRm zmpctOjZh3bwt!Oc>qVJhWQf>`HTwMH2ibK^eE*j!&Z`-bs8=A`Yvnb^?p;5+U=Fb8 z@h>j_3hhazd$y^Z-bt%3%E3vica%nYnLxW+4+?w{%|M_=w^04U{a6^22>M_?{@mXP zS|Qjcn4&F%WN7Z?u&I3fU(UQVw4msFehxR*80dSb=a&UG4zDQp&?r2UGPy@G?0FbY zVUQ?uU9-c;f9z06$O5FO1TOn|P{pLcDGP?rfdt`&uw|(Pm@$n+A?)8 zP$nG(VG&aRU*(_5z#{+yVnntu`6tEq>%9~n^*ao}`F6ph_@6_8|AfAXtFfWee_14` zKKURYV}4}=UJmxv7{RSz5QlwZtzbYQs0;t3?kx*7S%nf-aY&lJ@h?-BAn%~0&&@j) zQd_6TUOLXErJ`A3vE?DJIbLE;s~s%eVt(%fMzUq^UfZV9c?YuhO&6pwKt>j(=2CkgTNEq7&c zfeGN+%5DS@b9HO>zsoRXv@}(EiA|t5LPi}*R3?(-=iASADny<{D0WiQG>*-BSROk4vI6%$R>q64J&v-T+(D<_(b!LD z9GL;DV;;N3!pZYg23mcg81tx>7)=e%f|i{6Mx0GczVpc}{}Mg(W_^=Wh0Rp+xXgX` z@hw|5=Je&nz^Xa>>vclstYt;8c2PY)87Ap;z&S&`yRN>yQVV#K{4&diVR7Rm;S{6m z6<+;jwbm`==`JuC6--u6W7A@o4&ZpJV%5+H)}toy0afF*!)AaG5=pz_i9}@OG%?$O z2cec6#@=%xE3K8;^ps<2{t4SnqH+#607gAHP-G4^+PBiC1s>MXf&bQ|Pa;WBIiErV z?3VFpR9JFl9(W$7p3#xe(Bd?Z93Uu~jHJFo7U3K_x4Ej-=N#=a@f;kPV$>;hiN9i9 z<6elJl?bLI$o=|d6jlihA4~bG;Fm2eEnlGxZL`#H%Cdes>uJfMJ4>@1SGGeQ81DwxGxy7L5 zm05Ik*WpSgZvHh@Wpv|2i|Y#FG?Y$hbRM5ZF0Z7FB3cY0+ei#km9mDSPI}^!<<`vr zuv$SPg2vU{wa)6&QMY)h1hbbxvR2cc_6WcWR`SH& z&KuUQcgu}!iW2Wqvp~|&&LSec9>t(UR_|f$;f-fC&tSO-^-eE0B~Frttnf+XN(#T) z^PsuFV#(pE#6ztaI8(;ywN%CtZh?w&;_)w_s@{JiA-SMjf&pQk+Bw<}f@Q8-xCQMwfaf zMgHsAPU=>>Kw~uDFS(IVRN{$ak(SV(hrO!UqhJ?l{lNnA1>U24!=>|q_p404Xd>M# z7?lh^C&-IfeIr`Dri9If+bc%oU0?|Rh8)%BND5;_9@9tuM)h5Kcw6}$Ca7H_n)nOf0pd`boCXItb`o11 zb`)@}l6I_h>n+;`g+b^RkYs7;voBz&Gv6FLmyvY|2pS)z#P;t8k;lS>49a$XeVDc4 z(tx2Pe3N%Gd(!wM`E7WRBZy)~vh_vRGt&esDa0NCua)rH#_39*H0!gIXpd>~{rGx+ zJKAeXAZ-z5n=mMVqlM5Km;b;B&KSJlScD8n?2t}kS4Wf9@MjIZSJ2R?&=zQn zs_`=+5J$47&mP4s{Y{TU=~O_LzSrXvEP6W?^pz<#Y*6Fxg@$yUGp31d(h+4x>xpb< zH+R639oDST6F*0iH<9NHC^Ep*8D4-%p2^n-kD6YEI<6GYta6-I;V^ZH3n5}syTD=P z3b6z=jBsdP=FlXcUe@I|%=tY4J_2j!EVNEzph_42iO3yfir|Dh>nFl&Lu9!;`!zJB zCis9?_(%DI?$CA(00pkzw^Up`O;>AnPc(uE$C^a9868t$m?5Q)CR%!crI$YZpiYK6m= z!jv}82He`QKF;10{9@roL2Q7CF)OeY{~dBp>J~X#c-Z~{YLAxNmn~kWQW|2u!Yq00 zl5LKbzl39sVCTpm9eDW_T>Z{x@s6#RH|P zA~_lYas7B@SqI`N=>x50Vj@S)QxouKC(f6Aj zz}7e5e*5n?j@GO;mCYEo^Jp_*BmLt3!N)(T>f#L$XHQWzZEVlJo(>qH@7;c%fy zS-jm^Adju9Sm8rOKTxfTU^!&bg2R!7C_-t+#mKb_K?0R72%26ASF;JWA_prJ8_SVW zOSC7C&CpSrgfXRp8r)QK34g<~!1|poTS7F;)NseFsbwO$YfzEeG3oo!qe#iSxQ2S# z1=Fxc9J;2)pCab-9o-m8%BLjf(*mk#JJX3k9}S7Oq)dV0jG)SOMbw7V^Z<5Q0Cy$< z^U0QUVd4(96W03OA1j|x%{sd&BRqIERDb6W{u1p1{J(a;fd6lnWzjeS`d?L3-0#o7 z{Qv&L7!Tm`9|}u=|IbwS_jgH(_V@o`S*R(-XC$O)DVwF~B&5c~m!zl14ydT6sK+Ly zn+}2hQ4RTC^8YvrQ~vk$f9u=pTN{5H_yTOcza9SVE&nt_{`ZC8zkmFji=UyD`G4~f zUfSTR=Kju>6u+y&|Bylb*W&^P|8fvEbQH3+w*DrKq|9xMzq2OiZyM=;(?>~4+O|jn zC_Et05oc>e%}w4ye2Fm%RIR??VvofwZS-}BL@X=_4jdHp}FlMhW_IW?Zh`4$z*Wr!IzQHa3^?1|);~VaWmsIcmc6 zJs{k0YW}OpkfdoTtr4?9F6IX6$!>hhA+^y_y@vvA_Gr7u8T+i-< zDX(~W5W{8mfbbM-en&U%{mINU#Q8GA`byo)iLF7rMVU#wXXY`a3ji3m{4;x53216i z`zA8ap?>_}`tQj7-%$K78uR}R$|@C2)qgop$}o=g(jOv0ishl!E(R73N=i0~%S)6+ z1xFP7|H0yt3Z_Re*_#C2m3_X{=zi1C&3CM7e?9-Y5lCtAlA%RFG9PDD=Quw1dfYnZ zdUL)#+m`hKx@PT`r;mIx_RQ6Txbti+&;xQorP;$H=R2r)gPMO9>l+!p*Mt04VH$$M zSLwJ81IFjQ5N!S#;MyBD^IS`2n04kuYbZ2~4%3%tp0jn^**BZQ05ELp zY%yntZ=52s6U5Y93Aao)v~M3y?6h7mZcVGp63pK*d&!TRjW99rUU;@s#3kYB76Bs$|LRwkH>L!0Xe zE=dz1o}phhnOVYZFsajQsRA^}IYZnk9Wehvo>gHPA=TPI?2A`plIm8=F1%QiHx*Zn zi)*Y@)$aXW0v1J|#+R2=$ysooHZ&NoA|Wa}htd`=Eud!(HD7JlT8ug|yeBZmpry(W z)pS>^1$N#nuo3PnK*>Thmaxz4pLcY?PP2r3AlhJ7jw(TI8V#c}>Ym;$iPaw+83L+* z!_QWpYs{UWYcl0u z(&(bT0Q*S_uUX9$jC;Vk%oUXw=A-1I+!c18ij1CiUlP@pfP9}CHAVm{!P6AEJ(7Dn z?}u#}g`Q?`*|*_0Rrnu8{l4PP?yCI28qC~&zlwgLH2AkfQt1?B#3AOQjW&10%@@)Q zDG?`6$8?Nz(-sChL8mRs#3z^uOA>~G=ZIG*mgUibWmgd{a|Tn4nkRK9O^37E(()Q% zPR0#M4e2Q-)>}RSt1^UOCGuv?dn|IT3#oW_$S(YR+jxAzxCD_L25p_dt|^>g+6Kgj zJhC8n)@wY;Y7JI6?wjU$MQU|_Gw*FIC)x~^Eq1k41BjLmr}U>6#_wxP0-2Ka?uK14u5M-lAFSX$K1K{WH!M1&q}((MWWUp#Uhl#n_yT5dFs4X`>vmM& z*1!p0lACUVqp&sZG1GWATvZEENs^0_7Ymwem~PlFN3hTHVBv(sDuP;+8iH07a)s(# z%a7+p1QM)YkS7>kbo${k2N1&*%jFP*7UABJ2d||c!eSXWM*<4(_uD7;1XFDod@cT$ zP>IC%^fbC${^QrUXy$f)yBwY^g@}}kngZKa1US!lAa+D=G4wklukaY8AEW%GL zh40pnuv*6D>9`_e14@wWD^o#JvxYVG-~P)+<)0fW zP()DuJN?O*3+Ab!CP-tGr8S4;JN-Ye^9D%(%8d{vb_pK#S1z)nZzE^ezD&%L6nYbZ z*62>?u)xQe(Akd=e?vZbyb5)MMNS?RheZDHU?HK<9;PBHdC~r{MvF__%T)-9ifM#cR#2~BjVJYbA>xbPyl9yNX zX)iFVvv-lfm`d?tbfh^j*A|nw)RszyD<#e>llO8X zou=q3$1|M@Ob;F|o4H0554`&y9T&QTa3{yn=w0BLN~l;XhoslF-$4KGNUdRe?-lcV zS4_WmftU*XpP}*wFM^oKT!D%_$HMT#V*j;9weoOq0mjbl1271$F)`Q(C z76*PAw3_TE{vntIkd=|(zw)j^!@j ^tV@s0U~V+mu)vv`xgL$Z9NQLnuRdZ;95D|1)!0Aybwv}XCE#xz1k?ZC zxAU)v@!$Sm*?)t2mWrkevNFbILU9&znoek=d7jn*k+~ptQ)6z`h6e4B&g?Q;IK+aH z)X(BH`n2DOS1#{AJD-a?uL)@Vl+`B=6X3gF(BCm>Q(9+?IMX%?CqgpsvK+b_de%Q> zj-GtHKf!t@p2;Gu*~#}kF@Q2HMevg~?0{^cPxCRh!gdg7MXsS}BLtG_a0IY0G1DVm z2F&O-$Dzzc#M~iN`!j38gAn`6*~h~AP=s_gy2-#LMFoNZ0<3q+=q)a|4}ur7F#><%j1lnr=F42Mbti zi-LYs85K{%NP8wE1*r4Mm+ZuZ8qjovmB;f##!E*M{*A(4^~vg!bblYi1M@7tq^L8- zH7tf_70iWXqcSQgENGdEjvLiSLicUi3l0H*sx=K!!HLxDg^K|s1G}6Tam|KBV>%YeU)Q>zxQe;ddnDTWJZ~^g-kNeycQ?u242mZs`i8cP)9qW`cwqk)Jf?Re0=SD=2z;Gafh(^X-=WJ$i7Z9$Pao56bTwb+?p>L3bi9 zP|qi@;H^1iT+qnNHBp~X>dd=Us6v#FPDTQLb9KTk%z{&OWmkx3uY(c6JYyK3w|z#Q zMY%FPv%ZNg#w^NaW6lZBU+}Znwc|KF(+X0RO~Q6*O{T-P*fi@5cPGLnzWMSyoOPe3 z(J;R#q}3?z5Ve%crTPZQFLTW81cNY-finw!LH9wr$(C)p_@v?(y#b-R^Pv!}_#7t+A?pHEUMY zoQZIwSETTKeS!W{H$lyB1^!jn4gTD{_mgG?#l1Hx2h^HrpCXo95f3utP-b&%w80F} zXFs@Jp$lbIL64@gc?k*gJ;OForPaapOH7zNMB60FdNP<*9<@hEXJk9Rt=XhHR-5_$Ck-R?+1py&J3Y9^sBBZuj?GwSzua;C@9)@JZpaI zE?x6{H8@j9P06%K_m%9#nnp0Li;QAt{jf-7X%Pd2jHoI4As-9!UR=h6Rjc z!3{UPWiSeLG&>1V5RlM@;5HhQW_&-wL2?%k@dvRS<+@B6Yaj*NG>qE5L*w~1ATP$D zmWu6(OE=*EHqy{($~U4zjxAwpPn42_%bdH9dMphiUU|) z*+V@lHaf%*GcXP079>vy5na3h^>X=n;xc;VFx)`AJEk zYZFlS#Nc-GIHc}j06;cOU@ zAD7Egkw<2a8TOcfO9jCp4U4oI*`|jpbqMWo(={gG3BjuM3QTGDG`%y|xithFck}0J zG}N#LyhCr$IYP`#;}tdm-7^9=72+CBfBsOZ0lI=LC_a%U@(t3J_I1t(UdiJ^@NubM zvvA0mGvTC%{fj53M^|Ywv$KbW;n8B-x{9}Z!K6v-tw&Xe_D2{7tX?eVk$sA*0826( zuGz!K7$O#;K;1w<38Tjegl)PmRso`fc&>fAT5s z7hzQe-_`lx`}2=c)jz6;yn(~F6#M@z_7@Z(@GWbIAo6A2&;aFf&>CVHpqoPh5#~=G zav`rZ3mSL2qwNL+Pg>aQv;%V&41e|YU$!fQ9Ksle!XZERpjAowHtX zi#0lnw{(zmk&}t`iFEMmx-y7FWaE*vA{Hh&>ieZg{5u0-3@a8BY)Z47E`j-H$dadu zIP|PXw1gjO@%aSz*O{GqZs_{ke|&S6hV{-dPkl*V|3U4LpqhG0eVdqfeNX28hrafI zE13WOsRE|o?24#`gQJs@v*EwL{@3>Ffa;knvI4@VEG2I>t-L(KRS0ShZ9N!bwXa}e zI0}@2#PwFA&Y9o}>6(ZaSaz>kw{U=@;d{|dYJ~lyjh~@bBL>n}#@KjvXUOhrZ`DbnAtf5bz3LD@0RpmAyC-4cgu<7rZo&C3~A_jA*0)v|Ctcdu} zt@c7nQ6hSDC@76c4hI&*v|5A0Mj4eQ4kVb0$5j^*$@psB zdouR@B?l6E%a-9%i(*YWUAhxTQ(b@z&Z#jmIb9`8bZ3Um3UW!@w4%t0#nxsc;*YrG z@x$D9Yj3EiA(-@|IIzi@!E$N)j?gedGJpW!7wr*7zKZwIFa>j|cy<(1`VV_GzWN=1 zc%OO)o*RRobvTZE<9n1s$#V+~5u8ZwmDaysD^&^cxynksn!_ypmx)Mg^8$jXu5lMo zK3K_8GJh#+7HA1rO2AM8cK(#sXd2e?%3h2D9GD7!hxOEKJZK&T`ZS0e*c9c36Y-6yz2D0>Kvqy(EuiQtUQH^~M*HY!$e z20PGLb2Xq{3Ceg^sn+99K6w)TkprP)YyNU(+^PGU8}4&Vdw*u;(`Bw!Um76gL_aMT z>*82nmA8Tp;~hwi0d3S{vCwD};P(%AVaBr=yJ zqB?DktZ#)_VFh_X69lAHQw(ZNE~ZRo2fZOIP;N6fD)J*3u^YGdgwO(HnI4pb$H#9) zizJ<>qI*a6{+z=j+SibowDLKYI*Je2Y>~=*fL@i*f&8**s~4l&B&}$~nwhtbOTr=G zFx>{y6)dpJPqv={_@*!q0=jgw3^j`qi@!wiWiT_$1`SPUgaG&9z9u9=m5C8`GpMaM zyMRSv2llS4F}L?233!)f?mvcYIZ~U z7mPng^=p)@Z*Fp9owSYA`Fe4OjLiJ`rdM`-U(&z1B1`S`ufK_#T@_BvenxDQU`deH$X5eMVO=;I4EJjh6?kkG2oc6AYF6|(t)L0$ukG}Zn=c+R`Oq;nC)W^ z{ek!A?!nCsfd_5>d&ozG%OJmhmnCOtARwOq&p!FzWl7M))YjqK8|;6sOAc$w2%k|E z`^~kpT!j+Y1lvE0B)mc$Ez_4Rq~df#vC-FmW;n#7E)>@kMA6K30!MdiC19qYFnxQ* z?BKegU_6T37%s`~Gi2^ewVbciy-m5%1P3$88r^`xN-+VdhhyUj4Kzg2 zlKZ|FLUHiJCZL8&<=e=F2A!j@3D@_VN%z?J;uw9MquL`V*f^kYTrpoWZ6iFq00uO+ zD~Zwrs!e4cqGedAtYxZ76Bq3Ur>-h(m1~@{x@^*YExmS*vw9!Suxjlaxyk9P#xaZK z)|opA2v#h=O*T42z>Mub2O3Okd3GL86KZM2zlfbS z{Vps`OO&3efvt->OOSpMx~i7J@GsRtoOfQ%vo&jZ6^?7VhBMbPUo-V^Znt%-4k{I# z8&X)=KY{3lXlQg4^FH^{jw0%t#2%skLNMJ}hvvyd>?_AO#MtdvH;M^Y?OUWU6BdMX zJ(h;PM9mlo@i)lWX&#E@d4h zj4Z0Czj{+ipPeW$Qtz_A52HA<4$F9Qe4CiNQSNE2Q-d1OPObk4?7-&`={{yod5Iy3kB=PK3%0oYSr`Gca120>CHbC#SqE*ivL2R(YmI1A|nAT?JmK*2qj_3p#?0h)$#ixdmP?UejCg9%AS2 z8I(=_QP(a(s)re5bu-kcNQc-&2{QZ%KE*`NBx|v%K2?bK@Ihz_e<5Y(o(gQ-h+s&+ zjpV>uj~?rfJ!UW5Mop~ro^|FP3Z`@B6A=@f{Wn78cm`)3&VJ!QE+P9&$;3SDNH>hI z_88;?|LHr%1kTX0t*xzG-6BU=LRpJFZucRBQ<^zy?O5iH$t>o}C}Fc+kM1EZu$hm% zTTFKrJkXmCylFgrA;QAA(fX5Sia5TNo z?=Ujz7$Q?P%kM$RKqRQisOexvV&L+bolR%`u`k;~!o(HqgzV9I6w9|g*5SVZN6+kT9H$-3@%h%k7BBnB zPn+wmPYNG)V2Jv`&$LoI*6d0EO^&Nh`E* z&1V^!!Szd`8_uf%OK?fuj~! z%p9QLJ?V*T^)72<6p1ONqpmD?Wm((40>W?rhjCDOz?#Ei^sXRt|GM3ULLnoa8cABQ zA)gCqJ%Q5J%D&nJqypG-OX1`JLT+d`R^|0KtfGQU+jw79la&$GHTjKF>*8BI z0}l6TC@XB6`>7<&{6WX2kX4k+0SaI`$I8{{mMHB}tVo*(&H2SmZLmW* z+P8N>(r}tR?f!O)?)df>HIu>$U~e~tflVmwk*+B1;TuqJ+q_^`jwGwCbCgSevBqj$ z<`Fj*izeO)_~fq%wZ0Jfvi6<3v{Afz;l5C^C7!i^(W>%5!R=Ic7nm(0gJ~9NOvHyA zqWH2-6w^YmOy(DY{VrN6ErvZREuUMko@lVbdLDq*{A+_%F>!@6Z)X9kR1VI1+Ler+ zLUPtth=u~23=CqZoAbQ`uGE_91kR(8Ie$mq1p`q|ilkJ`Y-ob_=Nl(RF=o7k{47*I)F%_XMBz9uwRH8q1o$TkV@8Pwl zzi`^7i;K6Ak7o58a_D-V0AWp;H8pSjbEs$4BxoJkkC6UF@QNL)0$NU;Wv0*5 z0Ld;6tm7eR%u=`hnUb)gjHbE2cP?qpo3f4w%5qM0J*W_Kl6&z4YKX?iD@=McR!gTyhpGGYj!ljQm@2GL^J70`q~4CzPv@sz`s80FgiuxjAZ zLq61rHv1O>>w1qOEbVBwGu4%LGS!!muKHJ#JjfT>g`aSn>83Af<9gM3XBdY)Yql|{ zUds}u*;5wuus)D>HmexkC?;R&*Z`yB4;k;4T*(823M&52{pOd1yXvPJ3PPK{Zs>6w zztXy*HSH0scZHn7qIsZ8y-zftJ*uIW;%&-Ka0ExdpijI&xInDg-Bv-Q#Islcbz+R! zq|xz?3}G5W@*7jSd`Hv9q^5N*yN=4?Lh=LXS^5KJC=j|AJ5Y(f_fC-c4YQNtvAvn|(uP9@5Co{dL z?7|=jqTzD8>(6Wr&(XYUEzT~-VVErf@|KeFpKjh=v51iDYN_`Kg&XLOIG;ZI8*U$@ zKig{dy?1H}UbW%3jp@7EVSD>6c%#abQ^YfcO(`)*HuvNc|j( zyUbYozBR15$nNU$0ZAE%ivo4viW?@EprUZr6oX=4Sc!-WvrpJdF`3SwopKPyX~F>L zJ>N>v=_plttTSUq6bYu({&rkq)d94m5n~Sk_MO*gY*tlkPFd2m=Pi>MK)ObVV@Sgs zmXMNMvvcAuz+<$GLR2!j4w&;{)HEkxl{$B^*)lUKIn&p5_huD6+%WDoH4`p}9mkw$ zXCPw6Y7tc%rn$o_vy>%UNBC`0@+Ih-#T05AT)ooKt?94^ROI5;6m2pIM@@tdT=&WP z{u09xEVdD}{(3v}8AYUyT82;LV%P%TaJa%f)c36?=90z>Dzk5mF2}Gs0jYCmufihid8(VFcZWs8#59;JCn{!tHu5kSBbm zL`F{COgE01gg-qcP2Lt~M9}mALg@i?TZp&i9ZM^G<3`WSDh}+Ceb3Q!QecJ|N;Xrs z{wH{D8wQ2+mEfBX#M8)-32+~q4MRVr1UaSPtw}`iwx@x=1Xv-?UT{t}w}W(J&WKAC zrZ%hssvf*T!rs}}#atryn?LB=>0U%PLwA9IQZt$$UYrSw`7++}WR7tfE~*Qg)vRrM zT;(1>Zzka?wIIz8vfrG86oc^rjM@P7^i8D~b(S23AoKYj9HBC(6kq9g`1gN@|9^xO z{~h zbxGMHqGZ@eJ17bgES?HQnwp|G#7I>@p~o2zxWkgZUYSUeB*KT{1Q z*J3xZdWt`eBsA}7(bAHNcMPZf_BZC(WUR5B8wUQa=UV^e21>|yp+uop;$+#JwXD!> zunhJVCIKgaol0AM_AwJNl}_k&q|uD?aTE@{Q*&hxZ=k_>jcwp}KwG6mb5J*pV@K+- zj*`r0WuEU_8O=m&1!|rj9FG7ad<2px63;Gl z9lJrXx$~mPnuiqIH&n$jSt*ReG}1_?r4x&iV#3e_z+B4QbhHwdjiGu^J3vcazPi`| zaty}NFSWe=TDry*a*4XB)F;KDI$5i9!!(5p@5ra4*iW;FlGFV0P;OZXF!HCQ!oLm1 zsK+rY-FnJ?+yTBd0}{*Y6su|hul)wJ>RNQ{eau*;wWM{vWM`d0dTC-}Vwx6@cd#P? zx$Qyk^2*+_ZnMC}q0)+hE-q)PKoox#;pc%DNJ&D5+if6X4j~p$A7-s&AjDkSEV)aM z(<3UOw*&f)+^5F0Mpzw3zB1ZHl*B?C~Cx) zuNg*>5RM9F5{EpU@a2E7hAE`m<89wbQ2Lz&?Egu-^sglNXG5Q;{9n(%&*kEb0vApd zRHrY@22=pkFN81%x)~acZeu`yvK zovAVJNykgxqkEr^hZksHkpxm>2I8FTu2%+XLs@?ym0n;;A~X>i32{g6NOB@o4lk8{ zB}7Z2MNAJi>9u=y%s4QUXaNdt@SlAZr54!S6^ETWoik6gw=k-itu_}Yl_M9!l+Rbv z(S&WD`{_|SE@@(|Wp7bq1Zq}mc4JAG?mr2WN~6}~u`7M_F@J9`sr0frzxfuqSF~mA z$m$(TWAuCIE99yLSwi%R)8geQhs;6VBlRhJb(4Cx zu)QIF%_W9+21xI45U>JknBRaZ9nYkgAcK6~E|Zxo!B&z9zQhjsi^fgwZI%K@rYbMq znWBXg1uCZ+ljGJrsW7@x3h2 z;kn!J!bwCeOrBx;oPkZ}FeP%wExyf4=XMp)N8*lct~SyfK~4^-75EZFpHYO5AnuRM z!>u?>Vj3+j=uiHc<=cD~JWRphDSwxFaINB42-{@ZJTWe85>-RcQ&U%?wK)vjz z5u5fJYkck##j(bP7W0*RdW#BmAIK`D3=(U~?b`cJ&U2jHj}?w6 z_4BM)#EoJ6)2?pcR4AqBd)qAUn@RtNQq})FIQoBK4ie+GB(Vih2D|Ds>RJo2zE~C- z7mI)7p)5(-O6JRh6a@VZ5~piVC+Xv=O-)=0eTMSJsRE^c1@bPQWlr}E31VqO-%739 zdcmE{`1m;5LH8w|7euK>>>U#Iod8l1yivC>;YWsg=z#07E%cU9x1yw#3l6AcIm%79 zGi^zH6rM#CZMow(S(8dcOq#5$kbHnQV6s?MRsU3et!!YK5H?OV9vf2qy-UHCn>}2d zTwI(A_fzmmCtE@10yAGgU7R&|Fl$unZJ_^0BgCEDE6(B*SzfkapE9#0N6adc>}dtH zJ#nt^F~@JMJg4=Pv}OdUHyPt-<<9Z&c0@H@^4U?KwZM&6q0XjXc$>K3c&3iXLD9_%(?)?2kmZ=Ykb;)M`Tw=%_d=e@9eheGG zk0<`4so}r={C{zr|6+_1mA_=a56(XyJq||g6Es1E6%fPg#l{r+vk9;)r6VB7D84nu zE0Z1EIxH{Y@}hT+|#$0xn+CdMy6Uhh80eK~nfMEIpM z`|G1v!USmx81nY8XkhEOSWto}pc#{Ut#`Pqb}9j$FpzkQ7`0<-@5D_!mrLah98Mpr zz(R7;ZcaR-$aKqUaO!j z=7QT;Bu0cvYBi+LDfE_WZ`e@YaE_8CCxoRc?Y_!Xjnz~Gl|aYjN2&NtT5v4#q3od2 zkCQZHe#bn(5P#J**Fj4Py%SaaAKJsmV6}F_6Z7V&n6QAu8UQ#9{gkq+tB=VF_Q6~^ zf(hXvhJ#tC(eYm6g|I>;55Lq-;yY*COpTp4?J}hGQ42MIVI9CgEC{3hYw#CZfFKVG zgD(steIg8veyqX%pYMoulq zMUmbj8I`t>mC`!kZ@A>@PYXy*@NprM@e}W2Q+s?XIRM-U1FHVLM~c60(yz1<46-*j zW*FjTnBh$EzI|B|MRU11^McTPIGVJrzozlv$1nah_|t4~u}Ht^S1@V8r@IXAkN;lH z_s|WHlN90k4X}*#neR5bX%}?;G`X!1#U~@X6bbhgDYKJK17~oFF0&-UB#()c$&V<0 z7o~Pfye$P@$)Lj%T;axz+G1L_YQ*#(qO zQND$QTz(~8EF1c3<%;>dAiD$>8j@7WS$G_+ktE|Z?Cx<}HJb=!aChR&4z ziD&FwsiZ)wxS4k6KTLn>d~!DJ^78yb>?Trmx;GLHrbCBy|Bip<@sWdAfP0I~;(Ybr zoc-@j?wA!$ zIP0m3;LZy+>dl#&Ymws@7|{i1+OFLYf@+8+)w}n?mHUBCqg2=-Hb_sBb?=q))N7Ej zDIL9%@xQFOA!(EQmchHiDN%Omrr;WvlPIN5gW;u#ByV)x2aiOd2smy&;vA2+V!u|D zc~K(OVI8} z0t|e0OQ7h23e01O;%SJ}Q#yeDh`|jZR7j-mL(T4E;{w^}2hzmf_6PF|`gWVj{I?^2T3MBK>{?nMXed4kgNox2DP!jvP9v`;pa6AV)OD zDt*Vd-x7s{-;E?E5}3p-V;Y#dB-@c5vTWfS7<=>E+tN$ME`Z7K$px@!%{5{uV`cH80|IzU! zDs9=$%75P^QKCRQ`mW7$q9U?mU@vrFMvx)NNDrI(uk>xwO;^($EUvqVev#{W&GdtR z0ew;Iwa}(-5D28zABlC{WnN{heSY5Eq5Fc=TN^9X#R}0z53!xP85#@;2E=&oNYHyo z46~#Sf!1M1X!rh}ioe`>G2SkPH{5nCoP`GT@}rH;-LP1Q7U_ypw4+lwsqiBql80aA zJE<(88yw$`xzNiSnU(hsyJqHGac<}{Av)x9lQ=&py9djsh0uc}6QkmKN3{P!TEy;P zzLDVQj4>+0r<9B0owxBt5Uz`!M_VSS|{(?`_e+qD9b=vZHoo6>?u;!IP zM7sqoyP>kWY|=v06gkhaGRUrO8n@zE?Yh8$om@8%=1}*!2wdIWsbrCg@;6HfF?TEN z+B_xtSvT6H3in#8e~jvD7eE|LTQhO_>3b823&O_l$R$CFvP@3~)L7;_A}JpgN@ax{ z2d9Ra)~Yh%75wsmHK8e87yAn-ZMiLo6#=<&PgdFsJw1bby-j&3%&4=9dQFltFR(VB z@=6XmyNN4yr^^o$ON8d{PQ=!OX17^CrdM~7D-;ZrC!||<+FEOxI_WI3 zCA<35va%4v>gcEX-@h8esj=a4szW7x z{0g$hwoWRQG$yK{@3mqd-jYiVofJE!Wok1*nV7Gm&Ssq#hFuvj1sRyHg(6PFA5U*Q z8Rx>-blOs=lb`qa{zFy&n4xY;sd$fE+<3EI##W$P9M{B3c3Si9gw^jlPU-JqD~Cye z;wr=XkV7BSv#6}DrsXWFJ3eUNrc%7{=^sP>rp)BWKA9<}^R9g!0q7yWlh;gr_TEOD|#BmGq<@IV;ue zg+D2}cjpp+dPf&Q(36sFU&K8}hA85U61faW&{lB`9HUl-WWCG|<1XANN3JVAkRYvr5U z4q6;!G*MTdSUt*Mi=z_y3B1A9j-@aK{lNvxK%p23>M&=KTCgR!Ee8c?DAO2_R?Bkaqr6^BSP!8dHXxj%N1l+V$_%vzHjq zvu7p@%Nl6;>y*S}M!B=pz=aqUV#`;h%M0rUHfcog>kv3UZAEB*g7Er@t6CF8kHDmK zTjO@rejA^ULqn!`LwrEwOVmHx^;g|5PHm#B6~YD=gjJ!043F+&#_;D*mz%Q60=L9O zve|$gU&~As5^uz@2-BfQ!bW)Khn}G+Wyjw-19qI#oB(RSNydn0t~;tAmK!P-d{b-@ z@E5|cdgOS#!>%#Rj6ynkMvaW@37E>@hJP^82zk8VXx|3mR^JCcWdA|t{0nPmYFOxN z55#^-rlqobcr==<)bi?E?SPymF*a5oDDeSdO0gx?#KMoOd&G(2O@*W)HgX6y_aa6i zMCl^~`{@UR`nMQE`>n_{_aY5nA}vqU8mt8H`oa=g0SyiLd~BxAj2~l$zRSDHxvDs; zI4>+M$W`HbJ|g&P+$!U7-PHX4RAcR0szJ*(e-417=bO2q{492SWrqDK+L3#ChUHtz z*@MP)e^%@>_&#Yk^1|tv@j4%3T)diEXATx4K*hcO`sY$jk#jN5WD<=C3nvuVs zRh||qDHnc~;Kf59zr0;c7VkVSUPD%NnnJC_l3F^#f_rDu8l}l8qcAz0FFa)EAt32I zUy_JLIhU_J^l~FRH&6-iv zSpG2PRqzDdMWft>Zc(c)#tb%wgmWN%>IOPmZi-noqS!^Ft zb81pRcQi`X#UhWK70hy4tGW1mz|+vI8c*h@fFGJtW3r>qV>1Z0r|L>7I3un^gcep$ zAAWfZHRvB|E*kktY$qQP_$YG60C z@X~tTQjB3%@`uz!qxtxF+LE!+=nrS^07hn`EgAp!h|r03h7B!$#OZW#ACD+M;-5J!W+{h z|6I;5cNnE(Y863%1(oH}_FTW})8zYb$7czPg~Szk1+_NTm6SJ0MS_|oSz%e(S~P-& zSFp;!k?uFayytV$8HPwuyELSXOs^27XvK-DOx-Dl!P|28DK6iX>p#Yb%3`A&CG0X2 zS43FjN%IB}q(!hC$fG}yl1y9W&W&I@KTg6@K^kpH8=yFuP+vI^+59|3%Zqnb5lTDAykf9S#X`3N(X^SpdMyWQGOQRjhiwlj!0W-yD<3aEj^ z&X%=?`6lCy~?`&WSWt?U~EKFcCG_RJ(Qp7j=$I%H8t)Z@6Vj zA#>1f@EYiS8MRHZphpMA_5`znM=pzUpBPO)pXGYpQ6gkine{ z6u_o!P@Q+NKJ}k!_X7u|qfpAyIJb$_#3@wJ<1SE2Edkfk9C!0t%}8Yio09^F`YGzp zaJHGk*-ffsn85@)%4@`;Fv^8q(-Wk7r=Q8pT&hD`5(f?M{gfzGbbwh8(}G#|#fDuk z7v1W)5H9wkorE0ZZjL0Q1=NRGY>zwgfm81DdoaVwNH;or{{e zSyybt)m<=zXoA^RALYG-2touH|L*BLvmm9cdMmn+KGopyR@4*=&0 z&4g|FLoreZOhRmh=)R0bg~T2(8V_q7~42-zvb)+y959OAv!V$u(O z3)%Es0M@CRFmG{5sovIq4%8Ahjk#*5w{+)+MWQoJI_r$HxL5km1#6(e@{lK3Udc~n z0@g`g$s?VrnQJ$!oPnb?IHh-1qA`Rz$)Ai<6w$-MJW-gKNvOhL+XMbE7&mFt`x1KY z>k4(!KbbpZ`>`K@1J<(#vVbjx@Z@(6Q}MF#Mnbr-f55)vXj=^j+#)=s+ThMaV~E`B z8V=|W_fZWDwiso8tNMTNse)RNBGi=gVwgg%bOg8>mbRN%7^Um-7oj4=6`$|(K7!+t^90a{$1 z8Z>}<#!bm%ZEFQ{X(yBZMc>lCz0f1I2w9SquGh<9<=AO&g6BZte6hn>Qmvv;Rt)*c zJfTr2=~EnGD8P$v3R|&1RCl&7)b+`=QGapiPbLg_pxm`+HZurtFZ;wZ=`Vk*do~$wBxoW&=j0OTbQ=Q%S8XJ%~qoa3Ea|au5 zo}_(P;=!y z-AjFrERh%8la!z6Fn@lR?^E~H12D? z8#ht=1F;7@o4$Q8GDj;sSC%Jfn01xgL&%F2wG1|5ikb^qHv&9hT8w83+yv&BQXOQy zMVJSBL(Ky~p)gU3#%|blG?I zR9rP^zUbs7rOA0X52Ao=GRt@C&zlyjNLv-}9?*x{y(`509qhCV*B47f2hLrGl^<@S zuRGR!KwHei?!CM10pBKpDIoBNyRuO*>3FU?HjipIE#B~y3FSfOsMfj~F9PNr*H?0o zHyYB^G(YyNh{SxcE(Y-`x5jFMKb~HO*m+R%rq|ic4fzJ#USpTm;X7K+E%xsT_3VHK ze?*uc4-FsILUH;kL>_okY(w`VU*8+l>o>JmiU#?2^`>arnsl#)*R&nf_%>A+qwl%o z{l(u)M?DK1^mf260_oteV3#E_>6Y4!_hhVDM8AI6MM2V*^_M^sQ0dmHu11fy^kOqX zqzps-c5efIKWG`=Es(9&S@K@)ZjA{lj3ea7_MBPk(|hBFRjHVMN!sNUkrB;(cTP)T97M$ z0Dtc&UXSec<+q?y>5=)}S~{Z@ua;1xt@=T5I7{`Z=z_X*no8s>mY;>BvEXK%b`a6(DTS6t&b!vf_z#HM{Uoy z_5fiB(zpkF{})ruka$iX*~pq1ZxD?q68dIoIZSVls9kFGsTwvr4{T_LidcWtt$u{k zJlW7moRaH6+A5hW&;;2O#$oKyEN8kx z`LmG)Wfq4ykh+q{I3|RfVpkR&QH_x;t41UwxzRFXt^E2B$domKT@|nNW`EHwyj>&< zJatrLQ=_3X%vd%nHh^z@vIk(<5%IRAa&Hjzw`TSyVMLV^L$N5Kk_i3ey6byDt)F^U zuM+Ub4*8+XZpnnPUSBgu^ijLtQD>}K;eDpe1bNOh=fvIfk`&B61+S8ND<(KC%>y&? z>opCnY*r5M+!UrWKxv0_QvTlJc>X#AaI^xoaRXL}t5Ej_Z$y*|w*$6D+A?Lw-CO-$ zitm^{2Ct82-<0IW)0KMNvJHgBrdsIR0v~=H?n6^}l{D``Me90`^o|q!olsF?UX3YS zq^6Vu>Ijm>>PaZI8G@<^NGw{Cx&%|PwYrfwR!gX_%AR=L3BFsf8LxI|K^J}deh0Zd zV?$3r--FEX`#INxsOG6_=!v)DI>0q|BxT)z-G6kzA01M?rba+G_mwNMQD1mbVbNTW zmBi*{s_v_Ft9m2Avg!^78(QFu&n6mbRJ2bAv!b;%yo{g*9l2)>tsZJOOp}U~8VUH`}$8p_}t*XIOehezolNa-a2x0BS})Y9}& z*TPgua{Ewn-=wVrmJUeU39EKx+%w%=ixQWKDLpwaNJs65#6o7Ln7~~X+p_o2BR1g~ zVCfxLzxA{HlWAI6^H;`juI=&r1jQrUv_q0Z1Ja-tjdktrrP>GOC*#p?*xfQU5MqjM zsBe!9lh(u8)w$e@Z|>aUHI5o;MGw*|Myiz3-f0;pHg~Q#%*Kx8MxH%AluVXjG2C$) zWL-K63@Q`#y9_k_+}eR(x4~dp7oV-ek0H>Igy8p#i4GN{>#v=pFYUQT(g&b$OeTy- zX_#FDgNF8XyfGY6R!>inYn8IR2RDa&O!(6NIHrC0H+Qpam1bNa=(`SRKjixBTtm&e z`j9porEci!zdlg1RI0Jw#b(_Tb@RQK1Zxr_%7SUeH6=TrXt3J@js`4iDD0=I zoHhK~I7^W8^Rcp~Yaf>2wVe|Hh1bXa_A{oZ9eG$he;_xYvTbTD#moBy zY57-f2Ef1TP^lBi&p5_s7WGG9|0T}dlfxOxXvScJO1Cnq`c`~{Dp;{;l<-KkCDE+p zmexJkd}zCgE{eF=)K``-qC~IT6GcRog_)!X?fK^F8UDz$(zFUrwuR$qro5>qqn>+Z z%<5>;_*3pZ8QM|yv9CAtrAx;($>4l^_$_-L*&?(77!-=zvnCVW&kUcZMb6;2!83si z518Y%R*A3JZ8Is|kUCMu`!vxDgaWjs7^0j(iTaS4HhQ)ldR=r)_7vYFUr%THE}cPF z{0H45FJ5MQW^+W>P+eEX2kLp3zzFe*-pFVAdDZRybv?H|>`9f$AKVjFWJ=wegO7hO zOIYCtd?Vj{EYLT*^gl35|HbMX|NAEUf2ra9dy1=O;figB>La=~eA^#>O6n4?EMugV zbbt{Dbfef5l^(;}5kZ@!XaWwF8z0vUr6r|+QN*|WpF z^*osUHzOnE$lHuWYO$G7>}Y)bY0^9UY4eDV`E{s+{}Z$O$2*lMEYl zTA`ki(<0(Yrm~}15V-E^e2W6`*`%ydED-3G@$UFm6$ZtLx z+av`BhsHcAWqdxPWfu2*%{}|Sptax4_=NpDMeWy$* zZM6__s`enB$~0aT1BU^2k`J9F%+n+lL_|8JklWOCVYt*0%o*j4w1CsB_H^tVpYT_LLyKuyk=CV6~1M<7~^FylL*+AIFf3h>J=x$ygY-BG}4LJ z8XxYPY!v7dO3PVwEoY=`)6krokmR^|Mg5ztX_^#QR}ibr^X-|_St#rtv3gukh0(#A=};NPlNz57ZDFJ9hf#NP50zS)+Fo=StX)i@ zWS?W}i6LjB>kAB~lupAPyIjFb)izFgRq*iS*(Jt509jNr3r72{Gj`5DGoj;J&k5G@Rm!dJ($ox>SbxR)fc zz|Phug;~A7!p@?|mMva@rWuf2fSDK_ZxN3vVmlYz>rrf?LpiNs)^z!y{As@`55JC~ zS*GD3#N-ptY!2<613UelAJ;M4EEI$dm)`8#n$|o{ce^dlyoUY3bsy2hgnj-;ovubb zg2h1rZA6Ot}K_cpYBpIuF&CyK~5R0Wv;kG|3A^8K3nk{rw$Be8u@aos#qvKQKJyVU$cX6biw&Ep#+q7upFX z%qo&`WZ){<%zh@BTl{MO@v9#;t+cb7so0Uz49Fmo1e4>y!vUyIHadguZS0T7-x#_drMXz*16*c zymR0u^`ZQpXN}2ofegbpSedL%F9aypdQcrzjzPlBW0j zMlPzC&ePZ@Cq!?d%9oQNEg0`rHALm8l#lUdXMVEqDvb(AID~H(?H9z!e9G98fG@IzhajKr)3{L_Clu1(Bwg`RM!-(MOuZi zbeDsj9I3(~EITsE=3Z)a|l_rn8W92U0DB70gF7YYfO0j!)h?QobY1lSR>0 z_TVw@$eP~3k8r9;%g%RlZzCJ2%f}DvY`rsZ$;ak&^~-`i%B%+O!pnADeVyV!dHj|} zzOj#q4eRx9Q8c2Z7vy9L&fGLj+3_?fp}+8o`Xpwyi(81H|7P8#65%FIS*lOi={o&v z4NV$xu7az4Nb50dRGZv<tdZCx4Ek<_o3!mAT} zL5l*|K3Qr-)W8paaG z&R6{ped_4e2cy}ejD0!dt{*PaC*^L@eB%(1Fmc%Y#4)~!jF#lCGfj#E??4LG-T;!M z>Uha}f;W>ib_ZL-I7-v9KZQls^G!-JmL^w;=^}?!RXK;m4$#MwI2AH-l7M2-0 zVMK8k^+4+>2S0k^N_40EDa#`7c;2!&3-o6MHsnBfRnq@>E@)=hDulVq-g5SQWDWbt zj6H5?QS2gRZ^Zvbs~cW|8jagJV|;^zqC0e=D1oUsQPJ3MCb+eRGw(XgIY9y8v_tXq z9$(xWntWpx_Uronmvho{JfyYdV{L1N$^s^|-Nj`Ll`lUsiWTjm&8fadUGMXreJGw$ zQ**m+Tj|(XG}DyUKY~2?&9&n6SJ@9VKa9Hcayv{ar^pNr0WHy zP$bQv&8O!vd;GoT!pLwod-42qB^`m!b7nP@YTX}^+1hzA$}LSLh}Ln|?`%8xGMazw z8WT!LoYJ-Aq3=2p6ZSP~uMgSSWv3f`&-I06tU}WhZsA^6nr&r17hjQIZE>^pk=yZ% z06}dfR$85MjWJPq)T?OO(RxoaF+E#4{Z7)i9}Xsb;Nf+dzig61HO;@JX1Lf9)R5j9)Oi6vPL{H z&UQ9ln=$Q8jnh6-t;`hKM6pHftdd?$=1Aq16jty4-TF~`Gx=C&R242uxP{Y@Q~%O3 z*(16@x+vJsbW@^3tzY=-5MHi#(kB};CU%Ep`mVY1j$MAPpYJBB3x$ue`%t}wZ-@CG z(lBv36{2HMjxT)2$n%(UtHo{iW9>4HX4>)%k8QNnzIQYXrm-^M%#Qk%9odbUrZDz1YPdY`2Z4w~p!5tb^m(mUfk}kZ9+EsmenQ)5iwiaulcy zCJ#2o4Dz?@%)aAKfVXYMF;3t@aqNh2tBBlBkCdj`F31b=h93y(46zQ-YK@+zX5qM9 z&=KkN&3@Ptp*>UD$^q-WpG|9O)HBXz{D>p!`a36aPKkgz7uxEo0J>-o+4HHVD9!Hn z${LD0d{tuGsW*wvZoHc8mJroAs(3!FK@~<}Pz1+vY|Gw}Lwfxp{4DhgiQ_SSlV)E| zZWZxYZLu2EB1=g_y@(ieCQC_1?WNA0J0*}eMZfxCCs>oL;?kHdfMcKB+A)Qull$v( z2x6(38utR^-(?DG>d1GyU()8>ih3ud0@r&I$`ZSS<*1n6(76=OmP>r_JuNCdS|-8U zxGKXL1)Lc2kWY@`_kVBt^%7t9FyLVYX(g%a6>j=yURS1!V<9ieT$$5R+yT!I>}jI5 z?fem|T=Jq;BfZmsvqz_Ud*m5;&xE66*o*S22vf-L+MosmUPPA}~wy`kntf8rIeP-m;;{`xe}9E~G7J!PYoVH_$q~NzQab?F8vWUja5BJ!T5%5IpyqI#Dkps0B;gQ*z?c#N>spFw|wRE$gY?y4wQbJ zku2sVLh({KQz6e0yo+X!rV#8n8<;bHWd{ZLL_(*9Oi)&*`LBdGWz>h zx+p`Wi00u#V$f=CcMmEmgFjw+KnbK3`mbaKfoCsB{;Q^oJgj*LWnd_(dk9Kcssbj` z?*g8l`%{*LuY!Ls*|Tm`1Gv-tRparW8q4AK(5pfJFY5>@qO( zcY>pt*na>LlB^&O@YBDnWLE$x7>pMdSmb-?qMh79eB+Wa{)$%}^kX@Z3g>fytppz! zl%>pMD(Yw+5=!UgYHLD69JiJ;YhiGeEyZM$Au{ff;i zCBbNQfO{d!b7z^F732XX&qhEsJA1UZtJjJEIPyDq+F`LeAUU_4`%2aTX#3NG3%W8u zC!7OvlB?QJ4s2#Ok^_8SKcu&pBd}L?vLRT8Kow#xARt`5&Cg=ygYuz>>c z4)+Vv$;<$l=is&E{k&4Lf-Lzq#BHuWc;wDfm4Fbd5Sr!40s{UpKT$kzmUi{V0t1yp zPOf%H8ynE$x@dQ_!+ISaI}#%72UcYm7~|D*(Fp8xiFAj$CmQ4oH3C+Q8W=Y_9Sp|B z+k<%5=y{eW=YvTivV(*KvC?qxo)xqcEU9(Te=?ITts~;xA0Jph-vpd4@Zw#?r2!`? zB3#XtIY^wxrpjJv&(7Xjvm>$TIg2ZC&+^j(gT0R|&4cb)=92-2Hti1`& z=+M;*O%_j3>9zW|3h{0Tfh5i)Fa;clGNJpPRcUmgErzC{B+zACiPHbff3SmsCZ&X; zp=tgI=zW-t(5sXFL8;ITHw0?5FL3+*z5F-KcLN130l=jAU6%F=DClRPrzO|zY+HD`zlZ-)JT}X?2g!o zxg4Ld-mx6&*-N0-MQ(z+zJo8c`B39gf{-h2vqH<=^T&o1Dgd>4BnVht+JwLcrjJl1 zsP!8`>3-rSls07q2i1hScM&x0lQyBbk(U=#3hI7Bkh*kj6H*&^p+J?OMiT_3*vw5R zEl&p|QQHZq6f~TlAeDGy(^BC0vUK?V&#ezC0*#R-h}_8Cw8-*${mVfHssathC8%VA zUE^Qd!;Rvym%|f@?-!sEj|73Vg8!$$zj_QBZAOraF5HCFKl=(Ac|_p%-P;6z<2WSf zz(9jF2x7ZR{w+p)ETCW06PVt0YnZ>gW9^sr&~`%a_7j-Ful~*4=o|&TM@k@Px2z>^ t{*Ed16F~3V5p+(suF-++X8+nHtT~NSfJ>UC3v)>lEpV}<+rIR_{{yMcG_L>v diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d6e308a6..ca025c83 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..f3b75f3b 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +133,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +216,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 119ff48461c03ab3f7657c694a1dbc2e7f9ced5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:49:19 +0000 Subject: [PATCH 301/340] Bump com.gradleup.shadow from 8.3.6 to 8.3.8 (#317) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a401f82a..05ca621d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2" paperweight = "2.0.0-beta.17" -shadow = "8.3.6" +shadow = "8.3.8" folia-scheduler-wrapper = "v0.0.3" errorprone-core = "2.38.0" errorprone-gradle = "4.2.0" From cad76c6559f51c7fb22d36e5fe67bf2e01168702 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:50:13 +0000 Subject: [PATCH 302/340] Bump com.google.errorprone:error_prone_core from 2.38.0 to 2.39.0 (#318) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 05ca621d..413d6fb1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ annotations = "26.0.2" paperweight = "2.0.0-beta.17" shadow = "8.3.8" folia-scheduler-wrapper = "v0.0.3" -errorprone-core = "2.38.0" +errorprone-core = "2.39.0" errorprone-gradle = "4.2.0" [libraries] From 1897790db514d1ef5ba5806f02d6957e596cfd26 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Jul 2025 20:57:24 -0400 Subject: [PATCH 303/340] Update to 1.21.6/1.21.7 (#315) --- common/build.gradle.kts | 1 + .../lishid/openinv/util/JulLoggerAdapter.java | 134 +++++++++++ gradle/libs.versions.toml | 2 + internal/common/build.gradle.kts | 2 +- .../common/container/AnySilentContainer.java | 3 +- .../common/container/OpenInventory.java | 8 +- .../container/slot/ContentCrafting.java | 4 +- .../common/container/slot/ContentCursor.java | 4 +- .../common/container/slot/ContentDrop.java | 6 +- .../common/container/slot/ContentOffHand.java | 4 +- .../placeholder/PlaceholderLoaderBase.java | 15 +- .../slot/placeholder/Placeholders.java | 4 +- .../common/player/BaseOpenPlayer.java | 165 +++++++++++++ .../internal/common/player/OpenPlayer.java | 172 ++----------- .../internal/common/player/PlayerManager.java | 88 +++---- internal/paper1_21_1/build.gradle.kts | 1 + .../paper1_21_1/player/PlayerManager.java | 2 +- internal/paper1_21_3/build.gradle.kts | 1 + internal/paper1_21_4/build.gradle.kts | 1 + .../container/slot/ContentOffHand.java | 4 +- .../paper1_21_4/player/OpenPlayer.java | 3 +- .../paper1_21_4/player/PlayerManager.java | 13 +- internal/paper1_21_5/build.gradle.kts | 26 ++ .../paper1_21_5/InternalAccessor.java | 37 +++ .../PlaceholderLoaderLegacyParse.java | 36 +++ .../paper1_21_5/player/OpenPlayer.java | 54 +++++ .../paper1_21_5/player/PlayerManager.java | 106 ++++++++ internal/spigot/build.gradle.kts | 4 +- .../container/bukkit/OpenPlayerInventory.java | 4 +- .../container/slot/ContentEquipment.java | 4 +- .../internal/reobf/player/OpenPlayer.java | 226 ++++++++++++++++++ .../internal/reobf/player/PlayerManager.java | 39 +-- plugin/build.gradle.kts | 1 + .../lishid/openinv/util/InternalAccessor.java | 29 ++- .../minecraft/items/crafting_table.json | 3 +- .../items/white_stained_glass_pane.json | 3 +- .../openinv-legibility-pack/pack.mcmeta | 4 +- settings.gradle.kts | 1 + 38 files changed, 956 insertions(+), 258 deletions(-) create mode 100644 common/src/main/java/com/lishid/openinv/util/JulLoggerAdapter.java create mode 100644 internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java create mode 100644 internal/paper1_21_5/build.gradle.kts create mode 100644 internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java create mode 100644 internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/container/slot/placeholder/PlaceholderLoaderLegacyParse.java create mode 100644 internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java create mode 100644 internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java create mode 100644 internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 1be01183..3321bb0e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -4,4 +4,5 @@ plugins { dependencies { implementation(project(":openinvapi")) + compileOnly(libs.slf4j.api) } diff --git a/common/src/main/java/com/lishid/openinv/util/JulLoggerAdapter.java b/common/src/main/java/com/lishid/openinv/util/JulLoggerAdapter.java new file mode 100644 index 00000000..f53a3374 --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/util/JulLoggerAdapter.java @@ -0,0 +1,134 @@ +package com.lishid.openinv.util; + +import org.slf4j.Marker; +import org.slf4j.helpers.LegacyAbstractLogger; +import org.slf4j.helpers.MessageFormatter; +import org.slf4j.helpers.NormalizedParameters; +import org.slf4j.spi.LocationAwareLogger; + +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * An adapter for wrapping a {@link java.util.logging.Logger} as a {@link org.slf4j.Logger}. + *
Largely based on {@code JDK14LoggerAdapter}, which is not present at runtime. + */ +public class JulLoggerAdapter extends LegacyAbstractLogger implements LocationAwareLogger { + + private final Logger wrapped; + + public JulLoggerAdapter(Logger wrapped) { + this.wrapped = wrapped; + this.name = wrapped.getName(); + } + + @Override + protected String getFullyQualifiedCallerName() { + return JulLoggerAdapter.class.getName(); + } + + @Override + protected void handleNormalizedLoggingCall( + org.slf4j.event.Level slf4jLevel, + Marker marker, + String msg, + Object[] args, + Throwable thrown + ) { + Level level = fromSlf4jLevel(slf4jLevel); + + if (wrapped.isLoggable(level)) { + normalizedLog(getFullyQualifiedCallerName(), level, msg, args, thrown); + } + } + + private void normalizedLog(String fqcn, Level level, String msg, Object[] args, Throwable thrown) { + String formatted = MessageFormatter.basicArrayFormat(msg, args); + LogRecord logRecord = new LogRecord(level, formatted); + logRecord.setLoggerName(getName()); + logRecord.setThrown(thrown); + + addSource(fqcn, logRecord); + + wrapped.log(logRecord); + } + + private void addSource(String fqcn, LogRecord logRecord) { + // TODO stackwalker? + StackTraceElement[] trace = new Throwable().getStackTrace(); + int maxElements = 12; + int lastIgnored = maxElements; + // Start from 2; 0 is above and 1 is caller of internal method. + for (int i = 2; i < maxElements; ++i) { + if (isIgnored(trace[i].getClassName(), fqcn)) { + lastIgnored = i; + } + } + + if (lastIgnored < maxElements - 1) { + StackTraceElement caller = trace[lastIgnored + 1]; + logRecord.setSourceClassName(caller.getClassName()); + logRecord.setSourceMethodName(caller.getMethodName()); + } + } + + private boolean isIgnored(String className, String fqcn) { + if (className.equals(fqcn)) { + return true; + } + // Ignore slf4j classes - they shouldn't be the source. + if (className.startsWith("org.slf4j.")) { + return true; + } + return className.equals(getFullyQualifiedCallerName()); + } + + @Override + public void log(Marker marker, String callerFqn, int levelInt, String msg, Object[] args, Throwable thrown) { + Level level = fromSlf4jLevel(org.slf4j.event.Level.intToLevel(levelInt)); + + if (!wrapped.isLoggable(level)) { + return; + } + + NormalizedParameters params = NormalizedParameters.normalize(msg, args, thrown); + normalizedLog(callerFqn, level, params.getMessage(), params.getArguments(), params.getThrowable()); + } + + private Level fromSlf4jLevel(org.slf4j.event.Level level) { + return switch (level) { + case TRACE -> Level.FINEST; + case DEBUG -> Level.FINE; + case INFO -> Level.INFO; + case WARN -> Level.WARNING; + case ERROR -> Level.SEVERE; + }; + } + + @Override + public boolean isTraceEnabled() { + return wrapped.isLoggable(Level.FINEST); + } + + @Override + public boolean isDebugEnabled() { + return wrapped.isLoggable(Level.FINE); + } + + @Override + public boolean isInfoEnabled() { + return wrapped.isLoggable(Level.INFO); + } + + @Override + public boolean isWarnEnabled() { + return wrapped.isLoggable(Level.WARNING); + } + + @Override + public boolean isErrorEnabled() { + return wrapped.isLoggable(Level.SEVERE); + } + +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 413d6fb1..a88be9fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,7 @@ shadow = "8.3.8" folia-scheduler-wrapper = "v0.0.3" errorprone-core = "2.39.0" errorprone-gradle = "4.2.0" +slf4j = "2.0.17" [libraries] spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" } @@ -17,6 +18,7 @@ annotations = { module = "org.jetbrains:annotations", version.ref = "annotations folia-scheduler-wrapper = { module = "com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper", version.ref = "folia-scheduler-wrapper" } errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone-core" } errorprone-gradle = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorprone-gradle" } +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } [plugins] paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" } diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 4f926d00..50ca34c0 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) - paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT") } val spigot = tasks.register("spigotRelocations") { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java index da4d8776..4ca16ad4 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/AnySilentContainer.java @@ -27,6 +27,7 @@ import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.Entity; import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.PlayerEnderChestContainer; @@ -92,7 +93,7 @@ public boolean activateContainer( ServerPlayer player = PlayerManager.getHandle(bukkitPlayer); - final net.minecraft.world.level.Level level = player.level(); + final net.minecraft.world.level.Level level = ((Entity) player).level(); final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()); final BlockEntity blockEntity = level.getBlockEntity(blockPos); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index d93e7479..2f8c9825 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -70,8 +70,8 @@ protected void setupSlots() { // If inventory is expected size, we can arrange slots to be pretty. Inventory ownerInv = owner.getInventory(); if (ownerInv.getNonEquipmentItems().size() == 36 - && Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5 - && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) { + && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4 + && (Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5 || Inventory.EQUIPMENT_SLOT_MAPPING.size() == 7)) { // Armor slots: Bottom left. addArmor(36); // Off-hand: Below chestplate. @@ -136,8 +136,8 @@ private int addArmor(int startIndex) { .toArray(EquipmentSlot[]::new); int localIndex = 0; for (int i = sorted.length - 1; i >= 0; --i) { - // Skip off-hand, handled separately. - if (sorted[i] == EquipmentSlot.OFFHAND) { + // Skip off-hand, handled separately. Also skip non-player slots. + if (sorted[i].getType() != EquipmentSlot.Type.HUMANOID_ARMOR) { continue; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java index c2eb9cc6..ee8b370f 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCrafting.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.common.container.slot; import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; -import com.lishid.openinv.internal.common.player.OpenPlayer; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; @@ -35,7 +35,7 @@ public static boolean isAvailable(@NotNull ServerPlayer holder) { // Player must be online and not in creative - since the creative client is (semi-)authoritative, // it ignores changes without extra help, and will delete the item as a result. // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. - return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival(); + return BaseOpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival(); } @Override diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java index 38e7033d..2693698d 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentCursor.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.common.container.slot; import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; -import com.lishid.openinv.internal.common.player.OpenPlayer; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; @@ -64,7 +64,7 @@ private boolean isAvailable() { // Player must be online and not in creative - since the creative client is (semi-)authoritative, // it ignores changes without extra help, and will delete the item as a result. // Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory. - return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival(); + return BaseOpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival(); } @Override diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java index 97d53718..7ed6e700 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentDrop.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.common.container.slot; import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; -import com.lishid.openinv.internal.common.player.OpenPlayer; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.inventory.Slot; @@ -64,14 +64,14 @@ private SlotDrop(Container container, int index, int x, int y) { @Override public ItemStack getOrDefault() { - return OpenPlayer.isConnected(holder.connection) + return BaseOpenPlayer.isConnected(holder.connection) ? Placeholders.drop : Placeholders.blockedOffline; } @Override public boolean mayPlace(@NotNull ItemStack itemStack) { - return OpenPlayer.isConnected(holder.connection); + return BaseOpenPlayer.isConnected(holder.connection); } @Override diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java index 993a5caa..ba1db443 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/ContentOffHand.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot; -import com.lishid.openinv.internal.common.player.OpenPlayer; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -37,7 +37,7 @@ public Slot asSlot(Container container, int slot, int x, int y) { return new SlotEquipment(container, slot, x, y) { @Override public void setChanged() { - if (OpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { + if (BaseOpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { holder.connection.send( new ClientboundContainerSetSlotPacket( holder.inventoryMenu.containerId, diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java index d6f75bb0..4de686d0 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/PlaceholderLoaderBase.java @@ -1,10 +1,12 @@ package com.lishid.openinv.internal.common.container.slot.placeholder; +import com.mojang.serialization.DataResult; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -21,7 +23,6 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Optional; public abstract class PlaceholderLoaderBase { @@ -51,7 +52,7 @@ public void load(@Nullable ConfigurationSection section) throws Exception { Placeholders.BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", Placeholders.BLOCKED_GAME_TYPE.get(GameType.SPECTATOR))); } - private @NotNull ItemStack parse( + protected @NotNull ItemStack parse( @Nullable ConfigurationSection section, @NotNull String path, @NotNull ItemStack defaultStack @@ -67,8 +68,14 @@ public void load(@Nullable ConfigurationSection section) throws Exception { } CompoundTag compoundTag = parseTag(itemText); - Optional parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag); - return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); + DataResult parsed = ItemStack.CODEC.parse(CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE), compoundTag); + ItemStack itemStack; + try { + itemStack = parsed.getOrThrow(); + } catch (Exception e) { + itemStack = null; + } + return itemStack == null ? defaultStack : itemStack; } protected abstract @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java index 451ff3ec..b9fcd7cb 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/slot/placeholder/Placeholders.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.slot.placeholder; -import com.lishid.openinv.internal.common.player.OpenPlayer; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.GameType; @@ -23,7 +23,7 @@ public final class Placeholders { public static @NotNull ItemStack blockedOffline = ItemStack.EMPTY; public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) { - if (!OpenPlayer.isConnected(serverPlayer.connection)) { + if (!BaseOpenPlayer.isConnected(serverPlayer.connection)) { return blockedOffline; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java new file mode 100644 index 00000000..27b5232a --- /dev/null +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java @@ -0,0 +1,165 @@ +package com.lishid.openinv.internal.common.player; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.storage.ValueOutput; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.Set; + +public abstract class BaseOpenPlayer extends CraftPlayer { + + /** + * List of tags to always reset when saving. + * + * @see net.minecraft.world.entity.Entity#saveWithoutId(ValueOutput) + * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(ValueOutput) + * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(ValueOutput) + * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(ValueOutput) + */ + @Unmodifiable + protected static final Set RESET_TAGS = Set.of( + // Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "entered_nether_pos", // Replaces enteredNetherPosition + "enteredNetherPosition", + "respawn", // Replaces SpawnXyz fields as of 1.21.6 + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + "raid_omen_position", + "ender_pearls", + // Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + "current_explosion_impact_pos", + // LivingEntity#addAdditionalSaveData(CompoundTag) + "active_effects", + "sleeping_pos", // Replaces SleepingXyz fields as of 1.21.6 + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain", + "last_hurt_by_player", + "last_hurt_by_player_memory_time", + "last_hurt_by_mob", + "ticks_since_last_hurt_by_mob", + "equipment", + "locator_bar_icon" + ); + + private final PlayerManager manager; + + protected BaseOpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { + super(server, entity); + this.manager = manager; + } + + @Override + public void loadData() { + manager.loadData(server.getServer(), getHandle()); + } + + @Override + public abstract void saveData(); + + @Contract("null -> new") + protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + if (oldData == null) { + return new CompoundTag(); + } + + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.keySet().removeIf( + key -> RESET_TAGS.contains(key) + || key.startsWith("Bukkit") + || (key.startsWith("Paper") && key.length() > 5) + ); + + return oldData; + } + + protected void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @SuppressWarnings("SameParameterValue") @NotNull Class tagType + ) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @Nullable CompoundTag container, + @NotNull String key, + @NotNull Class dataType + ) { + if (container == null) { + return null; + } + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data + ) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + + public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) { + return connection != null && !connection.isDisconnected(); + } + +} diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 61a6a501..99877b65 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -5,85 +5,25 @@ import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.storage.PlayerDataStorage; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueOutput; import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; +import org.slf4j.Logger; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Set; -public class OpenPlayer extends CraftPlayer { +public class OpenPlayer extends BaseOpenPlayer { - /** - * List of tags to always reset when saving. - * - * @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag) - * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - */ - @Unmodifiable - protected static final Set RESET_TAGS = Set.of( - // Entity#saveWithoutId(CompoundTag) - "CustomName", - "CustomNameVisible", - "Silent", - "NoGravity", - "Glowing", - "TicksFrozen", - "HasVisualFire", - "Tags", - "Passengers", - // ServerPlayer#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - // As of 1.21.5 only ender_pearls tag still needs to be reset. Rest are for backwards compatibility. - "warden_spawn_tracker", - "enteredNetherPosition", - "SpawnX", - "SpawnY", - "SpawnZ", - "SpawnForced", - "SpawnAngle", - "SpawnDimension", - "raid_omen_position", - "ender_pearls", - // Player#addAdditionalSaveData(CompoundTag) - "ShoulderEntityLeft", - "ShoulderEntityRight", - "LastDeathLocation", - "current_explosion_impact_pos", // Unnecessary in 1.21.5 - // LivingEntity#addAdditionalSaveData(CompoundTag) - "active_effects", - "SleepingX", - "SleepingY", - "SleepingZ", - "Brain", - "last_hurt_by_player", - "last_hurt_by_player_memory_time", - "last_hurt_by_mob", - "ticks_since_last_hurt_by_mob", - "equipment" - ); - - private final PlayerManager manager; - - protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { - super(server, entity); - this.manager = manager; - } - - @Override - public void loadData() { - manager.loadData(getHandle()); + protected OpenPlayer( + CraftServer server, + ServerPlayer entity, + PlayerManager manager + ) { + super(server, entity, manager); } @Override @@ -93,14 +33,18 @@ public void saveData() { } ServerPlayer player = this.getHandle(); + Logger logger = LogUtils.getLogger(); // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) - try { - PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) { + PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo; - CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); + CompoundTag oldData = isOnline() + ? null + : worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null); CompoundTag playerData = getWritableTag(oldData); - playerData = player.saveWithoutId(playerData); - setExtraData(playerData); + + ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData); + player.saveWithoutId(valueOutput); if (oldData != null) { // Revert certain special data values when offline. @@ -118,80 +62,4 @@ public void saveData() { } } - @Contract("null -> new") - protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { - if (oldData == null) { - return new CompoundTag(); - } - - // Copy old data. This is a deep clone, so operating on it should be safe. - oldData = oldData.copy(); - - // Remove vanilla/server data that is not written every time. - oldData.keySet().removeIf( - key -> RESET_TAGS.contains(key) - || key.startsWith("Bukkit") - || (key.startsWith("Paper") && key.length() > 5) - ); - - return oldData; - } - - private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { - // Revert automatic updates to play timestamps. - copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); - copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); - } - - private void copyValue( - @NotNull CompoundTag source, - @NotNull CompoundTag target, - @NotNull String container, - @NotNull String key, - @SuppressWarnings("SameParameterValue") @NotNull Class tagType - ) { - CompoundTag oldContainer = getTag(source, container, CompoundTag.class); - CompoundTag newContainer = getTag(target, container, CompoundTag.class); - - // New container being null means the server implementation doesn't store this data. - if (newContainer == null) { - return; - } - - // If old tag exists, copy it to new location, removing otherwise. - setTag(newContainer, key, getTag(oldContainer, key, tagType)); - } - - private @Nullable T getTag( - @Nullable CompoundTag container, - @NotNull String key, - @NotNull Class dataType - ) { - if (container == null) { - return null; - } - Tag value = container.get(key); - if (value == null || !dataType.isAssignableFrom(value.getClass())) { - return null; - } - return dataType.cast(value); - } - - private void setTag( - @NotNull CompoundTag container, - @NotNull String key, - @Nullable T data - ) { - if (data == null) { - container.remove(key); - } else { - container.put(key, data); - } - } - - public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) { - return connection != null && !connection.isDisconnected(); - } - } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index ec14f7ef..6b2fedb9 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -3,11 +3,9 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.common.container.OpenEnderChest; import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.util.JulLoggerAdapter; import com.mojang.authlib.GameProfile; -import com.mojang.serialization.Dynamic; import io.papermc.paper.adventure.PaperAdventure; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -15,11 +13,12 @@ import net.minecraft.server.level.ParticleStatus; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.ValueInput; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; @@ -76,7 +75,11 @@ public PlayerManager(@NotNull Logger logger) { @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + if (!(Bukkit.getServer() instanceof CraftServer craftServer)) { + return null; + } + + MinecraftServer server = craftServer.getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); if (worldServer == null) { @@ -90,7 +93,7 @@ public PlayerManager(@NotNull Logger logger) { entity.getAdvancements().stopListening(); // Try to load the player's data. - if (loadData(entity)) { + if (loadData(server, entity)) { // If data is loaded successfully, return the Bukkit entity. return entity.getBukkitEntity(); } @@ -124,7 +127,7 @@ public PlayerManager(@NotNull Logger logger) { ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); try { - injectPlayer(entity); + injectPlayer(server, entity); } catch (IllegalAccessException e) { logger.log( java.util.logging.Level.WARNING, @@ -136,29 +139,30 @@ public PlayerManager(@NotNull Logger logger) { return entity; } - boolean loadData(@NotNull ServerPlayer player) { + protected boolean loadData(@NotNull MinecraftServer server, @NotNull ServerPlayer player) { // See CraftPlayer#loadData - CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return false; - } + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) { + ValueInput loadedData = server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null); - // Read basic data into the player. - player.load(loadedData); - // Also read "extra" data. - player.readAdditionalSaveData(loadedData); - // Game type settings are also loaded separately. - player.loadGameTypes(loadedData); + if (loadedData == null) { + // Exceptions with loading are logged. + return false; + } - // World is not loaded by ServerPlayer#load(CompoundTag) on Paper. - parseWorld(player, loadedData); + // Read basic data into the player. + player.load(loadedData); + // Game type settings are loaded separately. + player.loadGameTypes(loadedData); + + // World is not loaded by ServerPlayer#load(CompoundTag) on Paper. + parseWorld(server, player, loadedData); + } return true; } - protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + private void parseWorld(@NotNull MinecraftServer server, @NotNull ServerPlayer player, @NotNull ValueInput loadedData) { // See PlayerList#placeNewPlayer World bukkitWorld; Optional msbs = loadedData.getLong("WorldUUIDMost"); @@ -167,29 +171,17 @@ protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loa // Modern Bukkit world. bukkitWorld = Bukkit.getServer().getWorld(new UUID(msbs.get(), lsbs.get())); } else { - Optional worldName = loadedData.getString("world"); - if (worldName.isPresent()) { - // Legacy Bukkit world. - bukkitWorld = Bukkit.getServer().getWorld(worldName.get()); - } else { - // Vanilla player data. - DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) - .resultOrPartial(logger::warning) - .map(player.server::getLevel) - // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); - return; - } + bukkitWorld = loadedData.getString("world").map(Bukkit::getWorld).orElse(null); } if (bukkitWorld == null) { - spawnInDefaultWorld(player); + spawnInDefaultWorld(server, player); return; } player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } - protected void spawnInDefaultWorld(ServerPlayer player) { - ServerLevel level = player.server.getLevel(Level.OVERWORLD); + protected void spawnInDefaultWorld(@NotNull MinecraftServer server, @NotNull ServerPlayer player) { + ServerLevel level = server.getLevel(Level.OVERWORLD); if (level != null) { // Adjust player to default spawn (in keeping with Paper handling) when world not found. player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F); @@ -199,24 +191,34 @@ protected void spawnInDefaultWorld(ServerPlayer player) { } } - protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { + protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; } bukkitEntity.setAccessible(true); - bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); + bukkitEntity.set(player, new OpenPlayer(server.server, player, this)); } @Override public @NotNull Player inject(@NotNull Player player) { try { ServerPlayer nmsPlayer = getHandle(player); - if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + if (nmsPlayer.getBukkitEntity() instanceof BaseOpenPlayer openPlayer) { return openPlayer; } - injectPlayer(nmsPlayer); + MinecraftServer server = nmsPlayer.getServer(); + if (server == null) { + if (!(Bukkit.getServer() instanceof CraftServer craftServer)) { + logger.warning(() -> + "Unable to inject ServerPlayer, certain player data may be lost when saving! Server is not a CraftServer: " + + Bukkit.getServer().getClass().getName()); + return player; + } + server = craftServer.getServer(); + } + injectPlayer(server, nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { logger.log( @@ -235,7 +237,7 @@ protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { ) { ServerPlayer player = getHandle(bukkitPlayer); - if (!OpenPlayer.isConnected(player.connection)) { + if (!BaseOpenPlayer.isConnected(player.connection)) { return null; } diff --git a/internal/paper1_21_1/build.gradle.kts b/internal/paper1_21_1/build.gradle.kts index 05fe6f68..3c86f885 100644 --- a/internal/paper1_21_1/build.gradle.kts +++ b/internal/paper1_21_1/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) implementation(project(":openinvadapterpaper1_21_3")) diff --git a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java index bd2ec0fd..61d78002 100644 --- a/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java +++ b/internal/paper1_21_1/src/main/java/com/lishid/openinv/internal/paper1_21_1/player/PlayerManager.java @@ -43,7 +43,7 @@ public PlayerManager(@NotNull Logger logger) { ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo); try { - injectPlayer(entity); + injectPlayer(server, entity); } catch (IllegalAccessException e) { logger.log( java.util.logging.Level.WARNING, diff --git a/internal/paper1_21_3/build.gradle.kts b/internal/paper1_21_3/build.gradle.kts index 2de54978..04b5ec7e 100644 --- a/internal/paper1_21_3/build.gradle.kts +++ b/internal/paper1_21_3/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT") diff --git a/internal/paper1_21_4/build.gradle.kts b/internal/paper1_21_4/build.gradle.kts index 9716b3ed..1a23419f 100644 --- a/internal/paper1_21_4/build.gradle.kts +++ b/internal/paper1_21_4/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_5")) paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") } diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java index e6c798c1..31da3618 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/slot/ContentOffHand.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.paper1_21_4.container.slot; -import com.lishid.openinv.internal.common.player.OpenPlayer; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; @@ -37,7 +37,7 @@ public Slot asSlot(Container container, int slot, int x, int y) { return new SlotEquipment(container, slot, x, y) { @Override public void setChanged() { - if (OpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { + if (BaseOpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) { holder.connection.send( new ClientboundContainerSetSlotPacket( holder.inventoryMenu.containerId, diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java index 1d58c71f..3f1fe082 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/OpenPlayer.java @@ -1,6 +1,5 @@ package com.lishid.openinv.internal.paper1_21_4.player; -import com.lishid.openinv.internal.common.player.PlayerManager; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerPlayer; import org.bukkit.craftbukkit.CraftServer; @@ -8,7 +7,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenPlayer extends com.lishid.openinv.internal.common.player.OpenPlayer { +public class OpenPlayer extends com.lishid.openinv.internal.paper1_21_5.player.OpenPlayer { protected OpenPlayer( CraftServer server, ServerPlayer entity, diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java index 1a6c671b..4172afd7 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/player/PlayerManager.java @@ -3,6 +3,7 @@ import com.mojang.serialization.Dynamic; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; @@ -15,7 +16,7 @@ import java.util.UUID; import java.util.logging.Logger; -public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { +public class PlayerManager extends com.lishid.openinv.internal.paper1_21_5.player.PlayerManager { public PlayerManager(@NotNull Logger logger) { super(logger); @@ -37,19 +38,19 @@ protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loa .resultOrPartial(logger::warning) .map(player.server::getLevel) // If ServerLevel exists, set, otherwise move to spawn. - .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player)); + .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player.server, player)); return; } if (bukkitWorld == null) { - spawnInDefaultWorld(player); + spawnInDefaultWorld(player.server, player); return; } player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); } @Override - protected void spawnInDefaultWorld(ServerPlayer player) { - ServerLevel level = player.server.getLevel(Level.OVERWORLD); + protected void spawnInDefaultWorld(@NotNull MinecraftServer server, @NotNull ServerPlayer player) { + ServerLevel level = server.getLevel(Level.OVERWORLD); if (level != null) { // Adjust player to default spawn (in keeping with Paper handling) when world not found. player.moveTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F); @@ -60,7 +61,7 @@ protected void spawnInDefaultWorld(ServerPlayer player) { } @Override - protected void injectPlayer(ServerPlayer player) throws IllegalAccessException { + protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlayer player) throws IllegalAccessException { if (bukkitEntity == null) { return; } diff --git a/internal/paper1_21_5/build.gradle.kts b/internal/paper1_21_5/build.gradle.kts new file mode 100644 index 00000000..b9f0cf13 --- /dev/null +++ b/internal/paper1_21_5/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + + paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") +} diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java new file mode 100644 index 00000000..2eb73a41 --- /dev/null +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java @@ -0,0 +1,37 @@ +package com.lishid.openinv.internal.paper1_21_5; + +import com.lishid.openinv.internal.paper1_21_5.container.slot.placeholder.PlaceholderLoaderLegacyParse; +import com.lishid.openinv.internal.paper1_21_5.player.PlayerManager; +import com.lishid.openinv.util.lang.LanguageManager; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor { + + private final @NotNull PlayerManager manager; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + super(logger, lang); + manager = new PlayerManager(logger); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public void reload(@NotNull ConfigurationSection config) { + ConfigurationSection placeholders = config.getConfigurationSection("placeholders"); + try { + // Reset placeholders to defaults and try to load configuration. + new PlaceholderLoaderLegacyParse().load(placeholders); + } catch (Exception e) { + logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e); + } + } + +} diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/container/slot/placeholder/PlaceholderLoaderLegacyParse.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/container/slot/placeholder/PlaceholderLoaderLegacyParse.java new file mode 100644 index 00000000..3b11c697 --- /dev/null +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/container/slot/placeholder/PlaceholderLoaderLegacyParse.java @@ -0,0 +1,36 @@ +package com.lishid.openinv.internal.paper1_21_5.container.slot.placeholder; + +import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoader; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class PlaceholderLoaderLegacyParse extends PlaceholderLoader { + + @Override + protected @NotNull ItemStack parse( + @Nullable ConfigurationSection section, + @NotNull String path, + @NotNull ItemStack defaultStack + ) throws Exception { + if (section == null) { + return defaultStack; + } + + String itemText = section.getString(path); + + if (itemText == null) { + return defaultStack; + } + + CompoundTag compoundTag = parseTag(itemText); + Optional parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag); + return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack); + } + +} diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java new file mode 100644 index 00000000..54aa7911 --- /dev/null +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java @@ -0,0 +1,54 @@ +package com.lishid.openinv.internal.paper1_21_5.player; + +import com.lishid.openinv.event.OpenEvents; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; +import com.mojang.logging.LogUtils; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.bukkit.craftbukkit.CraftServer; + +import java.nio.file.Files; +import java.nio.file.Path; + +public class OpenPlayer extends BaseOpenPlayer { + + protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { + super(server, entity, manager); + } + + @Override + public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + + ServerPlayer player = this.getHandle(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try { + PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; + + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null); + CompoundTag playerData = getWritableTag(oldData); + + playerData = player.saveWithoutId(playerData); + + if (oldData != null) { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + } + + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, tempFile, backupFile); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); + } + } + +} diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java new file mode 100644 index 00000000..3fc0fe10 --- /dev/null +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java @@ -0,0 +1,106 @@ +package com.lishid.openinv.internal.paper1_21_5.player; + +import com.mojang.serialization.Dynamic; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.UUID; +import java.util.logging.Logger; + +public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { + + public PlayerManager(@NotNull Logger logger) { + super(logger); + } + + @Override + protected boolean loadData(@NotNull MinecraftServer server, @NotNull ServerPlayer player) { + // See CraftPlayer#loadData + CompoundTag loadedData = server.getPlayerList().playerIo.load(player).orElse(null); + + if (loadedData == null) { + // Exceptions with loading are logged. + return false; + } + + // Read basic data into the player. + player.load(loadedData); + // Game type settings are also loaded separately. + player.loadGameTypes(loadedData); + + // World is not loaded by ServerPlayer#load(CompoundTag) on Paper. + parseWorld(player, loadedData); + + return true; + } + + protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) { + // See PlayerList#placeNewPlayer + World bukkitWorld; + Optional msbs = loadedData.getLong("WorldUUIDMost"); + Optional lsbs = loadedData.getLong("WorldUUIDLeast"); + if (msbs.isPresent() && lsbs.isPresent()) { + // Modern Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(new UUID(msbs.get(), lsbs.get())); + } else { + Optional worldName = loadedData.getString("world"); + if (worldName.isPresent()) { + // Legacy Bukkit world. + bukkitWorld = Bukkit.getServer().getWorld(worldName.get()); + } else { + // Vanilla player data. + DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension"))) + .resultOrPartial(logger::warning) + .map(player.server::getLevel) + // If ServerLevel exists, set, otherwise move to spawn. + .ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player.server, player)); + return; + } + } + if (bukkitWorld == null) { + spawnInDefaultWorld(player.server, player); + return; + } + player.setServerLevel(((CraftWorld) bukkitWorld).getHandle()); + } + + @Override + public @NotNull Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) { + return openPlayer; + } + injectPlayer(nmsPlayer.server, nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); + return player; + } + } + + @Override + protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this)); + } + +} diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 5152c4a5..bbd662ef 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -10,9 +10,9 @@ plugins { apply() apply() -val spigotVer = "1.21.5-R0.1-SNAPSHOT" +val spigotVer = "1.21.7-R0.1-SNAPSHOT" // Used by common adapter to relocate Craftbukkit classes to a versioned package. -rootProject.extra["craftbukkitPackage"] = "v1_21_R4" +rootProject.extra["craftbukkitPackage"] = "v1_21_R5" configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java index a74b16e2..a39e1d2d 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java @@ -4,8 +4,8 @@ import com.lishid.openinv.internal.reobf.container.OpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.player.Inventory; -import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java index ca56e548..d28ac148 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java @@ -6,8 +6,8 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import org.bukkit.craftbukkit.v1_21_R4.CraftEquipmentSlot; -import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R5.CraftEquipmentSlot; +import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.PlayerInventory; import org.jetbrains.annotations.NotNull; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java new file mode 100644 index 00000000..2e22f750 --- /dev/null +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java @@ -0,0 +1,226 @@ +package com.lishid.openinv.internal.reobf.player; + +import com.lishid.openinv.event.OpenEvents; +import com.mojang.logging.LogUtils; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.storage.PlayerDataStorage; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; +import org.bukkit.craftbukkit.v1_21_R5.CraftServer; +import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; +import org.slf4j.Logger; + +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +public class OpenPlayer extends CraftPlayer { + + /** + * List of tags to always reset when saving. + * + * @see net.minecraft.world.entity.Entity#saveWithoutId(ValueOutput) + * @see ServerPlayer#addAdditionalSaveData(ValueOutput) + * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(ValueOutput) + * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(ValueOutput) + */ + @Unmodifiable + protected static final Set RESET_TAGS = Set.of( + // Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "entered_nether_pos", // Replaces enteredNetherPosition + "enteredNetherPosition", + "respawn", // Replaces SpawnXyz fields as of 1.21.6 + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + "raid_omen_position", + "ender_pearls", + // Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + "current_explosion_impact_pos", + // LivingEntity#addAdditionalSaveData(CompoundTag) + "active_effects", + "sleeping_pos", // Replaces SleepingXyz fields as of 1.21.6 + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain", + "last_hurt_by_player", + "last_hurt_by_player_memory_time", + "last_hurt_by_mob", + "ticks_since_last_hurt_by_mob", + "equipment", + "locator_bar_icon" + ); + + private final PlayerManager manager; + + protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) { + super(server, entity); + this.manager = manager; + } + + @Override + public void loadData() { + manager.loadData(getHandle()); + } + + @Override + public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + + ServerPlayer player = this.getHandle(); + Logger logger = LogUtils.getLogger(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) { + PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo; + + ValueInput oldDataInput = isOnline() + ? null + : worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector, player.registryAccess()).orElse(null); + // Grab CompoundTag out of TagValueInput - we can't iterate over the fields via TagValueInput. + CompoundTag oldData = null; + if (oldDataInput != null) { + Field tagValueInputInput = TagValueInput.class.getDeclaredField("input"); + tagValueInputInput.setAccessible(true); + oldData = (CompoundTag) tagValueInputInput.get(oldDataInput); + } + + CompoundTag playerData = getWritableTag(oldData); + + ValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, player.registryAccess()); + Field tagValueOutputOutput = TagValueOutput.class.getDeclaredField("output"); + tagValueOutputOutput.setAccessible(true); + CompoundTag newPlayerData = (CompoundTag) tagValueOutputOutput.get(valueOutput); + // Add existing old data. + newPlayerData.merge(playerData); + playerData = newPlayerData; + + player.saveWithoutId(valueOutput); + + if (oldData != null) { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + } + + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, tempFile, backupFile); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); + } + } + + @Contract("null -> new") + protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { + if (oldData == null) { + return new CompoundTag(); + } + + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.keySet().removeIf( + key -> RESET_TAGS.contains(key) + || key.startsWith("Bukkit") + || (key.startsWith("Paper") && key.length() > 5) + ); + + return oldData; + } + + protected void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { + // Revert automatic updates to play timestamps. + copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); + copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @SuppressWarnings("SameParameterValue") @NotNull Class tagType + ) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @Nullable CompoundTag container, + @NotNull String key, + @NotNull Class dataType + ) { + if (container == null) { + return null; + } + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data + ) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + + public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) { + return connection != null && !connection.isDisconnected(); + } + +} diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 3205da34..0c084bf2 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -3,8 +3,8 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.reobf.container.OpenEnderChest; import com.lishid.openinv.internal.reobf.container.OpenInventory; +import com.lishid.openinv.util.JulLoggerAdapter; import com.mojang.authlib.GameProfile; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -12,16 +12,18 @@ import net.minecraft.server.level.ParticleStatus; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.ValueInput; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_21_R4.CraftServer; -import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_21_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R5.CraftServer; +import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R5.event.CraftEventFactory; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -68,7 +70,11 @@ public PlayerManager(@NotNull Logger logger) { @Override public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + if (!(Bukkit.getServer() instanceof CraftServer craftServer)) { + return null; + } + + MinecraftServer server = craftServer.getServer(); ServerLevel worldServer = server.getLevel(Level.OVERWORLD); if (worldServer == null) { @@ -130,19 +136,20 @@ public PlayerManager(@NotNull Logger logger) { boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData - CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null); - if (loadedData == null) { - // Exceptions with loading are logged by Mojang. - return false; - } + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) { + ValueInput loadedData = player.server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null); - // Read basic data into the player. - player.load(loadedData); - // Also read "extra" data. - player.readAdditionalSaveData(loadedData); - // Game type settings are also loaded separately. - player.loadGameTypes(loadedData); + if (loadedData == null) { + // Exceptions with loading are logged. + return false; + } + + // Read basic data into the player. + player.load(loadedData); + // Game type settings are loaded separately. + player.loadGameTypes(loadedData); + } return true; } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 2e1cf641..eec509a6 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) implementation(project(":openinvadapterpaper1_21_3")) implementation(project(":openinvadapterpaper1_21_1")) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 7fe286f2..dac55cc7 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -64,7 +64,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - Version maxSupported = Version.of(1, 21, 5); + Version maxSupported = Version.of(1, 21, 7); Version minSupported = Version.of(1, 21, 1); // Ensure version is in supported range. @@ -84,9 +84,13 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } // Paper or a Paper fork, can use Mojang-mapped internals. - if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.5 + if (BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported) + && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7 return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); } + if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 5))) { // 1.21.5 + return new com.lishid.openinv.internal.paper1_21_5.InternalAccessor(logger, lang); + } if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) { // 1.21.4 return new com.lishid.openinv.internal.paper1_21_4.InternalAccessor(logger, lang); } @@ -166,18 +170,33 @@ public String getReleasesLink() { if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5 return "Unsupported; upgrade to 1.20.6: https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; } - if (PAPER && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 1))) { // Paper 1.21.1-1.21.3 - return "https://github.com/Jikoo/OpenInv/releases"; - } if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21 return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2"; } + if (!PAPER) { + return getSpigotReleaseLink(); + } + // Paper 1.21.1-1.21.7 + return "https://github.com/Jikoo/OpenInv/releases"; + } + + private String getSpigotReleaseLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) { return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3"; } if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) { return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.6"; } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 4))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.9"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 5))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.11"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 6))) { + return "Unsupported; upgrade to 1.21.7: https://github.com/Jikoo/OpenInv/releases"; + } + return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/crafting_table.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/crafting_table.json index c77347e5..1e24a414 100644 --- a/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/crafting_table.json +++ b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/crafting_table.json @@ -15,5 +15,6 @@ "model": "minecraft:block/crafting_table" }, "property": "minecraft:custom_model_data" - } + }, + "oversized_in_gui": true } diff --git a/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json index 69561da0..59f6d812 100644 --- a/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json +++ b/resource-pack/openinv-legibility-pack/openinv_44/assets/minecraft/items/white_stained_glass_pane.json @@ -15,5 +15,6 @@ "model": "item/white_stained_glass_pane" }, "property": "custom_model_data" - } + }, + "oversized_in_gui": true } diff --git a/resource-pack/openinv-legibility-pack/pack.mcmeta b/resource-pack/openinv-legibility-pack/pack.mcmeta index fd27eeef..9ba4f524 100644 --- a/resource-pack/openinv-legibility-pack/pack.mcmeta +++ b/resource-pack/openinv-legibility-pack/pack.mcmeta @@ -2,12 +2,12 @@ "pack": { "description": "Improve OpenInv's legibility", "pack_format": 55, - "supported_formats": [ 34, 55 ] + "supported_formats": [ 34, 81 ] }, "overlays": { "entries": [ { - "formats": [ 44, 55 ], + "formats": [ 44, 81 ], "directory": "openinv_44" }, { diff --git a/settings.gradle.kts b/settings.gradle.kts index d4a1252c..4e550317 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ if (!java.lang.Boolean.getBoolean("jitpack")) { val internals = listOf( "common", + "paper1_21_5", "paper1_21_4", "paper1_21_3", "paper1_21_1", From 9691d165061aef6f5bcbbe92bdfd685f3b2bf89f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 20:58:24 -0400 Subject: [PATCH 304/340] Bump softprops/action-gh-release from 2.2.2 to 2.3.2 (#316) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.2 to 2.3.2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.2.2...v2.3.2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.3.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 37ebc11a..7b70e493 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -20,7 +20,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.2.2 + uses: softprops/action-gh-release@v2.3.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 4504ba2b99401d56cddaf72ace9ebf43b13cab64 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 1 Jul 2025 20:59:38 -0400 Subject: [PATCH 305/340] Bump version to 5.1.12 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2d8ed732..5748f102 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.12-SNAPSHOT +version=5.1.12 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 642e2875076719ee6306fab95085b84c970d79cb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 1 Jul 2025 21:00:21 -0400 Subject: [PATCH 306/340] Bump version to 5.1.13-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5748f102..7ef7983b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.12 +version=5.1.13-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 34f5319f487d37b8510e83bd2c203e5c51a1d8f3 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 18 Jul 2025 11:33:52 -0400 Subject: [PATCH 307/340] Add 1.21.8 support (#321) * Add 1.21.8 to supported versions * Correct resource pack version range --- internal/common/build.gradle.kts | 2 +- internal/spigot/build.gradle.kts | 2 +- .../main/java/com/lishid/openinv/util/InternalAccessor.java | 6 +++--- resource-pack/openinv-legibility-pack/pack.mcmeta | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 50ca34c0..3d480317 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) - paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT") } val spigot = tasks.register("spigotRelocations") { diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index bbd662ef..25635da0 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -10,7 +10,7 @@ plugins { apply() apply() -val spigotVer = "1.21.7-R0.1-SNAPSHOT" +val spigotVer = "1.21.8-R0.1-SNAPSHOT" // Used by common adapter to relocate Craftbukkit classes to a versioned package. rootProject.extra["craftbukkitPackage"] = "v1_21_R5" diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index dac55cc7..e32cac56 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -64,7 +64,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - Version maxSupported = Version.of(1, 21, 7); + Version maxSupported = Version.of(1, 21, 8); Version minSupported = Version.of(1, 21, 1); // Ensure version is in supported range. @@ -74,7 +74,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { // Load Spigot accessor. if (!PAPER) { - if (BukkitVersions.MINECRAFT.equals(maxSupported)) { + if (BukkitVersions.MINECRAFT.equals(maxSupported) || BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 7))) { // Current Spigot, remapped internals are available. return new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); } else { @@ -85,7 +85,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { // Paper or a Paper fork, can use Mojang-mapped internals. if (BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported) - && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7 + && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7, 1.21.8 return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); } if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 5))) { // 1.21.5 diff --git a/resource-pack/openinv-legibility-pack/pack.mcmeta b/resource-pack/openinv-legibility-pack/pack.mcmeta index 9ba4f524..42ae406a 100644 --- a/resource-pack/openinv-legibility-pack/pack.mcmeta +++ b/resource-pack/openinv-legibility-pack/pack.mcmeta @@ -2,12 +2,12 @@ "pack": { "description": "Improve OpenInv's legibility", "pack_format": 55, - "supported_formats": [ 34, 81 ] + "supported_formats": [ 34, 64 ] }, "overlays": { "entries": [ { - "formats": [ 44, 81 ], + "formats": [ 44, 64 ], "directory": "openinv_44" }, { From 1e26c3331138ff56951ff7c40177e42ded0759b5 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 18 Jul 2025 11:43:15 -0400 Subject: [PATCH 308/340] Bump version to 5.1.13 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7ef7983b..79feac11 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.13-SNAPSHOT +version=5.1.13 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 0267090cb63e68beab193984f24f4b23e1e5f7c4 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 18 Jul 2025 11:43:17 -0400 Subject: [PATCH 309/340] Bump version to 5.1.14-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 79feac11..d641a745 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.13 +version=5.1.14-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 252e93f872b5184ffce6640e014fa8e88ffab295 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 28 Jul 2025 08:55:08 -0400 Subject: [PATCH 310/340] Update for new Paper changes (#324) * Fix potential desync if a plugin triggers a slot update under certain circumstances * Increase visibility to match changes to superclass --- .../common/container/menu/OpenChestMenu.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index 76a48f60..d577d12b 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -364,6 +364,18 @@ public void sendAllDataToRemote() { } } + @Override + public void forceSlot(@NotNull Container container, int slot) { + int slotsIndex = this.findSlot(container, slot).orElse(-1); + if (slotsIndex != -1) { + ItemStack item = this.slots.get(slotsIndex).getItem(); + this.remoteSlots.get(slotsIndex).force(item); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, slotsIndex, item.copy()); + } + } + } + @Override public void broadcastCarriedItem() { ItemStack carried = this.getCarried(); @@ -424,7 +436,7 @@ private void updateDataSlotListeners(int i, int j) { } } - private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + public void triggerSlotListeners(int index, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { ItemStack itemStack1 = this.lastSlots.get(index); if (!ItemStack.matches(itemStack1, itemStack)) { ItemStack itemStack2 = supplier.get(); @@ -436,7 +448,7 @@ private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + public void synchronizeSlotToRemote(int i, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { if (!this.suppressRemoteUpdates) { RemoteSlot slot = this.remoteSlots.get(i); if (!slot.matches(itemStack)) { From ad05f427fdd4bebd0b77023afb15872910ef67e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:36:44 +0000 Subject: [PATCH 311/340] Bump com.google.errorprone:error_prone_core from 2.39.0 to 2.41.0 (#325) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a88be9fa..fdb85355 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ annotations = "26.0.2" paperweight = "2.0.0-beta.17" shadow = "8.3.8" folia-scheduler-wrapper = "v0.0.3" -errorprone-core = "2.39.0" +errorprone-core = "2.41.0" errorprone-gradle = "4.2.0" slf4j = "2.0.17" From 70ae4321586a3e276748bc099535bc6f718541ca Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 7 Aug 2025 09:06:35 -0400 Subject: [PATCH 312/340] Fix missing override annotations --- .../openinv/internal/common/container/menu/OpenChestMenu.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index d577d12b..0cae6c7c 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -436,6 +436,7 @@ private void updateDataSlotListeners(int i, int j) { } } + @Override public void triggerSlotListeners(int index, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { ItemStack itemStack1 = this.lastSlots.get(index); if (!ItemStack.matches(itemStack1, itemStack)) { @@ -448,6 +449,7 @@ public void triggerSlotListeners(int index, @NotNull ItemStack itemStack, @NotNu } } + @Override public void synchronizeSlotToRemote(int i, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { if (!this.suppressRemoteUpdates) { RemoteSlot slot = this.remoteSlots.get(i); From 99f9099981024494ac91dac4be2018a64f80b069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:19:21 +0000 Subject: [PATCH 313/340] Bump org.jetbrains:annotations from 26.0.2 to 26.0.2-1 (#329) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fdb85355..2f4f5047 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ spigotapi = "1.21.5-R0.1-SNAPSHOT" specialsource = "1.11.5" planarwrappers = "3.3.0" -annotations = "26.0.2" +annotations = "26.0.2-1" paperweight = "2.0.0-beta.17" shadow = "8.3.8" folia-scheduler-wrapper = "v0.0.3" From 0ee3f8c1b251dc8ea1cee717640b70a2271008e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:15:47 +0000 Subject: [PATCH 314/340] Bump com.gradleup.shadow from 8.3.8 to 9.1.0 (#331) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2f4f5047..c9f6d024 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2-1" paperweight = "2.0.0-beta.17" -shadow = "8.3.8" +shadow = "9.1.0" folia-scheduler-wrapper = "v0.0.3" errorprone-core = "2.41.0" errorprone-gradle = "4.2.0" From e8c8e625c0c73fe5039d56657a95c636f2885f4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 09:10:37 -0400 Subject: [PATCH 315/340] Bump actions/checkout from 4 to 5 (#327) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/external_release.yml | 2 +- .github/workflows/resource_pack_ci.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 002311a8..dddd3084 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: diff --git a/.github/workflows/external_release.yml b/.github/workflows/external_release.yml index 593239d9..2ad596e3 100644 --- a/.github/workflows/external_release.yml +++ b/.github/workflows/external_release.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 diff --git a/.github/workflows/resource_pack_ci.yml b/.github/workflows/resource_pack_ci.yml index 4b209ef8..f4f11e09 100644 --- a/.github/workflows/resource_pack_ci.yml +++ b/.github/workflows/resource_pack_ci.yml @@ -13,7 +13,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build resource pack id: upload-resource-pack From 58be5cffc99c0d1021fadcfac449c4912e69a3c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:14:45 +0000 Subject: [PATCH 316/340] Bump actions/setup-java from 4 to 5 (#330) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dddd3084..9eb3b0d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' From 9e37b42c6304d7ce0377e1ad54e637ee052ce63a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:08:58 -0400 Subject: [PATCH 317/340] Bump actions/download-artifact from 4 to 5 (#328) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 7b70e493..e9b91355 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download build - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: dist path: dist From 80585dfc07a8e04c7afd99f457f0de7819a1841c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:24:53 +0000 Subject: [PATCH 318/340] Bump com.google.errorprone:error_prone_core from 2.41.0 to 2.42.0 (#338) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9f6d024..5972dd8c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ annotations = "26.0.2-1" paperweight = "2.0.0-beta.17" shadow = "9.1.0" folia-scheduler-wrapper = "v0.0.3" -errorprone-core = "2.41.0" +errorprone-core = "2.42.0" errorprone-gradle = "4.2.0" slf4j = "2.0.17" From 20cc6e004e39ebe8ec6d3c76bc704cc45eff34e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:25:00 +0000 Subject: [PATCH 319/340] Bump com.gradleup.shadow from 9.1.0 to 9.2.2 (#337) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5972dd8c..3d5381dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ specialsource = "1.11.5" planarwrappers = "3.3.0" annotations = "26.0.2-1" paperweight = "2.0.0-beta.17" -shadow = "9.1.0" +shadow = "9.2.2" folia-scheduler-wrapper = "v0.0.3" errorprone-core = "2.42.0" errorprone-gradle = "4.2.0" From 13621901ade8d248e973de736ddb0d92a0204b4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 08:49:37 -0400 Subject: [PATCH 320/340] Bump softprops/action-gh-release from 2.3.2 to 2.3.3 (#335) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.2 to 2.3.3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.3.2...v2.3.3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.3.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index e9b91355..e5ebc0d2 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -20,7 +20,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.3.2 + uses: softprops/action-gh-release@v2.3.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From d80b8a6f4d44c8fd3d16d1e3e8dcf9e65bc77396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:03:22 -0400 Subject: [PATCH 321/340] Bump gradle/actions from 4 to 5 (#334) Bumps [gradle/actions](https://github.com/gradle/actions) from 4 to 5. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/v4...v5) --- updated-dependencies: - dependency-name: gradle/actions dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb3b0d2..6e5b669f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: restore-keys: | ${{ runner.os }}-buildtools- - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 - name: Build with Gradle run: ./gradlew clean build From 606395936264f7a875e91f0423b0e479bae66cb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:03:50 -0400 Subject: [PATCH 322/340] Bump actions/github-script from 7 to 8 (#336) Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/automerge_dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/automerge_dependabot.yml b/.github/workflows/automerge_dependabot.yml index 5b406454..6fe77258 100644 --- a/.github/workflows/automerge_dependabot.yml +++ b/.github/workflows/automerge_dependabot.yml @@ -15,7 +15,7 @@ jobs: # Note: this is directly from GitHub's example for using data from a triggering workflow: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow - name: 'Download artifact' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ From 54f9d30a89f1885171f414292f203a0ce4d80a34 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 8 Oct 2025 15:00:13 -0400 Subject: [PATCH 323/340] Update to 1.21.9/1.21.10 (#339) --- .github/workflows/draft_release.yml | 2 +- internal/common/build.gradle.kts | 2 +- .../common/container/BaseOpenInventory.java | 335 ++++++++++++++++++ .../common/container/OpenInventory.java | 332 +---------------- .../container/bukkit/OpenPlayerInventory.java | 8 +- .../bukkit/OpenPlayerInventorySelf.java | 4 +- .../container/menu/OpenInventoryMenu.java | 10 +- .../common/player/BaseOpenPlayer.java | 6 +- .../internal/common/player/OpenPlayer.java | 2 +- .../internal/common/player/PlayerManager.java | 36 +- internal/paper1_21_1/build.gradle.kts | 1 + internal/paper1_21_3/build.gradle.kts | 1 + internal/paper1_21_4/build.gradle.kts | 1 + .../paper1_21_4/container/OpenInventory.java | 2 +- internal/paper1_21_5/build.gradle.kts | 1 + .../paper1_21_5/InternalAccessor.java | 2 +- .../paper1_21_5/player/PlayerManager.java | 2 +- internal/paper1_21_8/build.gradle.kts | 26 ++ .../paper1_21_8/InternalAccessor.java | 31 ++ .../paper1_21_8/container/OpenInventory.java | 43 +++ .../paper1_21_8/player/OpenPlayer.java | 66 ++++ .../paper1_21_8/player/PlayerManager.java | 100 ++++++ internal/spigot/build.gradle.kts | 4 +- .../container/bukkit/OpenPlayerInventory.java | 4 +- .../container/slot/ContentEquipment.java | 4 +- .../internal/reobf/player/OpenPlayer.java | 18 +- .../internal/reobf/player/PlayerManager.java | 16 +- plugin/build.gradle.kts | 1 + .../lishid/openinv/util/InternalAccessor.java | 18 +- .../openinv-legibility-pack/pack.mcmeta | 10 +- settings.gradle.kts | 1 + 31 files changed, 694 insertions(+), 395 deletions(-) create mode 100644 internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java create mode 100644 internal/paper1_21_8/build.gradle.kts create mode 100644 internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/InternalAccessor.java create mode 100644 internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java create mode 100644 internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java create mode 100644 internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/PlayerManager.java diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index e5ebc0d2..01ae259f 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -27,7 +27,7 @@ jobs: name: OpenInv ${{ env.VERSIONED_NAME }} body: |- ## Supported server versions - **Paper:** TODO VERSION, 1.21.4, 1.21.3, 1.21.1 + **Paper:** TODO VERSION, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.1 **Spigot:** TODO VERSION TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE. diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 3d480317..1e14a71b 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) - paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") } val spigot = tasks.register("spigotRelocations") { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java new file mode 100644 index 00000000..cb68d888 --- /dev/null +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java @@ -0,0 +1,335 @@ +package com.lishid.openinv.internal.common.container; + +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory; +import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu; +import com.lishid.openinv.internal.common.container.slot.Content; +import com.lishid.openinv.internal.common.container.slot.ContentCrafting; +import com.lishid.openinv.internal.common.container.slot.ContentCraftingResult; +import com.lishid.openinv.internal.common.container.slot.ContentCursor; +import com.lishid.openinv.internal.common.container.slot.ContentDrop; +import com.lishid.openinv.internal.common.container.slot.ContentEquipment; +import com.lishid.openinv.internal.common.container.slot.ContentList; +import com.lishid.openinv.internal.common.container.slot.ContentOffHand; +import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; +import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; +import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; +import com.lishid.openinv.internal.common.player.PlayerManager; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public abstract class BaseOpenInventory implements Container, InternalOwned, ISpecialPlayerInventory { + + protected final List slots; + private final int size; + protected ServerPlayer owner; + private int maxStackSize = 99; + protected CraftInventory bukkitEntity; + public List transaction = new ArrayList<>(); + + public BaseOpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { + owner = PlayerManager.getHandle(bukkitPlayer); + + // Get total size, rounding up to nearest 9 for client compatibility. + int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; + size = ((int) Math.ceil(rawSize / 9.0)) * 9; + + slots = NonNullList.withSize(size, new ContentViewOnly(owner)); + setupSlots(); + } + + protected void setupSlots() { + // Top of inventory: Regular contents. + int nextIndex = addMainInventory(); + + // If inventory is expected size, we can arrange slots to be pretty. + Inventory ownerInv = owner.getInventory(); + if (ownerInv.getNonEquipmentItems().size() == 36 + && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4 + && (Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5 || Inventory.EQUIPMENT_SLOT_MAPPING.size() == 7)) { + // Armor slots: Bottom left. + addArmor(36); + // Off-hand: Below chestplate. + addOffHand(46); + // Drop slot: Bottom right. + slots.set(53, new ContentDrop(owner)); + // Cursor slot: Above drop. + slots.set(44, new ContentCursor(owner)); + + // Crafting is displayed in the bottom right corner. + // As we're using the pretty view, this is a 3x2. + addCrafting(41, true); + return; + } + + // Otherwise we'll just add elements linearly. + nextIndex = addArmor(nextIndex); + nextIndex = addOffHand(nextIndex); + nextIndex = addCrafting(nextIndex, false); + slots.set(nextIndex, new ContentCursor(owner)); + // Drop slot last. + slots.set(slots.size() - 1, new ContentDrop(owner)); + } + + private int addMainInventory() { + int listSize = owner.getInventory().getNonEquipmentItems().size(); + // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, + // so everything else needs to move up a row. + int hotbarDiff = listSize - 9; + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + InventoryType.SlotType type; + int invIndex; + if (localIndex < hotbarDiff) { + invIndex = localIndex + 9; + type = InventoryType.SlotType.CONTAINER; + } else { + type = InventoryType.SlotType.QUICKBAR; + invIndex = localIndex - hotbarDiff; + } + + slots.set( + localIndex, + new ContentList(owner, invIndex, type) { + @Override + public void setHolder(@NotNull ServerPlayer holder) { + items = holder.getInventory().getNonEquipmentItems(); + } + } + ); + } + return listSize; + } + + private int addArmor(int startIndex) { + // Armor slots go bottom to top; boots are first and helmet is last. + // Since we have to display horizontally due to space restrictions, + // making the left side the "top" is more user-friendly. + EquipmentSlot[] sorted = Inventory.EQUIPMENT_SLOT_MAPPING.int2ObjectEntrySet() + .stream() + .sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey)) + .map(Map.Entry::getValue) + .toArray(EquipmentSlot[]::new); + int localIndex = 0; + for (int i = sorted.length - 1; i >= 0; --i) { + // Skip off-hand, handled separately. Also skip non-player slots. + if (sorted[i].getType() != EquipmentSlot.Type.HUMANOID_ARMOR) { + continue; + } + + slots.set(startIndex + localIndex, new ContentEquipment(owner, sorted[i])); + ++localIndex; + } + + return startIndex + localIndex; + } + + private int addOffHand(int startIndex) { + // No off-hand? + if (!Inventory.EQUIPMENT_SLOT_MAPPING.containsValue(EquipmentSlot.OFFHAND)) { + return startIndex; + } + + slots.set(startIndex, new ContentOffHand(owner)); + return startIndex + 1; + } + + private int addCrafting(int startIndex, boolean pretty) { + int listSize = owner.inventoryMenu.getCraftSlots().getContents().size(); + pretty &= listSize == 4; + + for (int localIndex = 0; localIndex < listSize; ++localIndex) { + // Pretty display is a 2x2 rather than linear. + // If index is in top row, grid is not 2x2, or pretty is disabled, just use current index. + // Otherwise, subtract 2 and add 9 to start in the same position on the next row. + int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); + + slots.set(modIndex, new ContentCrafting(owner, localIndex)); + } + + if (pretty) { + slots.set(startIndex + 2, new ContentViewOnly(owner) { + @Override + public Slot asSlot(Container container, int slot, int x, int y) { + return new SlotViewOnly(container, slot, x, y) { + @Override + public ItemStack getOrDefault() { + return Placeholders.craftingOutput; + } + }; + } + } + ); + slots.set(startIndex + 11, getCraftingResult(owner)); + } + + return startIndex + listSize; + } + + protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) { + return new ContentCraftingResult(serverPlayer); + } + + public Slot getMenuSlot(int index, int x, int y) { + return slots.get(index).asSlot(this, index, x, y); + } + + public InventoryType.SlotType getSlotType(int index) { + return slots.get(index).getSlotType(); + } + + public abstract Component getTitle(ServerPlayer player); + + @Override + public ServerPlayer getOwnerHandle() { + return owner; + } + + @Override + public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { + if (bukkitEntity == null) { + bukkitEntity = new OpenPlayerInventory(this); + } + return bukkitEntity; + } + + @Override + public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { + ServerPlayer newOwner = PlayerManager.getHandle(player); + // Only transfer regular inventory - crafting and cursor slots are transient. + newOwner.getInventory().replaceWith(owner.getInventory()); + owner = newOwner; + // Update slots to point to new inventory. + slots.forEach(slot -> slot.setHolder(newOwner)); + } + + @Override + public boolean isInUse() { + return !transaction.isEmpty(); + } + + @Override + public @NotNull org.bukkit.entity.Player getPlayer() { + return getOwner(); + } + + @Override + public int getContainerSize() { + return size; + } + + @Override + public boolean isEmpty() { + return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty); + } + + @Override + public @NotNull ItemStack getItem(int index) { + return slots.get(index).get(); + } + + @Override + public @NotNull ItemStack removeItem(int index, int amount) { + return slots.get(index).removePartial(amount); + } + + @Override + public @NotNull ItemStack removeItemNoUpdate(int index) { + return slots.get(index).remove(); + } + + @Override + public void setItem(int index, @NotNull ItemStack itemStack) { + slots.get(index).set(itemStack); + } + + @Override + public int getMaxStackSize() { + return maxStackSize; + } + + @Override + public void setMaxStackSize(int maxStackSize) { + this.maxStackSize = maxStackSize; + } + + @Override + public void setChanged() { + } + + @Override + public boolean stillValid(@NotNull Player player) { + return true; + } + + @Override + public @NotNull List getContents() { + NonNullList contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); + for (int i = 0; i < getContainerSize(); ++i) { + contents.set(i, getItem(i)); + } + return contents; + } + + @Override + public void onOpen(@NotNull CraftHumanEntity viewer) { + transaction.add(viewer); + } + + @Override + public void onClose(@NotNull CraftHumanEntity viewer) { + transaction.remove(viewer); + } + + @Override + public @NotNull List getViewers() { + return transaction; + } + + @Override + public @NotNull org.bukkit.entity.Player getOwner() { + return owner.getBukkitEntity(); + } + + @Override + public Location getLocation() { + return owner.getBukkitEntity().getLocation(); + } + + @Override + public void clearContent() { + owner.getInventory().clearContent(); + owner.inventoryMenu.getCraftSlots().clearContent(); + owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots()); + owner.containerMenu.setCarried(ItemStack.EMPTY); + } + + public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + if (player instanceof ServerPlayer serverPlayer) { + return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); + } + return null; + } + +} diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 2f8c9825..79f48fd1 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -1,207 +1,22 @@ package com.lishid.openinv.internal.common.container; -import com.lishid.openinv.internal.ISpecialPlayerInventory; -import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory; -import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu; -import com.lishid.openinv.internal.common.container.slot.Content; -import com.lishid.openinv.internal.common.container.slot.ContentCrafting; -import com.lishid.openinv.internal.common.container.slot.ContentCraftingResult; -import com.lishid.openinv.internal.common.container.slot.ContentCursor; -import com.lishid.openinv.internal.common.container.slot.ContentDrop; -import com.lishid.openinv.internal.common.container.slot.ContentEquipment; -import com.lishid.openinv.internal.common.container.slot.ContentList; -import com.lishid.openinv.internal.common.container.slot.ContentOffHand; -import com.lishid.openinv.internal.common.container.slot.ContentViewOnly; -import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; -import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders; -import com.lishid.openinv.internal.common.player.PlayerManager; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.ChatFormatting; -import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FontDescription; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.Location; -import org.bukkit.craftbukkit.entity.CraftHumanEntity; -import org.bukkit.craftbukkit.inventory.CraftInventory; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryType; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; +public class OpenInventory extends BaseOpenInventory { -public class OpenInventory implements Container, InternalOwned, ISpecialPlayerInventory { - - protected final List slots; - private final int size; - protected ServerPlayer owner; - private int maxStackSize = 99; - protected CraftInventory bukkitEntity; - public List transaction = new ArrayList<>(); - - public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { - owner = PlayerManager.getHandle(bukkitPlayer); - - // Get total size, rounding up to nearest 9 for client compatibility. - int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1; - size = ((int) Math.ceil(rawSize / 9.0)) * 9; - - slots = NonNullList.withSize(size, new ContentViewOnly(owner)); - setupSlots(); - } - - protected void setupSlots() { - // Top of inventory: Regular contents. - int nextIndex = addMainInventory(); - - // If inventory is expected size, we can arrange slots to be pretty. - Inventory ownerInv = owner.getInventory(); - if (ownerInv.getNonEquipmentItems().size() == 36 - && owner.inventoryMenu.getCraftSlots().getContainerSize() == 4 - && (Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5 || Inventory.EQUIPMENT_SLOT_MAPPING.size() == 7)) { - // Armor slots: Bottom left. - addArmor(36); - // Off-hand: Below chestplate. - addOffHand(46); - // Drop slot: Bottom right. - slots.set(53, new ContentDrop(owner)); - // Cursor slot: Above drop. - slots.set(44, new ContentCursor(owner)); - - // Crafting is displayed in the bottom right corner. - // As we're using the pretty view, this is a 3x2. - addCrafting(41, true); - return; - } - - // Otherwise we'll just add elements linearly. - nextIndex = addArmor(nextIndex); - nextIndex = addOffHand(nextIndex); - nextIndex = addCrafting(nextIndex, false); - slots.set(nextIndex, new ContentCursor(owner)); - // Drop slot last. - slots.set(slots.size() - 1, new ContentDrop(owner)); - } - - private int addMainInventory() { - int listSize = owner.getInventory().getNonEquipmentItems().size(); - // Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory, - // so everything else needs to move up a row. - int hotbarDiff = listSize - 9; - for (int localIndex = 0; localIndex < listSize; ++localIndex) { - InventoryType.SlotType type; - int invIndex; - if (localIndex < hotbarDiff) { - invIndex = localIndex + 9; - type = InventoryType.SlotType.CONTAINER; - } else { - type = InventoryType.SlotType.QUICKBAR; - invIndex = localIndex - hotbarDiff; - } - - slots.set( - localIndex, - new ContentList(owner, invIndex, type) { - @Override - public void setHolder(@NotNull ServerPlayer holder) { - items = holder.getInventory().getNonEquipmentItems(); - } - } - ); - } - return listSize; - } - - private int addArmor(int startIndex) { - // Armor slots go bottom to top; boots are first and helmet is last. - // Since we have to display horizontally due to space restrictions, - // making the left side the "top" is more user-friendly. - EquipmentSlot[] sorted = Inventory.EQUIPMENT_SLOT_MAPPING.int2ObjectEntrySet() - .stream() - .sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey)) - .map(Map.Entry::getValue) - .toArray(EquipmentSlot[]::new); - int localIndex = 0; - for (int i = sorted.length - 1; i >= 0; --i) { - // Skip off-hand, handled separately. Also skip non-player slots. - if (sorted[i].getType() != EquipmentSlot.Type.HUMANOID_ARMOR) { - continue; - } - - slots.set(startIndex + localIndex, new ContentEquipment(owner, sorted[i])); - ++localIndex; - } - - return startIndex + localIndex; - } - - private int addOffHand(int startIndex) { - // No off-hand? - if (!Inventory.EQUIPMENT_SLOT_MAPPING.containsValue(EquipmentSlot.OFFHAND)) { - return startIndex; - } - - slots.set(startIndex, new ContentOffHand(owner)); - return startIndex + 1; - } - - private int addCrafting(int startIndex, boolean pretty) { - int listSize = owner.inventoryMenu.getCraftSlots().getContents().size(); - pretty &= listSize == 4; - - for (int localIndex = 0; localIndex < listSize; ++localIndex) { - // Pretty display is a 2x2 rather than linear. - // If index is in top row, grid is not 2x2, or pretty is disabled, just use current index. - // Otherwise, subtract 2 and add 9 to start in the same position on the next row. - int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7); - - slots.set(modIndex, new ContentCrafting(owner, localIndex)); - } - - if (pretty) { - slots.set(startIndex + 2, new ContentViewOnly(owner) { - @Override - public Slot asSlot(Container container, int slot, int x, int y) { - return new SlotViewOnly(container, slot, x, y) { - @Override - public ItemStack getOrDefault() { - return Placeholders.craftingOutput; - } - }; - } - } - ); - slots.set(startIndex + 11, getCraftingResult(owner)); - } - - return startIndex + listSize; - } - - protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) { - return new ContentCraftingResult(serverPlayer); - } - - public Slot getMenuSlot(int index, int x, int y) { - return slots.get(index).asSlot(this, index, x, y); - } - - public InventoryType.SlotType getSlotType(int index) { - return slots.get(index).getSlotType(); + public OpenInventory(@NotNull Player bukkitPlayer) { + super(bukkitPlayer); } + @Override public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { MutableComponent component = Component.empty(); // Prefix for use with custom bitmap image fonts. @@ -209,13 +24,13 @@ public InventoryType.SlotType getSlotType(int index) { component.append( Component.translatableWithFallback("openinv.container.inventory.self", "") .withStyle(style -> style - .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) .withColor(ChatFormatting.WHITE))); } else { component.append( Component.translatableWithFallback("openinv.container.inventory.other", "") .withStyle(style -> style - .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) .withColor(ChatFormatting.WHITE))); } // Normal title: "Inventory - OwnerName" @@ -225,135 +40,4 @@ public InventoryType.SlotType getSlotType(int index) { return component; } - @Override - public ServerPlayer getOwnerHandle() { - return owner; - } - - @Override - public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() { - if (bukkitEntity == null) { - bukkitEntity = new OpenPlayerInventory(this); - } - return bukkitEntity; - } - - @Override - public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) { - ServerPlayer newOwner = PlayerManager.getHandle(player); - // Only transfer regular inventory - crafting and cursor slots are transient. - newOwner.getInventory().replaceWith(owner.getInventory()); - owner = newOwner; - // Update slots to point to new inventory. - slots.forEach(slot -> slot.setHolder(newOwner)); - } - - @Override - public boolean isInUse() { - return !transaction.isEmpty(); - } - - @Override - public @NotNull org.bukkit.entity.Player getPlayer() { - return getOwner(); - } - - @Override - public int getContainerSize() { - return size; - } - - @Override - public boolean isEmpty() { - return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty); - } - - @Override - public @NotNull ItemStack getItem(int index) { - return slots.get(index).get(); - } - - @Override - public @NotNull ItemStack removeItem(int index, int amount) { - return slots.get(index).removePartial(amount); - } - - @Override - public @NotNull ItemStack removeItemNoUpdate(int index) { - return slots.get(index).remove(); - } - - @Override - public void setItem(int index, @NotNull ItemStack itemStack) { - slots.get(index).set(itemStack); - } - - @Override - public int getMaxStackSize() { - return maxStackSize; - } - - @Override - public void setMaxStackSize(int maxStackSize) { - this.maxStackSize = maxStackSize; - } - - @Override - public void setChanged() { - } - - @Override - public boolean stillValid(@NotNull Player player) { - return true; - } - - @Override - public @NotNull List getContents() { - NonNullList contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY); - for (int i = 0; i < getContainerSize(); ++i) { - contents.set(i, getItem(i)); - } - return contents; - } - - @Override - public void onOpen(@NotNull CraftHumanEntity viewer) { - transaction.add(viewer); - } - - @Override - public void onClose(@NotNull CraftHumanEntity viewer) { - transaction.remove(viewer); - } - - @Override - public @NotNull List getViewers() { - return transaction; - } - - @Override - public @NotNull org.bukkit.entity.Player getOwner() { - return owner.getBukkitEntity(); - } - - @Override - public Location getLocation() { - return owner.getBukkitEntity().getLocation(); - } - - @Override - public void clearContent() { - owner.getInventory().clearContent(); - owner.inventoryMenu.getCraftSlots().clearContent(); - owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots()); - owner.containerMenu.setCarried(ItemStack.EMPTY); - } - - public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { - if (player instanceof ServerPlayer serverPlayer) { - return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); - } - return null; - } - } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java index d57b1a4b..9ef9a468 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventory.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.common.container.bukkit; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.internal.common.container.BaseOpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; @@ -18,13 +18,13 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { - public OpenPlayerInventory(@NotNull OpenInventory inventory) { + public OpenPlayerInventory(@NotNull BaseOpenInventory inventory) { super(inventory); } @Override - public @NotNull OpenInventory getInventory() { - return (OpenInventory) super.getInventory(); + public @NotNull BaseOpenInventory getInventory() { + return (BaseOpenInventory) super.getInventory(); } @Override diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java index 165211f2..326a6f51 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/bukkit/OpenPlayerInventorySelf.java @@ -1,6 +1,6 @@ package com.lishid.openinv.internal.common.container.bukkit; -import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.internal.common.container.BaseOpenInventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -8,7 +8,7 @@ public class OpenPlayerInventorySelf extends OpenPlayerInventory { private final int offset; - public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) { + public OpenPlayerInventorySelf(@NotNull BaseOpenInventory inventory, int offset) { super(inventory); this.offset = offset; } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java index c9f6c1aa..3f55b036 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.common.container.menu; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.internal.common.container.BaseOpenInventory; import com.lishid.openinv.internal.common.container.bukkit.OpenDummyPlayerInventory; import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventorySelf; import com.lishid.openinv.internal.common.container.slot.ContentDrop; @@ -23,15 +23,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenInventoryMenu extends OpenChestMenu { +public class OpenInventoryMenu extends OpenChestMenu { private int offset; - public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) { + public OpenInventoryMenu(BaseOpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) { super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly); } - private static MenuType getMenuType(OpenInventory inventory, ServerPlayer viewer) { + private static MenuType getMenuType(BaseOpenInventory inventory, ServerPlayer viewer) { int size = inventory.getContainerSize(); // Disallow duplicate access to own main inventory contents. if (inventory.getOwnerHandle().equals(viewer)) { @@ -99,7 +99,7 @@ protected void preSlotSetup() { } @Override - protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { + protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { org.bukkit.inventory.Inventory bukkitInventory; if (viewOnly) { bukkitInventory = new OpenDummyPlayerInventory(container); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java index 27b5232a..57cb1ab2 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java @@ -18,7 +18,8 @@ public abstract class BaseOpenPlayer extends CraftPlayer { /** - * List of tags to always reset when saving. + * List of tags to always reset when saving. These are items that do not get written + * if unset or empty, resulting in older values not being clobbered appropriately. * * @see net.minecraft.world.entity.Entity#saveWithoutId(ValueOutput) * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(ValueOutput) @@ -36,6 +37,7 @@ public abstract class BaseOpenPlayer extends CraftPlayer { "TicksFrozen", "HasVisualFire", "Tags", + "data", "Passengers", // ServerPlayer#addAdditionalSaveData(CompoundTag) // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle @@ -51,9 +53,9 @@ public abstract class BaseOpenPlayer extends CraftPlayer { "SpawnDimension", "raid_omen_position", "ender_pearls", - // Player#addAdditionalSaveData(CompoundTag) "ShoulderEntityLeft", "ShoulderEntityRight", + // Player#addAdditionalSaveData(CompoundTag) "LastDeathLocation", "current_explosion_impact_pos", // LivingEntity#addAdditionalSaveData(CompoundTag) diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 99877b65..79e43186 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -40,7 +40,7 @@ public void saveData() { CompoundTag oldData = isOnline() ? null - : worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null); + : worldNBTStorage.load(player.nameAndId()).orElse(null); CompoundTag playerData = getWritableTag(oldData); ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData); diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index 6b2fedb9..40a1d2e6 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -1,11 +1,13 @@ package com.lishid.openinv.internal.common.player; import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.common.container.BaseOpenInventory; import com.lishid.openinv.internal.common.container.OpenEnderChest; import com.lishid.openinv.internal.common.container.OpenInventory; import com.lishid.openinv.util.JulLoggerAdapter; import com.mojang.authlib.GameProfile; import io.papermc.paper.adventure.PaperAdventure; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -18,6 +20,8 @@ import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.ValueInput; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -143,26 +147,30 @@ protected boolean loadData(@NotNull MinecraftServer server, @NotNull ServerPlaye // See CraftPlayer#loadData try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) { - ValueInput loadedData = server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null); + CompoundTag loadedData = server.getPlayerList().playerIo.load(player.nameAndId()).orElse(null); if (loadedData == null) { // Exceptions with loading are logged. return false; } + ValueInput valueInput = TagValueInput.create(scopedCollector, player.registryAccess(), loadedData); + // Read basic data into the player. - player.load(loadedData); - // Game type settings are loaded separately. - player.loadGameTypes(loadedData); + player.load(valueInput); // World is not loaded by ServerPlayer#load(CompoundTag) on Paper. - parseWorld(server, player, loadedData); + parseWorld(server, player, valueInput); } return true; } - private void parseWorld(@NotNull MinecraftServer server, @NotNull ServerPlayer player, @NotNull ValueInput loadedData) { + protected void parseWorld( + @NotNull MinecraftServer server, + @NotNull ServerPlayer player, + @NotNull ValueInput loadedData + ) { // See PlayerList#placeNewPlayer World bukkitWorld; Optional msbs = loadedData.getLong("WorldUUIDMost"); @@ -184,7 +192,8 @@ protected void spawnInDefaultWorld(@NotNull MinecraftServer server, @NotNull Ser ServerLevel level = server.getLevel(Level.OVERWORLD); if (level != null) { // Adjust player to default spawn (in keeping with Paper handling) when world not found. - player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F); + LevelData.RespawnData respawnData = level.levelData.getRespawnData(); + player.snapTo(player.adjustSpawnLocation(level, respawnData.pos()).getBottomCenter(), respawnData.yaw(), 0.0F); player.spawnIn(level); } else { logger.warning("Tried to load player with invalid world when no fallback was available!"); @@ -208,16 +217,7 @@ protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlay if (nmsPlayer.getBukkitEntity() instanceof BaseOpenPlayer openPlayer) { return openPlayer; } - MinecraftServer server = nmsPlayer.getServer(); - if (server == null) { - if (!(Bukkit.getServer() instanceof CraftServer craftServer)) { - logger.warning(() -> - "Unable to inject ServerPlayer, certain player data may be lost when saving! Server is not a CraftServer: " - + Bukkit.getServer().getClass().getName()); - return player; - } - server = craftServer.getServer(); - } + MinecraftServer server = nmsPlayer.level().getServer(); injectPlayer(server, nmsPlayer); return nmsPlayer.getBukkitEntity(); } catch (IllegalAccessException e) { @@ -244,7 +244,7 @@ protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlay // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) AbstractContainerMenu menu; Component title; - if (inventory instanceof OpenInventory playerInv) { + if (inventory instanceof BaseOpenInventory playerInv) { menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly); title = playerInv.getTitle(player); } else if (inventory instanceof OpenEnderChest enderChest) { diff --git a/internal/paper1_21_1/build.gradle.kts b/internal/paper1_21_1/build.gradle.kts index 3c86f885..84ba9a0d 100644 --- a/internal/paper1_21_1/build.gradle.kts +++ b/internal/paper1_21_1/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_8")) implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) implementation(project(":openinvadapterpaper1_21_3")) diff --git a/internal/paper1_21_3/build.gradle.kts b/internal/paper1_21_3/build.gradle.kts index 04b5ec7e..86fa2f13 100644 --- a/internal/paper1_21_3/build.gradle.kts +++ b/internal/paper1_21_3/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_8")) implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) diff --git a/internal/paper1_21_4/build.gradle.kts b/internal/paper1_21_4/build.gradle.kts index 1a23419f..761ada30 100644 --- a/internal/paper1_21_4/build.gradle.kts +++ b/internal/paper1_21_4/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_8")) implementation(project(":openinvadapterpaper1_21_5")) paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java index a935bd22..68970217 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory { +public class OpenInventory extends com.lishid.openinv.internal.paper1_21_8.container.OpenInventory { public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) { super(bukkitPlayer); diff --git a/internal/paper1_21_5/build.gradle.kts b/internal/paper1_21_5/build.gradle.kts index b9f0cf13..2b1471c1 100644 --- a/internal/paper1_21_5/build.gradle.kts +++ b/internal/paper1_21_5/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_8")) paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") } diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java index 2eb73a41..e0552bd2 100644 --- a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/InternalAccessor.java @@ -9,7 +9,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor { +public class InternalAccessor extends com.lishid.openinv.internal.paper1_21_8.InternalAccessor { private final @NotNull PlayerManager manager; diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java index 3fc0fe10..9637b062 100644 --- a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/PlayerManager.java @@ -16,7 +16,7 @@ import java.util.UUID; import java.util.logging.Logger; -public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { +public class PlayerManager extends com.lishid.openinv.internal.paper1_21_8.player.PlayerManager { public PlayerManager(@NotNull Logger logger) { super(logger); diff --git a/internal/paper1_21_8/build.gradle.kts b/internal/paper1_21_8/build.gradle.kts new file mode 100644 index 00000000..306db103 --- /dev/null +++ b/internal/paper1_21_8/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + + paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT") +} diff --git a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/InternalAccessor.java b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/InternalAccessor.java new file mode 100644 index 00000000..0c68893d --- /dev/null +++ b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/InternalAccessor.java @@ -0,0 +1,31 @@ +package com.lishid.openinv.internal.paper1_21_8; + +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.paper1_21_8.container.OpenInventory; +import com.lishid.openinv.internal.paper1_21_8.player.PlayerManager; +import com.lishid.openinv.util.lang.LanguageManager; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Logger; + +public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor { + + private final @NotNull PlayerManager manager; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + super(logger, lang); + manager = new PlayerManager(logger); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new OpenInventory(player); + } + +} diff --git a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java new file mode 100644 index 00000000..1784c44b --- /dev/null +++ b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java @@ -0,0 +1,43 @@ +package com.lishid.openinv.internal.paper1_21_8.container; + +import com.lishid.openinv.internal.common.container.BaseOpenInventory; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenInventory extends BaseOpenInventory { + + public OpenInventory(@NotNull Player bukkitPlayer) { + super(bukkitPlayer); + } + + @Override + public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { + MutableComponent component = Component.empty(); + // Prefix for use with custom bitmap image fonts. + if (owner.equals(viewer)) { + component.append( + Component.translatableWithFallback("openinv.container.inventory.self", "") + .withStyle(style -> style + .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withColor(ChatFormatting.WHITE))); + } else { + component.append( + Component.translatableWithFallback("openinv.container.inventory.other", "") + .withStyle(style -> style + .withFont(ResourceLocation.parse("openinv:font/inventory")) + .withColor(ChatFormatting.WHITE))); + } + // Normal title: "Inventory - OwnerName" + component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) + .append(Component.translatable("container.inventory")) + .append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName())); + return component; + } + +} diff --git a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java new file mode 100644 index 00000000..9462c8e4 --- /dev/null +++ b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java @@ -0,0 +1,66 @@ +package com.lishid.openinv.internal.paper1_21_8.player; + +import com.lishid.openinv.event.OpenEvents; +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; +import com.mojang.logging.LogUtils; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.storage.PlayerDataStorage; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueOutput; +import org.bukkit.craftbukkit.CraftServer; +import org.slf4j.Logger; + +import java.nio.file.Files; +import java.nio.file.Path; + +public class OpenPlayer extends BaseOpenPlayer { + + protected OpenPlayer( + CraftServer server, + ServerPlayer entity, + PlayerManager manager + ) { + super(server, entity, manager); + } + + @Override + public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + + ServerPlayer player = this.getHandle(); + Logger logger = LogUtils.getLogger(); + // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) { + PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo; + + CompoundTag oldData = isOnline() + ? null + : worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null); + CompoundTag playerData = getWritableTag(oldData); + + ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData); + player.saveWithoutId(valueOutput); + + if (oldData != null) { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + } + + Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(dataFile, tempFile, backupFile); + } catch (Exception e) { + LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); + } + } + +} diff --git a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/PlayerManager.java b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/PlayerManager.java new file mode 100644 index 00000000..dc3ad219 --- /dev/null +++ b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/PlayerManager.java @@ -0,0 +1,100 @@ +package com.lishid.openinv.internal.paper1_21_8.player; + +import com.lishid.openinv.internal.common.player.BaseOpenPlayer; +import com.lishid.openinv.util.JulLoggerAdapter; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.ValueInput; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Logger; + +public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { + + public PlayerManager(@NotNull Logger logger) { + super(logger); + } + + @Override + protected boolean loadData(@NotNull MinecraftServer server, @NotNull ServerPlayer player) { + // See CraftPlayer#loadData + + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) { + ValueInput loadedData = server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null); + + if (loadedData == null) { + // Exceptions with loading are logged. + return false; + } + + // Read basic data into the player. + player.load(loadedData); + // Game type settings are loaded separately. + player.loadGameTypes(loadedData); + + // World is not loaded by ServerPlayer#load(CompoundTag) on Paper. + parseWorld(server, player, loadedData); + } + + return true; + } + + @Override + protected void spawnInDefaultWorld(@NotNull MinecraftServer server, @NotNull ServerPlayer player) { + ServerLevel level = server.getLevel(Level.OVERWORLD); + if (level != null) { + // Adjust player to default spawn (in keeping with Paper handling) when world not found. + player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F); + player.spawnIn(level); + } else { + logger.warning("Tried to load player with invalid world when no fallback was available!"); + } + } + + @Override + protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(server.server, player, this)); + } + + @Override + public @NotNull Player inject(@NotNull Player player) { + try { + ServerPlayer nmsPlayer = getHandle(player); + if (nmsPlayer.getBukkitEntity() instanceof BaseOpenPlayer openPlayer) { + return openPlayer; + } + MinecraftServer server = nmsPlayer.getServer(); + if (server == null) { + if (!(Bukkit.getServer() instanceof CraftServer craftServer)) { + logger.warning(() -> + "Unable to inject ServerPlayer, certain player data may be lost when saving! Server is not a CraftServer: " + + Bukkit.getServer().getClass().getName()); + return player; + } + server = craftServer.getServer(); + } + injectPlayer(server, nmsPlayer); + return nmsPlayer.getBukkitEntity(); + } catch (IllegalAccessException e) { + logger.log( + java.util.logging.Level.WARNING, + e, + () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!" + ); + return player; + } + } + +} diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 25635da0..94cf844f 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -10,9 +10,9 @@ plugins { apply() apply() -val spigotVer = "1.21.8-R0.1-SNAPSHOT" +val spigotVer = "1.21.10-R0.1-SNAPSHOT" // Used by common adapter to relocate Craftbukkit classes to a versioned package. -rootProject.extra["craftbukkitPackage"] = "v1_21_R5" +rootProject.extra["craftbukkitPackage"] = "v1_21_R6" configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java index a39e1d2d..6646f436 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java @@ -4,8 +4,8 @@ import com.lishid.openinv.internal.reobf.container.OpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.player.Inventory; -import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java index d28ac148..10f16100 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java @@ -6,8 +6,8 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import org.bukkit.craftbukkit.v1_21_R5.CraftEquipmentSlot; -import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R6.CraftEquipmentSlot; +import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.PlayerInventory; import org.jetbrains.annotations.NotNull; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java index 2e22f750..c200ccd9 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java @@ -11,12 +11,10 @@ import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.storage.PlayerDataStorage; -import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.TagValueOutput; -import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -import org.bukkit.craftbukkit.v1_21_R5.CraftServer; -import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R6.CraftServer; +import org.bukkit.craftbukkit.v1_21_R6.entity.CraftPlayer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -108,17 +106,9 @@ public void saveData() { try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) { PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo; - ValueInput oldDataInput = isOnline() + CompoundTag oldData = isOnline() ? null - : worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector, player.registryAccess()).orElse(null); - // Grab CompoundTag out of TagValueInput - we can't iterate over the fields via TagValueInput. - CompoundTag oldData = null; - if (oldDataInput != null) { - Field tagValueInputInput = TagValueInput.class.getDeclaredField("input"); - tagValueInputInput.setAccessible(true); - oldData = (CompoundTag) tagValueInputInput.get(oldDataInput); - } - + : worldNBTStorage.load(player.nameAndId()).orElse(null); CompoundTag playerData = getWritableTag(oldData); ValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, player.registryAccess()); diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 0c084bf2..6dde10d7 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -5,6 +5,7 @@ import com.lishid.openinv.internal.reobf.container.OpenInventory; import com.lishid.openinv.util.JulLoggerAdapter; import com.mojang.authlib.GameProfile; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.MinecraftServer; @@ -17,13 +18,14 @@ import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.ValueInput; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_21_R5.CraftServer; -import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_21_R5.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R6.CraftServer; +import org.bukkit.craftbukkit.v1_21_R6.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R6.event.CraftEventFactory; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; @@ -138,17 +140,17 @@ boolean loadData(@NotNull ServerPlayer player) { // See CraftPlayer#loadData try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) { - ValueInput loadedData = player.server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null); + CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player.nameAndId()).orElse(null); if (loadedData == null) { // Exceptions with loading are logged. return false; } + ValueInput valueInput = TagValueInput.create(scopedCollector, player.registryAccess(), loadedData); + // Read basic data into the player. - player.load(loadedData); - // Game type settings are loaded separately. - player.loadGameTypes(loadedData); + player.load(valueInput); } return true; diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index eec509a6..95d899c2 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_8")) implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) implementation(project(":openinvadapterpaper1_21_3")) diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index e32cac56..671924e3 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -64,7 +64,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - Version maxSupported = Version.of(1, 21, 8); + Version maxSupported = Version.of(1, 21, 10); Version minSupported = Version.of(1, 21, 1); // Ensure version is in supported range. @@ -74,7 +74,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { // Load Spigot accessor. if (!PAPER) { - if (BukkitVersions.MINECRAFT.equals(maxSupported) || BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 7))) { + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // Current Spigot, remapped internals are available. return new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang); } else { @@ -85,9 +85,13 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { // Paper or a Paper fork, can use Mojang-mapped internals. if (BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported) - && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7, 1.21.8 + && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 9))) { // 1.21.9, 1.21.10 return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 8)) + && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7, 1.21.8 + return new com.lishid.openinv.internal.paper1_21_8.InternalAccessor(logger, lang); + } if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 5))) { // 1.21.5 return new com.lishid.openinv.internal.paper1_21_5.InternalAccessor(logger, lang); } @@ -176,7 +180,7 @@ public String getReleasesLink() { if (!PAPER) { return getSpigotReleaseLink(); } - // Paper 1.21.1-1.21.7 + // Paper 1.21.1+ return "https://github.com/Jikoo/OpenInv/releases"; } @@ -196,6 +200,12 @@ private String getSpigotReleaseLink() { if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 6))) { return "Unsupported; upgrade to 1.21.7: https://github.com/Jikoo/OpenInv/releases"; } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 8))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.13"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 9))) { + return "Unsupported; upgrade to 1.21.10: https://github.com/Jikoo/OpenInv/releases"; + } return "https://github.com/Jikoo/OpenInv/releases"; } diff --git a/resource-pack/openinv-legibility-pack/pack.mcmeta b/resource-pack/openinv-legibility-pack/pack.mcmeta index 42ae406a..17314e78 100644 --- a/resource-pack/openinv-legibility-pack/pack.mcmeta +++ b/resource-pack/openinv-legibility-pack/pack.mcmeta @@ -1,13 +1,17 @@ { "pack": { "description": "Improve OpenInv's legibility", - "pack_format": 55, - "supported_formats": [ 34, 64 ] + "min_format": 34, + "max_format": [69, 0], + "pack_format": 64, + "supported_formats": [ 34, 69 ] }, "overlays": { "entries": [ { - "formats": [ 44, 64 ], + "min_format": 44, + "max_format": [69, 0], + "formats": [ 44, 69 ], "directory": "openinv_44" }, { diff --git a/settings.gradle.kts b/settings.gradle.kts index 4e550317..e418ca9d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ if (!java.lang.Boolean.getBoolean("jitpack")) { val internals = listOf( "common", + "paper1_21_8", "paper1_21_5", "paper1_21_4", "paper1_21_3", From 080d02f93f11f628370ef6fc5c60c21ce7832310 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 8 Oct 2025 15:01:31 -0400 Subject: [PATCH 324/340] Bump version to 5.1.14 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d641a745..2e061eb8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.14-SNAPSHOT +version=5.1.14 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 8c102c4400956f7eb57d0f0de837b0396dec2a8f Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 8 Oct 2025 15:01:55 -0400 Subject: [PATCH 325/340] Bump version to 5.1.15-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2e061eb8..7b08c855 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.14 +version=5.1.15-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From b269a5ed3c297a0f44eac103e2921509448e3cdc Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 11 Oct 2025 09:35:59 -0400 Subject: [PATCH 326/340] Fix /openinv on Spigot (#342) --- .../reobf/container/bukkit/OpenPlayerInventory.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java index 6646f436..04985b24 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java @@ -1,7 +1,7 @@ package com.lishid.openinv.internal.reobf.container.bukkit; import com.google.common.base.Preconditions; -import com.lishid.openinv.internal.reobf.container.OpenInventory; +import com.lishid.openinv.internal.reobf.container.BaseOpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.player.Inventory; import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftInventory; @@ -15,13 +15,13 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInventory { - public OpenPlayerInventory(@NotNull OpenInventory inventory) { + public OpenPlayerInventory(@NotNull BaseOpenInventory inventory) { super(inventory); } @Override - public @NotNull OpenInventory getInventory() { - return (OpenInventory) super.getInventory(); + public @NotNull BaseOpenInventory getInventory() { + return (BaseOpenInventory) super.getInventory(); } @Override From 806fb848a777ffe2ece1a4591978371c1b6052cf Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 11 Oct 2025 09:46:34 -0400 Subject: [PATCH 327/340] Add line break between server implementations I noticed this was a problem the first update and have been forgetting and then manually fixing it on Bukkit since. A travesty. --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 01ae259f..5399df29 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -27,7 +27,7 @@ jobs: name: OpenInv ${{ env.VERSIONED_NAME }} body: |- ## Supported server versions - **Paper:** TODO VERSION, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.1 + **Paper:** TODO VERSION, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.1 **Spigot:** TODO VERSION TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE. From e3285a6d616bc00c2b25be2af499e008b3489587 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 11 Oct 2025 09:48:35 -0400 Subject: [PATCH 328/340] Bump version to 5.1.15 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7b08c855..dfc18abc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.15-SNAPSHOT +version=5.1.15 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 743c46f322b56065631f22aac6aca36a38ddb22e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sat, 11 Oct 2025 09:48:58 -0400 Subject: [PATCH 329/340] Bump version to 5.1.16-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index dfc18abc..1dd58c71 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.15 +version=5.1.16-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From 2ffff99acf4624c00fe2662eaaa18aa7eba30744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:24:15 +0000 Subject: [PATCH 330/340] Bump com.google.errorprone:error_prone_core from 2.42.0 to 2.43.0 (#351) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3d5381dd..b15b4022 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ annotations = "26.0.2-1" paperweight = "2.0.0-beta.17" shadow = "9.2.2" folia-scheduler-wrapper = "v0.0.3" -errorprone-core = "2.42.0" +errorprone-core = "2.43.0" errorprone-gradle = "4.2.0" slf4j = "2.0.17" From d8b5aa145b1a232668182bbf6a702df8e3268092 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 3 Nov 2025 09:51:00 -0500 Subject: [PATCH 331/340] Improve equal access behaviors (#346) * Extract common chest code, expose view-only state * Add prefix for view-only state * Add nodes for behavior of accessing equal * Include permission-based denial feedback --- .../java/com/lishid/openinv/IOpenInv.java | 9 +- .../com/lishid/openinv/util/Permissions.java | 3 + gradle.properties | 2 +- .../common/container/BaseOpenInventory.java | 6 +- .../common/container/OpenEnderChest.java | 16 +- .../common/container/OpenInventory.java | 8 +- .../common/container/menu/OpenChestMenu.java | 226 +-------- .../container/menu/OpenEnderChestMenu.java | 2 +- .../container/menu/OpenInventoryMenu.java | 2 +- .../common/container/menu/OpenSyncMenu.java | 251 ++++++++++ .../internal/common/player/PlayerManager.java | 20 +- .../paper1_21_4/container/OpenEnderChest.java | 2 +- .../paper1_21_4/container/OpenInventory.java | 4 +- .../container/menu/OpenChestMenu.java | 467 ------------------ .../container/menu/OpenEnderChestMenu.java | 5 +- .../container/menu/OpenInventoryMenu.java | 5 +- .../container/menu/OpenSyncMenu.java | 229 +++++++++ .../paper1_21_8/container/OpenInventory.java | 8 +- .../internal/reobf/player/PlayerManager.java | 19 +- .../main/java/com/lishid/openinv/OpenInv.java | 21 +- .../openinv/command/ClearInvCommand.java | 3 +- .../openinv/command/OpenInvCommand.java | 5 +- .../openinv/command/PlayerLookupCommand.java | 101 +++- plugin/src/main/resources/plugin.yml | 3 + 24 files changed, 660 insertions(+), 757 deletions(-) create mode 100644 internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenSyncMenu.java delete mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java create mode 100644 internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenSyncMenu.java diff --git a/api/src/main/java/com/lishid/openinv/IOpenInv.java b/api/src/main/java/com/lishid/openinv/IOpenInv.java index 4639f328..9634f7af 100644 --- a/api/src/main/java/com/lishid/openinv/IOpenInv.java +++ b/api/src/main/java/com/lishid/openinv/IOpenInv.java @@ -138,14 +138,21 @@ public interface IOpenInv { boolean online ) throws InstantiationException; + /** + * @deprecated Use {@link #openInventory(Player, ISpecialInventory, boolean)} + */ + @Deprecated(forRemoval = true, since = "5.2.0") + @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); + /** * Open an {@link ISpecialInventory} for a {@link Player}. * * @param player the viewer * @param inventory the inventory to open + * @param viewOnly whether the inventory should be view-only * @return the resulting {@link InventoryView} */ - @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); + @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly); /** * Check if a {@link Player} is currently loaded by OpenInv. diff --git a/common/src/main/java/com/lishid/openinv/util/Permissions.java b/common/src/main/java/com/lishid/openinv/util/Permissions.java index e9836951..0f2fcd65 100644 --- a/common/src/main/java/com/lishid/openinv/util/Permissions.java +++ b/common/src/main/java/com/lishid/openinv/util/Permissions.java @@ -42,6 +42,9 @@ public enum Permissions { ACCESS_CROSSWORLD("access.crossworld"), ACCESS_OFFLINE("access.offline"), ACCESS_ONLINE("access.online"), + ACCESS_EQUAL_EDIT("access.equal.edit"), + ACCESS_EQUAL_VIEW("access.equal.view"), + ACCESS_EQUAL_DENY("access.equal.deny"), SPECTATE_CLICK("spectate.click"), diff --git a/gradle.properties b/gradle.properties index 1dd58c71..3ed8b188 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.1.16-SNAPSHOT +version=5.2.0-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java index cb68d888..4987afd3 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/BaseOpenInventory.java @@ -3,6 +3,7 @@ import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.internal.InternalOwned; import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu; import com.lishid.openinv.internal.common.container.slot.Content; import com.lishid.openinv.internal.common.container.slot.ContentCrafting; @@ -24,7 +25,6 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; @@ -199,7 +199,7 @@ public InventoryType.SlotType getSlotType(int index) { return slots.get(index).getSlotType(); } - public abstract Component getTitle(ServerPlayer player); + public abstract Component getTitle(ServerPlayer player, @Nullable OpenChestMenu menu); @Override public ServerPlayer getOwnerHandle() { @@ -325,7 +325,7 @@ public void clearContent() { owner.containerMenu.setCarried(ItemStack.EMPTY); } - public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + public @Nullable OpenChestMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java index f3160570..284bf7ab 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenEnderChest.java @@ -2,16 +2,17 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import com.lishid.openinv.internal.common.container.menu.OpenEnderChestMenu; import com.lishid.openinv.internal.common.player.PlayerManager; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.StackedItemContents; -import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.StackedContentsCompatible; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; @@ -177,13 +178,20 @@ public void fillStackedContents(@NotNull StackedItemContents stackedContents) { } } - public Component getTitle() { - return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName()) + public Component getTitle(@Nullable OpenChestMenu menu) { + MutableComponent component; + if (menu != null && menu.isViewOnly()) { + component = Component.translatableWithFallback("openinv.container.enderchest.viewonly", "[RO] "); + } else { + component = Component.translatableWithFallback("openinv.container.enderchest.editable", ""); + } + return component + .append(Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName())) .append(Component.translatable("container.enderchest")) .append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName())); } - public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + public @Nullable OpenChestMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 79f48fd1..48204e49 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -1,5 +1,6 @@ package com.lishid.openinv.internal.common.container; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FontDescription; @@ -17,7 +18,7 @@ public OpenInventory(@NotNull Player bukkitPlayer) { } @Override - public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { + public @NotNull Component getTitle(@Nullable ServerPlayer viewer, @Nullable OpenChestMenu menu) { MutableComponent component = Component.empty(); // Prefix for use with custom bitmap image fonts. if (owner.equals(viewer)) { @@ -33,6 +34,11 @@ public OpenInventory(@NotNull Player bukkitPlayer) { .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) .withColor(ChatFormatting.WHITE))); } + if (menu != null && menu.isViewOnly()) { + component.append(Component.translatableWithFallback("openinv.container.inventory.viewonly", "[RO] ")); + } else { + component.append(Component.translatableWithFallback("openinv.container.inventory.editable", "")); + } // Normal title: "Inventory - OwnerName" component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) .append(Component.translatable("container.inventory")) diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java index 0cae6c7c..939ecd1b 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenChestMenu.java @@ -1,26 +1,16 @@ package com.lishid.openinv.internal.common.container.menu; -import com.google.common.base.Suppliers; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.InternalOwned; import com.lishid.openinv.internal.common.container.bukkit.OpenDummyInventory; -import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.network.HashedStack; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.ClickType; -import net.minecraft.world.inventory.ContainerData; -import net.minecraft.world.inventory.ContainerListener; -import net.minecraft.world.inventory.ContainerSynchronizer; -import net.minecraft.world.inventory.DataSlot; import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.RemoteSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.craftbukkit.inventory.CraftInventoryView; @@ -30,14 +20,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - /** - * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. + * An extension of {@link AbstractContainerMenu} storing and managing data common to all special inventories. */ -@SuppressWarnings("HidingField") // Revisit when removing 1.21.4 support public abstract class OpenChestMenu> extends AbstractContainerMenu { @@ -49,13 +34,6 @@ public abstract class OpenChestMenu, Inventory> bukkitEntity; - // Syncher fields - protected @Nullable ContainerSynchronizer synchronizer; - protected final List dataSlots = new ArrayList<>(); - protected final IntList remoteDataSlots = new IntArrayList(); - protected final List containerListeners = new ArrayList<>(); - private RemoteSlot remoteCarried = RemoteSlot.PLACEHOLDER; - protected boolean suppressRemoteUpdates; protected OpenChestMenu( @NotNull MenuType type, @@ -125,6 +103,7 @@ protected OpenChestMenu( }; } + protected void preSlotSetup() { } @@ -136,6 +115,9 @@ protected void preSlotSetup() { return slot; } + public boolean isViewOnly() { + return viewOnly; + } @Override public final @NotNull CraftInventoryView, Inventory> getBukkitView() { @@ -303,202 +285,4 @@ public boolean stillValid(@NotNull Player player) { return true; } - // Overrides from here on are purely to modify the sync process to send placeholder items. - @Override - protected @NotNull Slot addSlot(@NotNull Slot slot) { - slot.index = this.slots.size(); - this.slots.add(slot); - this.lastSlots.add(ItemStack.EMPTY); - this.remoteSlots.add(this.synchronizer != null ? this.synchronizer.createSlot() : RemoteSlot.PLACEHOLDER); - return slot; - } - - @Override - protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) { - this.dataSlots.add(dataSlot); - this.remoteDataSlots.add(0); - return dataSlot; - } - - @Override - protected void addDataSlots(ContainerData containerData) { - for (int i = 0; i < containerData.getCount(); i++) { - this.addDataSlot(DataSlot.forContainer(containerData, i)); - } - } - - @Override - public void addSlotListener(@NotNull ContainerListener containerListener) { - if (!this.containerListeners.contains(containerListener)) { - this.containerListeners.add(containerListener); - this.broadcastChanges(); - } - } - - @Override - public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { - this.synchronizer = containerSynchronizer; - this.remoteCarried = synchronizer.createSlot(); - this.remoteSlots.replaceAll(slot -> synchronizer.createSlot()); - this.sendAllDataToRemote(); - } - - @Override - public void sendAllDataToRemote() { - List contentsCopy = new ArrayList<>(); - for (int index = 0; index < slots.size(); ++index) { - Slot slot = slots.get(index); - ItemStack itemStack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); - contentsCopy.add(itemStack); - this.remoteSlots.get(index).force(itemStack); - } - - remoteCarried.force(getCarried()); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); - } - - if (this.synchronizer != null) { - this.synchronizer.sendInitialData(this, contentsCopy, this.getCarried().copy(), this.remoteDataSlots.toIntArray()); - } - } - - @Override - public void forceSlot(@NotNull Container container, int slot) { - int slotsIndex = this.findSlot(container, slot).orElse(-1); - if (slotsIndex != -1) { - ItemStack item = this.slots.get(slotsIndex).getItem(); - this.remoteSlots.get(slotsIndex).force(item); - if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(this, slotsIndex, item.copy()); - } - } - } - - @Override - public void broadcastCarriedItem() { - ItemStack carried = this.getCarried(); - this.remoteCarried.force(carried); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, carried.copy()); - } - } - - @Override - public void removeSlotListener(@NotNull ContainerListener containerListener) { - this.containerListeners.remove(containerListener); - } - - @Override - public void broadcastChanges() { - for (int index = 0; index < this.slots.size(); ++index) { - Slot slot = this.slots.get(index); - ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); - Supplier supplier = Suppliers.memoize(itemstack::copy); - this.triggerSlotListeners(index, itemstack, supplier); - this.synchronizeSlotToRemote(index, itemstack, supplier); - } - - this.synchronizeCarriedToRemote(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot dataSlot = this.dataSlots.get(index); - int j = dataSlot.get(); - if (dataSlot.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, j); - } - - this.synchronizeDataSlotToRemote(index, j); - } - } - - @Override - public void broadcastFullState() { - for (int index = 0; index < this.slots.size(); ++index) { - ItemStack itemstack = this.slots.get(index).getItem(); - this.triggerSlotListeners(index, itemstack, itemstack::copy); - } - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot containerproperty = this.dataSlots.get(index); - if (containerproperty.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, containerproperty.get()); - } - } - - this.sendAllDataToRemote(); - } - - private void updateDataSlotListeners(int i, int j) { - for (ContainerListener containerListener : this.containerListeners) { - containerListener.dataChanged(this, i, j); - } - } - - @Override - public void triggerSlotListeners(int index, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { - ItemStack itemStack1 = this.lastSlots.get(index); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemStack2 = supplier.get(); - this.lastSlots.set(index, itemStack2); - - for (ContainerListener containerListener : this.containerListeners) { - containerListener.slotChanged(this, index, itemStack2); - } - } - } - - @Override - public void synchronizeSlotToRemote(int i, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { - if (!this.suppressRemoteUpdates) { - RemoteSlot slot = this.remoteSlots.get(i); - if (!slot.matches(itemStack)) { - slot.force(itemStack); - if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(this, i, supplier.get()); - } - } - } - } - - private void synchronizeDataSlotToRemote(int index, int value) { - if (!this.suppressRemoteUpdates) { - int existing = this.remoteDataSlots.getInt(index); - if (existing != value) { - this.remoteDataSlots.set(index, value); - if (this.synchronizer != null) { - this.synchronizer.sendDataChange(this, index, value); - } - } - } - } - - private void synchronizeCarriedToRemote() { - if (!this.suppressRemoteUpdates) { - ItemStack carried = this.getCarried(); - if (!this.remoteCarried.matches(carried)) { - this.remoteCarried.force(carried); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, carried.copy()); - } - } - } - } - - @Override - public void setRemoteCarried(@NotNull HashedStack stack) { - this.remoteCarried.receive(stack); - } - - @Override - public void suppressRemoteUpdates() { - this.suppressRemoteUpdates = true; - } - - @Override - public void resumeRemoteUpdates() { - this.suppressRemoteUpdates = false; - } - } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java index 62d94198..f0e70d33 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenEnderChestMenu.java @@ -7,7 +7,7 @@ import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; -public class OpenEnderChestMenu extends OpenChestMenu { +public class OpenEnderChestMenu extends OpenSyncMenu { public OpenEnderChestMenu( @NotNull OpenEnderChest enderChest, diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java index 3f55b036..52ca724e 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenInventoryMenu.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenInventoryMenu extends OpenChestMenu { +public class OpenInventoryMenu extends OpenSyncMenu { private int offset; diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenSyncMenu.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenSyncMenu.java new file mode 100644 index 00000000..612b2ec9 --- /dev/null +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/menu/OpenSyncMenu.java @@ -0,0 +1,251 @@ +package com.lishid.openinv.internal.common.container.menu; + +import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.network.HashedStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.ContainerListener; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.RemoteSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * An extension of {@link OpenChestMenu} that supports {@link SlotPlaceholder placeholders}. + */ +@SuppressWarnings("HidingField") // Revisit when removing 1.21.4 support +public abstract class OpenSyncMenu> + extends OpenChestMenu { + + // Syncher fields + protected @Nullable ContainerSynchronizer synchronizer; + protected final List dataSlots = new ArrayList<>(); + protected final IntList remoteDataSlots = new IntArrayList(); + protected final List containerListeners = new ArrayList<>(); + private RemoteSlot remoteCarried = RemoteSlot.PLACEHOLDER; + protected boolean suppressRemoteUpdates; + + protected OpenSyncMenu( + @NotNull MenuType type, + int containerCounter, + @NotNull T container, + @NotNull ServerPlayer viewer, + boolean viewOnly + ) { + super(type, containerCounter, container, viewer, viewOnly); + } + + // Overrides from here on are purely to modify the sync process to send placeholder items. + @Override + protected @NotNull Slot addSlot(@NotNull Slot slot) { + slot.index = this.slots.size(); + this.slots.add(slot); + this.lastSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(this.synchronizer != null ? this.synchronizer.createSlot() : RemoteSlot.PLACEHOLDER); + return slot; + } + + @Override + protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) { + this.dataSlots.add(dataSlot); + this.remoteDataSlots.add(0); + return dataSlot; + } + + @Override + protected void addDataSlots(ContainerData containerData) { + for (int i = 0; i < containerData.getCount(); i++) { + this.addDataSlot(DataSlot.forContainer(containerData, i)); + } + } + + @Override + public void addSlotListener(@NotNull ContainerListener containerListener) { + if (!this.containerListeners.contains(containerListener)) { + this.containerListeners.add(containerListener); + this.broadcastChanges(); + } + } + + @Override + public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { + this.synchronizer = containerSynchronizer; + this.remoteCarried = synchronizer.createSlot(); + this.remoteSlots.replaceAll(slot -> synchronizer.createSlot()); + this.sendAllDataToRemote(); + } + + @Override + public void sendAllDataToRemote() { + List contentsCopy = new ArrayList<>(); + for (int index = 0; index < slots.size(); ++index) { + Slot slot = slots.get(index); + ItemStack itemStack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + contentsCopy.add(itemStack); + this.remoteSlots.get(index).force(itemStack); + } + + remoteCarried.force(getCarried()); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); + } + + if (this.synchronizer != null) { + this.synchronizer.sendInitialData(this, contentsCopy, this.getCarried().copy(), this.remoteDataSlots.toIntArray()); + } + } + + @Override + public void forceSlot(@NotNull Container container, int slot) { + int slotsIndex = this.findSlot(container, slot).orElse(-1); + if (slotsIndex != -1) { + ItemStack item = this.slots.get(slotsIndex).getItem(); + this.remoteSlots.get(slotsIndex).force(item); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, slotsIndex, item.copy()); + } + } + } + + @Override + public void broadcastCarriedItem() { + ItemStack carried = this.getCarried(); + this.remoteCarried.force(carried); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, carried.copy()); + } + } + + @Override + public void removeSlotListener(@NotNull ContainerListener containerListener) { + this.containerListeners.remove(containerListener); + } + + @Override + public void broadcastChanges() { + for (int index = 0; index < this.slots.size(); ++index) { + Slot slot = this.slots.get(index); + ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + Supplier supplier = Suppliers.memoize(itemstack::copy); + this.triggerSlotListeners(index, itemstack, supplier); + this.synchronizeSlotToRemote(index, itemstack, supplier); + } + + this.synchronizeCarriedToRemote(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot dataSlot = this.dataSlots.get(index); + int j = dataSlot.get(); + if (dataSlot.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, j); + } + + this.synchronizeDataSlotToRemote(index, j); + } + } + + @Override + public void broadcastFullState() { + for (int index = 0; index < this.slots.size(); ++index) { + ItemStack itemstack = this.slots.get(index).getItem(); + this.triggerSlotListeners(index, itemstack, itemstack::copy); + } + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot containerproperty = this.dataSlots.get(index); + if (containerproperty.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, containerproperty.get()); + } + } + + this.sendAllDataToRemote(); + } + + private void updateDataSlotListeners(int i, int j) { + for (ContainerListener containerListener : this.containerListeners) { + containerListener.dataChanged(this, i, j); + } + } + + @Override + public void triggerSlotListeners(int index, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { + ItemStack itemStack1 = this.lastSlots.get(index); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemStack2 = supplier.get(); + this.lastSlots.set(index, itemStack2); + + for (ContainerListener containerListener : this.containerListeners) { + containerListener.slotChanged(this, index, itemStack2); + } + } + } + + @Override + public void synchronizeSlotToRemote(int i, @NotNull ItemStack itemStack, @NotNull Supplier supplier) { + if (!this.suppressRemoteUpdates) { + RemoteSlot slot = this.remoteSlots.get(i); + if (!slot.matches(itemStack)) { + slot.force(itemStack); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, i, supplier.get()); + } + } + } + } + + private void synchronizeDataSlotToRemote(int index, int value) { + if (!this.suppressRemoteUpdates) { + int existing = this.remoteDataSlots.getInt(index); + if (existing != value) { + this.remoteDataSlots.set(index, value); + if (this.synchronizer != null) { + this.synchronizer.sendDataChange(this, index, value); + } + } + } + } + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates) { + ItemStack carried = this.getCarried(); + if (!this.remoteCarried.matches(carried)) { + this.remoteCarried.force(carried); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, carried.copy()); + } + } + } + } + + @Override + public void setRemoteCarried(@NotNull HashedStack stack) { + this.remoteCarried.receive(stack); + } + + @Override + public void suppressRemoteUpdates() { + this.suppressRemoteUpdates = true; + } + + @Override + public void resumeRemoteUpdates() { + this.suppressRemoteUpdates = false; + } + +} diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java index 40a1d2e6..b43348a5 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/PlayerManager.java @@ -3,7 +3,7 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.common.container.BaseOpenInventory; import com.lishid.openinv.internal.common.container.OpenEnderChest; -import com.lishid.openinv.internal.common.container.OpenInventory; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import com.lishid.openinv.util.JulLoggerAdapter; import com.mojang.authlib.GameProfile; import io.papermc.paper.adventure.PaperAdventure; @@ -242,14 +242,14 @@ protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlay } // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) - AbstractContainerMenu menu; + OpenChestMenu menu; Component title; if (inventory instanceof BaseOpenInventory playerInv) { menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly); - title = playerInv.getTitle(player); + title = playerInv.getTitle(player, menu); } else if (inventory instanceof OpenEnderChest enderChest) { menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly); - title = enderChest.getTitle(); + title = enderChest.getTitle(menu); } else { return null; } @@ -265,10 +265,10 @@ protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlay menu.setTitle(title); var pair = CraftEventFactory.callInventoryOpenEventWithTitle(player, menu); - menu = pair.getSecond(); + AbstractContainerMenu opened = pair.getSecond(); // Menu is null if event is cancelled. - if (menu == null) { + if (opened == null) { return null; } @@ -277,11 +277,11 @@ protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlay title = PaperAdventure.asVanilla(newTitle); } - player.containerMenu = menu; - player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), title)); - player.initMenu(menu); + player.containerMenu = opened; + player.connection.send(new ClientboundOpenScreenPacket(opened.containerId, opened.getType(), title)); + player.initMenu(opened); - return menu.getBukkitView(); + return opened.getBukkitView(); } } diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java index 401b6083..5ffd2665 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenEnderChest.java @@ -14,7 +14,7 @@ public OpenEnderChest(@NotNull Player player) { } @Override - public @Nullable AbstractContainerMenu createMenu( + public @Nullable OpenEnderChestMenu createMenu( net.minecraft.world.entity.player.Player player, int i, boolean viewOnly diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java index 68970217..4eea3374 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/OpenInventory.java @@ -1,5 +1,6 @@ package com.lishid.openinv.internal.paper1_21_4.container; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import com.lishid.openinv.internal.common.container.slot.ContentCrafting; import com.lishid.openinv.internal.common.container.slot.ContentCursor; import com.lishid.openinv.internal.common.container.slot.ContentDrop; @@ -16,7 +17,6 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.bukkit.event.inventory.InventoryType; @@ -181,7 +181,7 @@ public ItemStack getOrDefault() { } @Override - public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) { + public @Nullable OpenChestMenu createMenu(Player player, int i, boolean viewOnly) { if (player instanceof ServerPlayer serverPlayer) { return new OpenInventoryMenu(this, serverPlayer, i, viewOnly); } diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java deleted file mode 100644 index 273ad9b1..00000000 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenChestMenu.java +++ /dev/null @@ -1,467 +0,0 @@ -package com.lishid.openinv.internal.paper1_21_4.container.menu; - -import com.google.common.base.Suppliers; -import com.lishid.openinv.internal.ISpecialInventory; -import com.lishid.openinv.internal.InternalOwned; -import com.lishid.openinv.internal.common.container.bukkit.OpenDummyInventory; -import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; -import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ChestMenu; -import net.minecraft.world.inventory.ClickType; -import net.minecraft.world.inventory.ContainerData; -import net.minecraft.world.inventory.ContainerListener; -import net.minecraft.world.inventory.ContainerSynchronizer; -import net.minecraft.world.inventory.DataSlot; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import org.bukkit.craftbukkit.inventory.CraftInventoryView; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -/** - * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. - */ -@SuppressWarnings("HidingField") -public abstract class OpenChestMenu> - extends AbstractContainerMenu { - - protected static final int BOTTOM_INVENTORY_SIZE = 36; - - protected final T container; - protected final ServerPlayer viewer; - protected final boolean viewOnly; - protected final boolean ownContainer; - protected final int topSize; - private CraftInventoryView, Inventory> bukkitEntity; - // Syncher fields - private @Nullable ContainerSynchronizer synchronizer; - private final List dataSlots = new ArrayList<>(); - private final IntList remoteDataSlots = new IntArrayList(); - private final List containerListeners = new ArrayList<>(); - private ItemStack remoteCarried = ItemStack.EMPTY; - private boolean suppressRemoteUpdates; - - protected OpenChestMenu( - @NotNull MenuType type, - int containerCounter, - @NotNull T container, - @NotNull ServerPlayer viewer, - boolean viewOnly - ) { - super(type, containerCounter); - this.container = container; - this.viewer = viewer; - this.viewOnly = viewOnly; - ownContainer = container.getOwnerHandle().equals(viewer); - topSize = getTopSize(viewer); - - preSlotSetup(); - - int upperRows = topSize / 9; - // View's upper inventory - our container - for (int row = 0; row < upperRows; ++row) { - for (int col = 0; col < 9; ++col) { - // x and y for client purposes, but hey, we're thorough here. - // Adapted from net.minecraft.world.inventory.ChestMenu - int x = 8 + col * 18; - int y = 18 + row * 18; - int index = row * 9 + col; - - // Guard against weird inventory sizes. - if (index >= container.getContainerSize()) { - addSlot(new SlotViewOnly(container, index, x, y)); - continue; - } - - Slot slot = getUpperSlot(index, x, y); - - addSlot(slot); - } - } - - // View's lower inventory - viewer inventory - int playerInvPad = (upperRows - 4) * 18; - for (int row = 0; row < 3; ++row) { - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + row * 18 + 103; - addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y)); - } - } - // Hotbar - for (int col = 0; col < 9; ++col) { - int x = 8 + col * 18; - int y = playerInvPad + 161; - addSlot(new Slot(viewer.getInventory(), col, x, y)); - } - } - - protected void preSlotSetup() { - } - - protected @NotNull Slot getUpperSlot(int index, int x, int y) { - Slot slot = new Slot(container, index, x, y); - if (viewOnly) { - return SlotViewOnly.wrap(slot); - } - return slot; - } - - - @Override - public final @NotNull CraftInventoryView, Inventory> getBukkitView() { - if (bukkitEntity == null) { - bukkitEntity = createBukkitEntity(); - } - - return bukkitEntity; - } - - protected @NotNull CraftInventoryView, Inventory> createBukkitEntity() { - Inventory top; - if (viewOnly) { - top = new OpenDummyInventory(container, container.getBukkitType()); - } else { - top = container.getBukkitInventory(); - } - return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) { - @Override - public @Nullable Inventory getInventory(int rawSlot) { - if (viewOnly) { - return null; - } - return super.getInventory(rawSlot); - } - - @Override - public int convertSlot(int rawSlot) { - if (viewOnly) { - return InventoryView.OUTSIDE; - } - return super.convertSlot(rawSlot); - } - - @Override - public @NotNull InventoryType.SlotType getSlotType(int slot) { - if (viewOnly) { - return InventoryType.SlotType.OUTSIDE; - } - return super.getSlotType(slot); - } - }; - } - - private int getTopSize(ServerPlayer viewer) { - MenuType menuType = getType(); - if (menuType == MenuType.GENERIC_9x1) { - return 9; - } else if (menuType == MenuType.GENERIC_9x2) { - return 18; - } else if (menuType == MenuType.GENERIC_9x3) { - return 27; - } else if (menuType == MenuType.GENERIC_9x4) { - return 36; - } else if (menuType == MenuType.GENERIC_9x5) { - return 45; - } else if (menuType == MenuType.GENERIC_9x6) { - return 54; - } - // This is a bit gross, but allows us a safe fallthrough. - return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE; - } - - /** - * Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake - * slots and respects {@link Slot#hasItem()}. - * - * @param itemStack the stack to quick-move - * @param rangeLow the start of the range of slots that can be moved to, inclusive - * @param rangeHigh the end of the range of slots that can be moved to, exclusive - * @param topDown whether to start at the top of the range or bottom - * @return whether the stack was modified as a result of being quick-moved - */ - @Override - protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) { - boolean modified = false; - boolean stackable = itemStack.isStackable(); - Slot firstEmpty = null; - - for (int index = topDown ? rangeHigh - 1 : rangeLow; - !itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh); - index += topDown ? -1 : 1 - ) { - Slot slot = slots.get(index); - // If the slot cannot be added to, check the next slot. - if (slot.isFake() || !slot.mayPlace(itemStack)) { - continue; - } - - if (slot.hasItem()) { - // If the item isn't stackable, check the next slot. - if (!stackable) { - continue; - } - // Otherwise, add as many as we can from our stack to the slot. - modified |= addToExistingStack(itemStack, slot); - } else { - // If this is the first empty slot, keep track of it for later use. - if (firstEmpty == null) { - firstEmpty = slot; - } - // If the item isn't stackable, we've located the slot we're adding it to, so we're done. - if (!stackable) { - break; - } - } - } - - // If the item hasn't been fully added yet, add as many as we can to the first open slot. - if (!itemStack.isEmpty() && firstEmpty != null) { - firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack)))); - firstEmpty.setChanged(); - modified = true; - } - - return modified; - } - - private static boolean addToExistingStack(ItemStack itemStack, Slot slot) { - ItemStack existing = slot.getItem(); - - // If the items aren't the same, we can't add our item. - if (!ItemStack.isSameItemSameComponents(itemStack, existing)) { - return false; - } - - int max = slot.getMaxStackSize(existing); - int existingCount = existing.getCount(); - - // If the stack is already full, we can't add more. - if (existingCount >= max) { - return false; - } - - int total = existingCount + itemStack.getCount(); - - // If the existing item can accept the entirety of our item, we're done! - if (total <= max) { - itemStack.setCount(0); - existing.setCount(total); - slot.setChanged(); - return true; - } - - // Otherwise, add as many as we can. - itemStack.shrink(max - existingCount); - existing.setCount(max); - slot.setChanged(); - return true; - } - - @Override - public void clicked(int i, int j, @NotNull ClickType clickType, @NotNull Player player) { - if (viewOnly) { - if (clickType == ClickType.QUICK_CRAFT) { - sendAllDataToRemote(); - } - return; - } - super.clicked(i, j, clickType, player); - } - - @Override - public boolean stillValid(@NotNull Player player) { - return true; - } - - // Overrides from here on are purely to modify the sync process to send placeholder items. - @Override - protected @NotNull Slot addSlot(@NotNull Slot slot) { - slot.index = this.slots.size(); - this.slots.add(slot); - this.lastSlots.add(ItemStack.EMPTY); - this.remoteSlots.add(ItemStack.EMPTY); - return slot; - } - - @Override - protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) { - this.dataSlots.add(dataSlot); - this.remoteDataSlots.add(0); - return dataSlot; - } - - @Override - protected void addDataSlots(ContainerData containerData) { - for (int i = 0; i < containerData.getCount(); i++) { - this.addDataSlot(DataSlot.forContainer(containerData, i)); - } - } - - @Override - public void addSlotListener(@NotNull ContainerListener containerListener) { - if (!this.containerListeners.contains(containerListener)) { - this.containerListeners.add(containerListener); - this.broadcastChanges(); - } - } - - @Override - public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { - this.synchronizer = containerSynchronizer; - this.sendAllDataToRemote(); - } - - @Override - public void sendAllDataToRemote() { - for (int index = 0; index < slots.size(); ++index) { - Slot slot = slots.get(index); - this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); - } - - remoteCarried = getCarried().copy(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); - } - - if (this.synchronizer != null) { - this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); - } - } - - @Override - public void broadcastCarriedItem() { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); - } - } - - @Override - public void removeSlotListener(@NotNull ContainerListener containerListener) { - this.containerListeners.remove(containerListener); - } - - @Override - public void broadcastChanges() { - for (int index = 0; index < this.slots.size(); ++index) { - Slot slot = this.slots.get(index); - ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); - Supplier supplier = Suppliers.memoize(itemstack::copy); - this.triggerSlotListeners(index, itemstack, supplier); - this.synchronizeSlotToRemote(index, itemstack, supplier); - } - - this.synchronizeCarriedToRemote(); - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot dataSlot = this.dataSlots.get(index); - int j = dataSlot.get(); - if (dataSlot.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, j); - } - - this.synchronizeDataSlotToRemote(index, j); - } - } - - @Override - public void broadcastFullState() { - for (int index = 0; index < this.slots.size(); ++index) { - ItemStack itemstack = this.slots.get(index).getItem(); - this.triggerSlotListeners(index, itemstack, itemstack::copy); - } - - for (int index = 0; index < this.dataSlots.size(); ++index) { - DataSlot containerproperty = this.dataSlots.get(index); - if (containerproperty.checkAndClearUpdateFlag()) { - this.updateDataSlotListeners(index, containerproperty.get()); - } - } - - this.sendAllDataToRemote(); - } - - private void updateDataSlotListeners(int i, int j) { - for (ContainerListener containerListener : this.containerListeners) { - containerListener.dataChanged(this, i, j); - } - } - - private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { - ItemStack itemStack1 = this.lastSlots.get(index); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemStack2 = supplier.get(); - this.lastSlots.set(index, itemStack2); - - for (ContainerListener containerListener : this.containerListeners) { - containerListener.slotChanged(this, index, itemStack2); - } - } - } - - private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { - if (!this.suppressRemoteUpdates) { - ItemStack itemStack1 = this.remoteSlots.get(i); - if (!ItemStack.matches(itemStack1, itemStack)) { - ItemStack itemstack2 = supplier.get(); - this.remoteSlots.set(i, itemstack2); - if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(this, i, itemstack2); - } - } - } - } - - private void synchronizeDataSlotToRemote(int index, int value) { - if (!this.suppressRemoteUpdates) { - int existing = this.remoteDataSlots.getInt(index); - if (existing != value) { - this.remoteDataSlots.set(index, value); - if (this.synchronizer != null) { - this.synchronizer.sendDataChange(this, index, value); - } - } - } - } - - private void synchronizeCarriedToRemote() { - if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { - this.remoteCarried = this.getCarried().copy(); - if (this.synchronizer != null) { - this.synchronizer.sendCarriedChange(this, this.remoteCarried); - } - } - } - - @Override - public void setRemoteCarried(ItemStack itemstack) { - this.remoteCarried = itemstack.copy(); - } - - @Override - public void suppressRemoteUpdates() { - this.suppressRemoteUpdates = true; - } - - @Override - public void resumeRemoteUpdates() { - this.suppressRemoteUpdates = false; - } - -} diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java index 902dd60b..6ba23c14 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenEnderChestMenu.java @@ -1,13 +1,14 @@ package com.lishid.openinv.internal.paper1_21_4.container.menu; import com.lishid.openinv.internal.common.container.OpenEnderChest; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; -public class OpenEnderChestMenu extends OpenChestMenu { +public class OpenEnderChestMenu extends OpenSyncMenu { public OpenEnderChestMenu( @NotNull OpenEnderChest enderChest, @@ -16,7 +17,7 @@ public OpenEnderChestMenu( boolean viewOnly ) { super( - com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(enderChest.getContainerSize()), + OpenChestMenu.getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java index 94c394fc..1bdbf469 100644 --- a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenInventoryMenu.java @@ -2,6 +2,7 @@ import com.google.common.base.Preconditions; import com.lishid.openinv.internal.common.container.bukkit.OpenDummyPlayerInventory; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import com.lishid.openinv.internal.common.container.slot.ContentDrop; import com.lishid.openinv.internal.common.container.slot.SlotViewOnly; import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory; @@ -23,7 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class OpenInventoryMenu extends OpenChestMenu { +public class OpenInventoryMenu extends OpenSyncMenu { private int offset; @@ -39,7 +40,7 @@ private static MenuType getMenuType(OpenInventory inventory, ServerPl size = ((int) Math.ceil(size / 9.0)) * 9; } - return com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(size); + return OpenChestMenu.getChestMenuType(size); } @Override diff --git a/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenSyncMenu.java b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenSyncMenu.java new file mode 100644 index 00000000..5a2d6162 --- /dev/null +++ b/internal/paper1_21_4/src/main/java/com/lishid/openinv/internal/paper1_21_4/container/menu/OpenSyncMenu.java @@ -0,0 +1,229 @@ +package com.lishid.openinv.internal.paper1_21_4.container.menu; + +import com.google.common.base.Suppliers; +import com.lishid.openinv.internal.ISpecialInventory; +import com.lishid.openinv.internal.InternalOwned; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; +import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.ContainerListener; +import net.minecraft.world.inventory.ContainerSynchronizer; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}. + */ +@SuppressWarnings("HidingField") +public abstract class OpenSyncMenu> + extends OpenChestMenu { + + // Syncher fields + private @Nullable ContainerSynchronizer synchronizer; + private final List dataSlots = new ArrayList<>(); + private final IntList remoteDataSlots = new IntArrayList(); + private final List containerListeners = new ArrayList<>(); + private ItemStack remoteCarried = ItemStack.EMPTY; + private boolean suppressRemoteUpdates; + + protected OpenSyncMenu( + @NotNull MenuType type, + int containerCounter, + @NotNull T container, + @NotNull ServerPlayer viewer, + boolean viewOnly + ) { + super(type, containerCounter, container, viewer, viewOnly); + } + + // Overrides from here on are purely to modify the sync process to send placeholder items. + @Override + protected @NotNull Slot addSlot(@NotNull Slot slot) { + slot.index = this.slots.size(); + this.slots.add(slot); + this.lastSlots.add(ItemStack.EMPTY); + this.remoteSlots.add(ItemStack.EMPTY); + return slot; + } + + @Override + protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) { + this.dataSlots.add(dataSlot); + this.remoteDataSlots.add(0); + return dataSlot; + } + + @Override + protected void addDataSlots(ContainerData containerData) { + for (int i = 0; i < containerData.getCount(); i++) { + this.addDataSlot(DataSlot.forContainer(containerData, i)); + } + } + + @Override + public void addSlotListener(@NotNull ContainerListener containerListener) { + if (!this.containerListeners.contains(containerListener)) { + this.containerListeners.add(containerListener); + this.broadcastChanges(); + } + } + + @Override + public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) { + this.synchronizer = containerSynchronizer; + this.sendAllDataToRemote(); + } + + @Override + public void sendAllDataToRemote() { + for (int index = 0; index < slots.size(); ++index) { + Slot slot = slots.get(index); + this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy()); + } + + remoteCarried = getCarried().copy(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + this.remoteDataSlots.set(index, this.dataSlots.get(index).get()); + } + + if (this.synchronizer != null) { + this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray()); + } + } + + @Override + public void broadcastCarriedItem() { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + + @Override + public void removeSlotListener(@NotNull ContainerListener containerListener) { + this.containerListeners.remove(containerListener); + } + + @Override + public void broadcastChanges() { + for (int index = 0; index < this.slots.size(); ++index) { + Slot slot = this.slots.get(index); + ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem(); + Supplier supplier = Suppliers.memoize(itemstack::copy); + this.triggerSlotListeners(index, itemstack, supplier); + this.synchronizeSlotToRemote(index, itemstack, supplier); + } + + this.synchronizeCarriedToRemote(); + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot dataSlot = this.dataSlots.get(index); + int j = dataSlot.get(); + if (dataSlot.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, j); + } + + this.synchronizeDataSlotToRemote(index, j); + } + } + + @Override + public void broadcastFullState() { + for (int index = 0; index < this.slots.size(); ++index) { + ItemStack itemstack = this.slots.get(index).getItem(); + this.triggerSlotListeners(index, itemstack, itemstack::copy); + } + + for (int index = 0; index < this.dataSlots.size(); ++index) { + DataSlot containerproperty = this.dataSlots.get(index); + if (containerproperty.checkAndClearUpdateFlag()) { + this.updateDataSlotListeners(index, containerproperty.get()); + } + } + + this.sendAllDataToRemote(); + } + + private void updateDataSlotListeners(int i, int j) { + for (ContainerListener containerListener : this.containerListeners) { + containerListener.dataChanged(this, i, j); + } + } + + private void triggerSlotListeners(int index, ItemStack itemStack, Supplier supplier) { + ItemStack itemStack1 = this.lastSlots.get(index); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemStack2 = supplier.get(); + this.lastSlots.set(index, itemStack2); + + for (ContainerListener containerListener : this.containerListeners) { + containerListener.slotChanged(this, index, itemStack2); + } + } + } + + private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier supplier) { + if (!this.suppressRemoteUpdates) { + ItemStack itemStack1 = this.remoteSlots.get(i); + if (!ItemStack.matches(itemStack1, itemStack)) { + ItemStack itemstack2 = supplier.get(); + this.remoteSlots.set(i, itemstack2); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(this, i, itemstack2); + } + } + } + } + + private void synchronizeDataSlotToRemote(int index, int value) { + if (!this.suppressRemoteUpdates) { + int existing = this.remoteDataSlots.getInt(index); + if (existing != value) { + this.remoteDataSlots.set(index, value); + if (this.synchronizer != null) { + this.synchronizer.sendDataChange(this, index, value); + } + } + } + } + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) { + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); + } + } + } + + @Override + public void setRemoteCarried(ItemStack itemstack) { + this.remoteCarried = itemstack.copy(); + } + + @Override + public void suppressRemoteUpdates() { + this.suppressRemoteUpdates = true; + } + + @Override + public void resumeRemoteUpdates() { + this.suppressRemoteUpdates = false; + } + +} diff --git a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java index 1784c44b..076d69a8 100644 --- a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java +++ b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/container/OpenInventory.java @@ -1,6 +1,7 @@ package com.lishid.openinv.internal.paper1_21_8.container; import com.lishid.openinv.internal.common.container.BaseOpenInventory; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -17,7 +18,7 @@ public OpenInventory(@NotNull Player bukkitPlayer) { } @Override - public @NotNull Component getTitle(@Nullable ServerPlayer viewer) { + public @NotNull Component getTitle(@Nullable ServerPlayer viewer, @Nullable OpenChestMenu menu) { MutableComponent component = Component.empty(); // Prefix for use with custom bitmap image fonts. if (owner.equals(viewer)) { @@ -33,6 +34,11 @@ public OpenInventory(@NotNull Player bukkitPlayer) { .withFont(ResourceLocation.parse("openinv:font/inventory")) .withColor(ChatFormatting.WHITE))); } + if (menu != null && menu.isViewOnly()) { + component.append(Component.translatableWithFallback("openinv.container.inventory.viewonly", "[RO] ")); + } else { + component.append(Component.translatableWithFallback("openinv.container.inventory.editable", "")); + } // Normal title: "Inventory - OwnerName" component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) .append(Component.translatable("container.inventory")) diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 6dde10d7..2a79c830 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -3,6 +3,7 @@ import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.reobf.container.OpenEnderChest; import com.lishid.openinv.internal.reobf.container.OpenInventory; +import com.lishid.openinv.internal.reobf.container.menu.OpenChestMenu; import com.lishid.openinv.util.JulLoggerAdapter; import com.mojang.authlib.GameProfile; import net.minecraft.nbt.CompoundTag; @@ -197,14 +198,14 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { } // See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider) - AbstractContainerMenu menu; + OpenChestMenu menu; Component title; if (inventory instanceof OpenInventory playerInv) { menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly); - title = playerInv.getTitle(player); + title = playerInv.getTitle(player, menu); } else if (inventory instanceof OpenEnderChest enderChest) { menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly); - title = enderChest.getTitle(); + title = enderChest.getTitle(menu); } else { return null; } @@ -219,18 +220,18 @@ private void injectPlayer(ServerPlayer player) throws IllegalAccessException { // resulting in a title change but no other state modifications (like cursor position). menu.setTitle(title); - menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false); + AbstractContainerMenu opened = CraftEventFactory.callInventoryOpenEvent(player, menu, false); // Menu is null if event is cancelled. - if (menu == null) { + if (opened == null) { return null; } - player.containerMenu = menu; - player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle())); - player.initMenu(menu); + player.containerMenu = opened; + player.connection.send(new ClientboundOpenScreenPacket(opened.containerId, opened.getType(), opened.getTitle())); + player.initMenu(opened); - return menu.getBukkitView(); + return opened.getBukkitView(); } } diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 593bff86..b68254a7 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -244,6 +244,7 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final } @Override + @Deprecated(forRemoval = true) public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { Permissions edit = null; HumanEntity target = inventory.getPlayer(); @@ -260,6 +261,17 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final return this.accessor.openInventory(player, inventory, viewOnly); } + AccessEqualMode accessMode; + if (Permissions.ACCESS_EQUAL_EDIT.hasPermission(player)) { + accessMode = AccessEqualMode.ALLOW; + } else if (Permissions.ACCESS_EQUAL_VIEW.hasPermission(player)) { + accessMode = AccessEqualMode.VIEW; + } else if (Permissions.ACCESS_EQUAL_DENY.hasPermission(player)) { + accessMode = AccessEqualMode.DENY; + } else { + accessMode = config.getAccessEqualMode(); + } + for (int level = 4; level > 0; --level) { String permission = "openinv.access.level." + level; // If the target doesn't have this access level... @@ -273,17 +285,22 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final } // If the viewer doesn't have an equal access level or equal access is a denial, deny. - if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) { + if (!player.hasPermission(permission) || accessMode == AccessEqualMode.DENY) { return null; } // Since this is a tie, setting decides view state. - if (config.getAccessEqualMode() == AccessEqualMode.VIEW) { + if (accessMode == AccessEqualMode.VIEW) { viewOnly = true; } break; } + return openInventory(player, inventory, viewOnly); + } + + @Override + public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) { return this.accessor.openInventory(player, inventory, viewOnly); } diff --git a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java index 1960ec88..1aa2da15 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/ClearInvCommand.java @@ -87,10 +87,11 @@ protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player o @Override protected void handle( @NotNull CommandSender sender, - @NotNull Player onlineTarget, + @NotNull PlayerAccess playerAccess, boolean accessInv, @NotNull String @NotNull [] args ) { + Player onlineTarget = playerAccess.player(); // Create the inventory final ISpecialInventory inv; try { diff --git a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java index 9cc9f63b..14abf66b 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/OpenInvCommand.java @@ -151,11 +151,12 @@ protected boolean deniedCommand(@NotNull CommandSender sender, @NotNull Player o @Override protected void handle( @NotNull CommandSender sender, - @NotNull Player target, + @NotNull PlayerAccess playerAccess, boolean accessInv, @NotNull String @NotNull [] args ) { Player player = (Player) sender; + Player target = playerAccess.player(); if (!config.doesNoArgsOpenSelf()) { // Record the target (accessInv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString()); @@ -172,7 +173,7 @@ protected void handle( } // Open the inventory - plugin.openInventory(player, inv); + plugin.openInventory(player, inv, playerAccess.viewOnly()); } } diff --git a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java index 268188c0..24dbba13 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java @@ -79,7 +79,7 @@ public void run() { } // Perform access checks and load target if necessary. - Player onlineTarget = access(sender, target, accessInv); + PlayerAccess onlineTarget = access(sender, target, accessInv); if (onlineTarget != null) { handle(sender, onlineTarget, accessInv, args); @@ -135,7 +135,11 @@ public void run() { * @param invPerms {@code true} to use inventory permissions, {@code false} for ender chest * @return the {@link Player} loaded or {@code null} if target is not accessible */ - protected @Nullable Player access(@NotNull CommandSender sender, @NotNull OfflinePlayer target, boolean invPerms) { + protected @Nullable PlayerAccess access( + @NotNull CommandSender sender, + @NotNull OfflinePlayer target, + boolean invPerms + ) { // Attempt to load online player dependent on permissions and configuration. Player onlineTarget = accessAsPlayer(sender, target); @@ -144,11 +148,11 @@ public void run() { } // Permissions checks. - if (deniedCommand(sender, onlineTarget, invPerms) || deniedAccess(sender, onlineTarget)) { + if (deniedCommand(sender, onlineTarget, invPerms)) { return null; } - return onlineTarget; + return accessGeneralized(sender, onlineTarget, invPerms); } /** @@ -203,30 +207,21 @@ protected abstract boolean deniedCommand( ); /** - * Check for a lack of generalized permissions for accessing the target. + * Check for generalized permissions for accessing the target. * By default, this is access levels and cross-world restrictions. * * @param sender the {@link CommandSender} attempting access * @param onlineTarget the {@link Player} being targeted by the command - * @return {@code true} if the sender does not have access to the target + * @param invPerms {@code true} to use inventory permissions, {@code false} for ender chest + * @return a {@link PlayerAccess} containing the accessed player and view mode, or {@code null} if denied */ - protected boolean deniedAccess(@NotNull CommandSender sender, @NotNull Player onlineTarget) { + protected @Nullable PlayerAccess accessGeneralized( + @NotNull CommandSender sender, + @NotNull Player onlineTarget, + boolean invPerms + ) { if (sender.equals(onlineTarget)) { - return false; - } - - // Protected check - for (int level = 4; level > 0; --level) { - String permission = "openinv.access.level." + level; - if (onlineTarget.hasPermission(permission) - && (!sender.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY)) { - lang.sendMessage( - sender, - "messages.error.permissionExempt", - new Replacement("%target%", onlineTarget.getDisplayName()) - ); - return true; - } + return null; } // Crossworld check @@ -238,10 +233,64 @@ protected boolean deniedAccess(@NotNull CommandSender sender, @NotNull Player on "messages.error.permissionCrossWorld", new Replacement("%target%", onlineTarget.getDisplayName()) ); - return true; + return null; + } + + boolean ownContainer = sender.equals(onlineTarget); + Permissions edit; + if (invPerms) { + edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER; + } else { + edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_EDIT_OTHER; } - return false; + boolean viewOnly = !edit.hasPermission(sender); + + if (ownContainer || (viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY)) { + return new PlayerAccess(onlineTarget, viewOnly); + } + + AccessEqualMode accessMode; + if (Permissions.ACCESS_EQUAL_EDIT.hasPermission(sender)) { + accessMode = AccessEqualMode.ALLOW; + } else if (Permissions.ACCESS_EQUAL_VIEW.hasPermission(sender)) { + accessMode = AccessEqualMode.VIEW; + } else if (Permissions.ACCESS_EQUAL_DENY.hasPermission(sender)) { + accessMode = AccessEqualMode.DENY; + } else { + accessMode = config.getAccessEqualMode(); + } + + for (int level = 4; level > 0; --level) { + String permission = "openinv.access.level." + level; + // If the target doesn't have this access level... + if (!onlineTarget.hasPermission(permission)) { + // If the viewer does have the access level, all good. + if (sender.hasPermission(permission)) { + break; + } + // Otherwise check next access level. + continue; + } + + // If the viewer doesn't have an equal access level or equal access is a denial, deny. + if (!sender.hasPermission(permission) || accessMode == AccessEqualMode.DENY) { + lang.sendMessage( + sender, + "messages.error.permissionExempt", + new Replacement("%target%", onlineTarget.getDisplayName()) + ); + return null; + } + + // Since this is a tie, setting decides view state. + if (accessMode == AccessEqualMode.VIEW) { + viewOnly = true; + } + break; + } + + return new PlayerAccess(onlineTarget, viewOnly); } /** @@ -254,7 +303,7 @@ protected boolean deniedAccess(@NotNull CommandSender sender, @NotNull Player on */ protected abstract void handle( @NotNull CommandSender sender, - @NotNull Player target, + @NotNull PlayerAccess target, boolean accessInv, @NotNull String @NotNull [] args ); @@ -273,4 +322,6 @@ public List onTabComplete( return TabCompleter.completeOnlinePlayer(sender, args[0]); } + protected record PlayerAccess(Player player, boolean viewOnly) {} + } diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index b0c78a65..e3da2390 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -66,6 +66,9 @@ permissions: openinv.access.level.2: false openinv.access.level.3: false openinv.access.level.4: false + openinv.access.equal.edit: false + openinv.access.equal.view: false + openinv.access.equal.deny: false # Spectate features openinv.spectate: children: From 172e1cd55b9870fb7ff76eef97ad3219f5dacadb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:58:40 -0500 Subject: [PATCH 332/340] Bump actions/download-artifact from 5 to 6 (#350) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 5399df29..001675d8 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download build - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: dist path: dist From f1b86b4eb1e7ce190cca38546560919f4650d687 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:59:24 -0500 Subject: [PATCH 333/340] Bump softprops/action-gh-release from 2.3.3 to 2.4.1 (#349) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.3 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.3.3...v2.4.1) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index 001675d8..bfa466f3 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -20,7 +20,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.3.3 + uses: softprops/action-gh-release@v2.4.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 16cba8a137530581216aa98a6fd3d1af5514761a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:59:45 -0500 Subject: [PATCH 334/340] Bump actions/upload-artifact from 4 to 5 (#348) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pull_request.yml | 2 +- .github/workflows/resource_pack_ci.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e5b669f..955b33dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: # Upload artifacts - name: Upload Distributable Jar id: upload-final - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: dist path: ./dist/* diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index f2dbf2f8..5ad508c1 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,7 +16,7 @@ jobs: run: | mkdir -p ./pr echo ${{ github.event.number }} > ./pr/pr_number - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: name: pr_number path: pr/ diff --git a/.github/workflows/resource_pack_ci.yml b/.github/workflows/resource_pack_ci.yml index f4f11e09..3c7bffd9 100644 --- a/.github/workflows/resource_pack_ci.yml +++ b/.github/workflows/resource_pack_ci.yml @@ -17,7 +17,7 @@ jobs: - name: Build resource pack id: upload-resource-pack - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: openinv-legibility-pack path: ./resource-pack/openinv-legibility-pack/ From 3df3d41a7fbb9d2794af12b41e12f83bba49da0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 20:21:44 +0000 Subject: [PATCH 335/340] Bump com.google.errorprone:error_prone_core from 2.43.0 to 2.45.0 (#356) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b15b4022..ddfe1660 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ annotations = "26.0.2-1" paperweight = "2.0.0-beta.17" shadow = "9.2.2" folia-scheduler-wrapper = "v0.0.3" -errorprone-core = "2.43.0" +errorprone-core = "2.45.0" errorprone-gradle = "4.2.0" slf4j = "2.0.17" From 08b43a8acab22692b5e04459cbfb4539896e7423 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:08:30 -0500 Subject: [PATCH 336/340] Bump softprops/action-gh-release from 2.4.1 to 2.5.0 (#355) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.4.1 to 2.5.0. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2.4.1...v2.5.0) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft_release.yml b/.github/workflows/draft_release.yml index bfa466f3..0655b2e0 100644 --- a/.github/workflows/draft_release.yml +++ b/.github/workflows/draft_release.yml @@ -20,7 +20,7 @@ jobs: - name: Create Release id: create-release - uses: softprops/action-gh-release@v2.4.1 + uses: softprops/action-gh-release@v2.5.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From fb409f8024fae8a09199de9bcb77a5b243b2103e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:48:28 -0500 Subject: [PATCH 337/340] Bump actions/checkout from 5 to 6 (#354) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/external_release.yml | 2 +- .github/workflows/resource_pack_ci.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 955b33dc..03d2e29b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: diff --git a/.github/workflows/external_release.yml b/.github/workflows/external_release.yml index 2ad596e3..a4c4dc76 100644 --- a/.github/workflows/external_release.yml +++ b/.github/workflows/external_release.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/resource_pack_ci.yml b/.github/workflows/resource_pack_ci.yml index 3c7bffd9..b4651e16 100644 --- a/.github/workflows/resource_pack_ci.yml +++ b/.github/workflows/resource_pack_ci.yml @@ -13,7 +13,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build resource pack id: upload-resource-pack From 9940b9d84037c61af010be586179f34b32142094 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 17 Dec 2025 15:11:56 -0500 Subject: [PATCH 338/340] Update to 1.21.11 (#358) --- internal/common/build.gradle.kts | 2 +- .../common/container/OpenInventory.java | 6 +- .../common/player/BaseOpenPlayer.java | 57 +++++++++++++++++-- .../internal/common/player/OpenPlayer.java | 35 ++++-------- internal/paper1_21_10/build.gradle.kts | 26 +++++++++ .../paper1_21_10/InternalAccessor.java | 31 ++++++++++ .../paper1_21_10/container/OpenInventory.java | 50 ++++++++++++++++ .../paper1_21_10/player/OpenPlayer.java | 35 ++++++++++++ .../paper1_21_10/player/PlayerManager.java | 26 +++++++++ .../paper1_21_5/player/OpenPlayer.java | 34 +++++------ .../paper1_21_8/player/OpenPlayer.java | 38 +++++-------- internal/spigot/build.gradle.kts | 4 +- .../container/bukkit/OpenPlayerInventory.java | 4 +- .../container/slot/ContentEquipment.java | 4 +- .../internal/reobf/player/OpenPlayer.java | 6 +- .../internal/reobf/player/PlayerManager.java | 6 +- plugin/build.gradle.kts | 1 + .../main/java/com/lishid/openinv/OpenInv.java | 13 +---- .../openinv/command/PlayerLookupCommand.java | 39 +++++-------- .../lishid/openinv/util/AccessEqualMode.java | 15 +++++ .../lishid/openinv/util/InternalAccessor.java | 14 +++-- .../openinv-legibility-pack/pack.mcmeta | 8 +-- settings.gradle.kts | 1 + 23 files changed, 320 insertions(+), 135 deletions(-) create mode 100644 internal/paper1_21_10/build.gradle.kts create mode 100644 internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/InternalAccessor.java create mode 100644 internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/container/OpenInventory.java create mode 100644 internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/OpenPlayer.java create mode 100644 internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/PlayerManager.java diff --git a/internal/common/build.gradle.kts b/internal/common/build.gradle.kts index 1e14a71b..71ca46d2 100644 --- a/internal/common/build.gradle.kts +++ b/internal/common/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) - paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT") } val spigot = tasks.register("spigotRelocations") { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java index 48204e49..641b4d54 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/container/OpenInventory.java @@ -5,7 +5,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FontDescription; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -25,13 +25,13 @@ public OpenInventory(@NotNull Player bukkitPlayer) { component.append( Component.translatableWithFallback("openinv.container.inventory.self", "") .withStyle(style -> style - .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) + .withFont(new FontDescription.Resource(Identifier.parse("openinv:font/inventory"))) .withColor(ChatFormatting.WHITE))); } else { component.append( Component.translatableWithFallback("openinv.container.inventory.other", "") .withStyle(style -> style - .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) + .withFont(new FontDescription.Resource(Identifier.parse("openinv:font/inventory"))) .withColor(ChatFormatting.WHITE))); } if (menu != null && menu.isViewOnly()) { diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java index 57cb1ab2..77e9fb50 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/BaseOpenPlayer.java @@ -1,10 +1,13 @@ package com.lishid.openinv.internal.common.player; +import com.lishid.openinv.event.OpenEvents; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.ValueOutput; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.entity.CraftPlayer; @@ -13,6 +16,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Set; public abstract class BaseOpenPlayer extends CraftPlayer { @@ -21,7 +27,7 @@ public abstract class BaseOpenPlayer extends CraftPlayer { * List of tags to always reset when saving. These are items that do not get written * if unset or empty, resulting in older values not being clobbered appropriately. * - * @see net.minecraft.world.entity.Entity#saveWithoutId(ValueOutput) + * @see net.minecraft.world.entity.Entity#saveWithoutId(ValueOutput, boolean, boolean, boolean) * @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(ValueOutput) * @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(ValueOutput) * @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(ValueOutput) @@ -41,8 +47,8 @@ public abstract class BaseOpenPlayer extends CraftPlayer { "Passengers", // ServerPlayer#addAdditionalSaveData(CompoundTag) // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - "warden_spawn_tracker", - "entered_nether_pos", // Replaces enteredNetherPosition + "warden_spawn_tracker", // No longer needed as of 1.21.11 + "entered_nether_pos", // Replaces enteredNetherPosition as of 1.21.6 "enteredNetherPosition", "respawn", // Replaces SpawnXyz fields as of 1.21.6 "SpawnX", @@ -86,7 +92,40 @@ public void loadData() { } @Override - public abstract void saveData(); + public void saveData() { + if (OpenEvents.saveCancelled(this)) { + return; + } + + trySave(this.getHandle()); + } + + protected abstract void trySave(ServerPlayer player); + + protected void saveSafe( + @NotNull ServerPlayer player, + @Nullable CompoundTag oldData, + @NotNull CompoundTag playerData, + @NotNull PlayerDataStorage worldNbtStorage + ) throws IOException { + // Revert certain special data values when offline. + revertSpecialValues(playerData, oldData); + + Path playerDataDir = worldNbtStorage.getPlayerDir().toPath(); + Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); + NbtIo.writeCompressed(playerData, tempFile); + Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); + Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); + safeReplaceFile(dataFile, tempFile, backupFile); + } + + protected void safeReplaceFile( + @NotNull Path dataFile, + @NotNull Path tempFile, + @NotNull Path backupFile + ) { + net.minecraft.util.Util.safeReplaceFile(dataFile, tempFile, backupFile); + } @Contract("null -> new") protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { @@ -107,7 +146,11 @@ public void loadData() { return oldData; } - protected void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { + protected void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + if (oldData == null) { + return; + } + // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); @@ -154,12 +197,14 @@ private void setTag( @Nullable T data ) { if (data == null) { - container.remove(key); + remove(container, key); } else { container.put(key, data); } } + protected abstract void remove(@NotNull CompoundTag tag, @NotNull String key); + public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) { return connection != null && !connection.isDisconnected(); } diff --git a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java index 79e43186..80f87129 100644 --- a/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java +++ b/internal/common/src/main/java/com/lishid/openinv/internal/common/player/OpenPlayer.java @@ -1,21 +1,16 @@ package com.lishid.openinv.internal.common.player; -import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; -import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueOutput; import org.bukkit.craftbukkit.CraftServer; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; -import java.nio.file.Files; -import java.nio.file.Path; - public class OpenPlayer extends BaseOpenPlayer { protected OpenPlayer( @@ -27,39 +22,29 @@ protected OpenPlayer( } @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); + protected void trySave(ServerPlayer player) { Logger logger = LogUtils.getLogger(); // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) { - PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo; + PlayerDataStorage worldNbtStorage = server.getServer().getPlayerList().playerIo; CompoundTag oldData = isOnline() ? null - : worldNBTStorage.load(player.nameAndId()).orElse(null); + : worldNbtStorage.load(player.nameAndId()).orElse(null); CompoundTag playerData = getWritableTag(oldData); ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData); player.saveWithoutId(valueOutput); - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, tempFile); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, tempFile, backupFile); + saveSafe(player, oldData, playerData, worldNbtStorage); } catch (Exception e) { LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } } + @Override + protected void remove(@NotNull CompoundTag tag, @NotNull String key) { + tag.remove(key); + } + } diff --git a/internal/paper1_21_10/build.gradle.kts b/internal/paper1_21_10/build.gradle.kts new file mode 100644 index 00000000..d727317a --- /dev/null +++ b/internal/paper1_21_10/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `openinv-base` + alias(libs.plugins.paperweight) +} + +configurations.all { + resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { + val paper = candidates.firstOrNull { + it.id.let { id -> + id is ModuleComponentIdentifier && id.module == "paper-api" + } + } + if (paper != null) { + select(paper) + } + because("module is written for Paper servers") + } +} + +dependencies { + implementation(project(":openinvapi")) + implementation(project(":openinvcommon")) + implementation(project(":openinvadaptercommon")) + + paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") +} diff --git a/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/InternalAccessor.java b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/InternalAccessor.java new file mode 100644 index 00000000..b6333bf0 --- /dev/null +++ b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/InternalAccessor.java @@ -0,0 +1,31 @@ +package com.lishid.openinv.internal.paper1_21_10; + +import com.lishid.openinv.internal.ISpecialPlayerInventory; +import com.lishid.openinv.internal.paper1_21_10.container.OpenInventory; +import com.lishid.openinv.internal.paper1_21_10.player.PlayerManager; +import com.lishid.openinv.util.lang.LanguageManager; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Logger; + +public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor { + + private final @NotNull PlayerManager manager; + + public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { + super(logger, lang); + manager = new PlayerManager(logger); + } + + @Override + public @NotNull PlayerManager getPlayerManager() { + return manager; + } + + @Override + public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) { + return new OpenInventory(player); + } + +} diff --git a/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/container/OpenInventory.java b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/container/OpenInventory.java new file mode 100644 index 00000000..3210726c --- /dev/null +++ b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/container/OpenInventory.java @@ -0,0 +1,50 @@ +package com.lishid.openinv.internal.paper1_21_10.container; + +import com.lishid.openinv.internal.common.container.BaseOpenInventory; +import com.lishid.openinv.internal.common.container.menu.OpenChestMenu; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FontDescription; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OpenInventory extends BaseOpenInventory { + + public OpenInventory(@NotNull Player bukkitPlayer) { + super(bukkitPlayer); + } + + @Override + public @NotNull Component getTitle(@Nullable ServerPlayer viewer, @Nullable OpenChestMenu menu) { + MutableComponent component = Component.empty(); + // Prefix for use with custom bitmap image fonts. + if (owner.equals(viewer)) { + component.append( + Component.translatableWithFallback("openinv.container.inventory.self", "") + .withStyle(style -> style + .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) + .withColor(ChatFormatting.WHITE))); + } else { + component.append( + Component.translatableWithFallback("openinv.container.inventory.other", "") + .withStyle(style -> style + .withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory"))) + .withColor(ChatFormatting.WHITE))); + } + if (menu != null && menu.isViewOnly()) { + component.append(Component.translatableWithFallback("openinv.container.inventory.viewonly", "[RO] ")); + } else { + component.append(Component.translatableWithFallback("openinv.container.inventory.editable", "")); + } + // Normal title: "Inventory - OwnerName" + component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName())) + .append(Component.translatable("container.inventory")) + .append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName())); + return component; + } + +} diff --git a/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/OpenPlayer.java b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/OpenPlayer.java new file mode 100644 index 00000000..0595e9b0 --- /dev/null +++ b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/OpenPlayer.java @@ -0,0 +1,35 @@ +package com.lishid.openinv.internal.paper1_21_10.player; + +import com.lishid.openinv.internal.common.player.PlayerManager; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.craftbukkit.CraftServer; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; + +public class OpenPlayer extends com.lishid.openinv.internal.common.player.OpenPlayer { + + protected OpenPlayer( + CraftServer server, + ServerPlayer entity, + PlayerManager manager + ) { + super(server, entity, manager); + } + + @Override + protected void safeReplaceFile( + @NotNull Path dataFile, + @NotNull Path tempFile, + @NotNull Path backupFile + ) { + net.minecraft.Util.safeReplaceFile(dataFile, tempFile, backupFile); + } + + @Override + protected void remove(@NotNull CompoundTag tag, @NotNull String key) { + tag.remove(key); + } + +} diff --git a/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/PlayerManager.java b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/PlayerManager.java new file mode 100644 index 00000000..3382bfaf --- /dev/null +++ b/internal/paper1_21_10/src/main/java/com/lishid/openinv/internal/paper1_21_10/player/PlayerManager.java @@ -0,0 +1,26 @@ +package com.lishid.openinv.internal.paper1_21_10.player; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Logger; + +public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager { + + public PlayerManager(@NotNull Logger logger) { + super(logger); + } + + @Override + protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlayer player) throws IllegalAccessException { + if (bukkitEntity == null) { + return; + } + + bukkitEntity.setAccessible(true); + + bukkitEntity.set(player, new OpenPlayer(server.server, player, this)); + } + +} diff --git a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java index 54aa7911..46b4aa87 100644 --- a/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java +++ b/internal/paper1_21_5/src/main/java/com/lishid/openinv/internal/paper1_21_5/player/OpenPlayer.java @@ -1,16 +1,13 @@ package com.lishid.openinv.internal.paper1_21_5.player; -import com.lishid.openinv.event.OpenEvents; import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import com.mojang.logging.LogUtils; -import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.bukkit.craftbukkit.CraftServer; +import org.jetbrains.annotations.NotNull; -import java.nio.file.Files; import java.nio.file.Path; public class OpenPlayer extends BaseOpenPlayer { @@ -20,12 +17,7 @@ protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager mana } @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); + protected void trySave(ServerPlayer player) { // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; @@ -35,20 +27,20 @@ public void saveData() { playerData = player.saveWithoutId(playerData); - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, tempFile); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, tempFile, backupFile); + saveSafe(player, oldData, playerData, worldNBTStorage); } catch (Exception e) { LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } } + @Override + protected void safeReplaceFile(@NotNull Path dataFile, @NotNull Path tempFile, @NotNull Path backupFile) { + net.minecraft.Util.safeReplaceFile(dataFile, tempFile, backupFile); + } + + @Override + protected void remove(@NotNull CompoundTag tag, @NotNull String key) { + tag.remove(key); + } + } diff --git a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java index 9462c8e4..0cad37a7 100644 --- a/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java +++ b/internal/paper1_21_8/src/main/java/com/lishid/openinv/internal/paper1_21_8/player/OpenPlayer.java @@ -1,20 +1,17 @@ package com.lishid.openinv.internal.paper1_21_8.player; -import com.lishid.openinv.event.OpenEvents; import com.lishid.openinv.internal.common.player.BaseOpenPlayer; import com.mojang.logging.LogUtils; -import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueOutput; import org.bukkit.craftbukkit.CraftServer; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; -import java.nio.file.Files; import java.nio.file.Path; public class OpenPlayer extends BaseOpenPlayer { @@ -28,39 +25,34 @@ protected OpenPlayer( } @Override - public void saveData() { - if (OpenEvents.saveCancelled(this)) { - return; - } - - ServerPlayer player = this.getHandle(); + protected void trySave(ServerPlayer player) { Logger logger = LogUtils.getLogger(); // See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman) try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) { - PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo; + PlayerDataStorage worldNbtStorage = server.getServer().getPlayerList().playerIo; CompoundTag oldData = isOnline() ? null - : worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null); + : worldNbtStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null); CompoundTag playerData = getWritableTag(oldData); ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData); player.saveWithoutId(valueOutput); - if (oldData != null) { - // Revert certain special data values when offline. - revertSpecialValues(playerData, oldData); - } - - Path playerDataDir = worldNBTStorage.getPlayerDir().toPath(); - Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat"); - NbtIo.writeCompressed(playerData, tempFile); - Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat"); - Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(dataFile, tempFile, backupFile); + saveSafe(player, oldData, playerData, worldNbtStorage); } catch (Exception e) { LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e); } } + @Override + protected void safeReplaceFile(@NotNull Path dataFile, @NotNull Path tempFile, @NotNull Path backupFile) { + net.minecraft.Util.safeReplaceFile(dataFile, tempFile, backupFile); + } + + @Override + protected void remove(@NotNull CompoundTag tag, @NotNull String key) { + tag.remove(key); + } + } diff --git a/internal/spigot/build.gradle.kts b/internal/spigot/build.gradle.kts index 94cf844f..5a74fb87 100644 --- a/internal/spigot/build.gradle.kts +++ b/internal/spigot/build.gradle.kts @@ -10,9 +10,9 @@ plugins { apply() apply() -val spigotVer = "1.21.10-R0.1-SNAPSHOT" +val spigotVer = "1.21.11-R0.1-SNAPSHOT" // Used by common adapter to relocate Craftbukkit classes to a versioned package. -rootProject.extra["craftbukkitPackage"] = "v1_21_R6" +rootProject.extra["craftbukkitPackage"] = "v1_21_R7" configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") { diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java index 04985b24..77aefe24 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/bukkit/OpenPlayerInventory.java @@ -4,8 +4,8 @@ import com.lishid.openinv.internal.reobf.container.BaseOpenInventory; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.player.Inventory; -import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftInventory; -import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java index 10f16100..bfec218e 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/container/slot/ContentEquipment.java @@ -6,8 +6,8 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import org.bukkit.craftbukkit.v1_21_R6.CraftEquipmentSlot; -import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R7.CraftEquipmentSlot; +import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftItemStack; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.PlayerInventory; import org.jetbrains.annotations.NotNull; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java index c200ccd9..33c630ac 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/OpenPlayer.java @@ -2,7 +2,6 @@ import com.lishid.openinv.event.OpenEvents; import com.mojang.logging.LogUtils; -import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NumericTag; @@ -10,11 +9,12 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.util.ProblemReporter; +import net.minecraft.util.Util; import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueOutput; -import org.bukkit.craftbukkit.v1_21_R6.CraftServer; -import org.bukkit.craftbukkit.v1_21_R6.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R7.CraftServer; +import org.bukkit.craftbukkit.v1_21_R7.entity.CraftPlayer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java index 2a79c830..d619f847 100644 --- a/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java +++ b/internal/spigot/src/main/java/com/lishid/openinv/internal/reobf/player/PlayerManager.java @@ -24,9 +24,9 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.craftbukkit.v1_21_R6.CraftServer; -import org.bukkit.craftbukkit.v1_21_R6.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_21_R6.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_21_R7.CraftServer; +import org.bukkit.craftbukkit.v1_21_R7.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R7.event.CraftEventFactory; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 95d899c2..d00a60a6 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(project(":openinvapi")) implementation(project(":openinvcommon")) implementation(project(":openinvadaptercommon")) + implementation(project(":openinvadapterpaper1_21_10")) implementation(project(":openinvadapterpaper1_21_8")) implementation(project(":openinvadapterpaper1_21_5")) implementation(project(":openinvadapterpaper1_21_4")) diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index b68254a7..e56d9d22 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -257,20 +257,11 @@ public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean viewOnly = edit != null && !edit.hasPermission(player); - if (ownContainer || (viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY)) { + if (ownContainer) { return this.accessor.openInventory(player, inventory, viewOnly); } - AccessEqualMode accessMode; - if (Permissions.ACCESS_EQUAL_EDIT.hasPermission(player)) { - accessMode = AccessEqualMode.ALLOW; - } else if (Permissions.ACCESS_EQUAL_VIEW.hasPermission(player)) { - accessMode = AccessEqualMode.VIEW; - } else if (Permissions.ACCESS_EQUAL_DENY.hasPermission(player)) { - accessMode = AccessEqualMode.DENY; - } else { - accessMode = config.getAccessEqualMode(); - } + AccessEqualMode accessMode = AccessEqualMode.getByPerm(player, config); for (int level = 4; level > 0; --level) { String permission = "openinv.access.level." + level; diff --git a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java index 24dbba13..9081ad1f 100644 --- a/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/command/PlayerLookupCommand.java @@ -220,21 +220,6 @@ protected abstract boolean deniedCommand( @NotNull Player onlineTarget, boolean invPerms ) { - if (sender.equals(onlineTarget)) { - return null; - } - - // Crossworld check - if (sender instanceof Player player - && !Permissions.ACCESS_CROSSWORLD.hasPermission(sender) - && !onlineTarget.getWorld().equals(player.getWorld())) { - lang.sendMessage( - sender, - "messages.error.permissionCrossWorld", - new Replacement("%target%", onlineTarget.getDisplayName()) - ); - return null; - } boolean ownContainer = sender.equals(onlineTarget); Permissions edit; @@ -246,21 +231,25 @@ protected abstract boolean deniedCommand( boolean viewOnly = !edit.hasPermission(sender); - if (ownContainer || (viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY)) { + if (ownContainer) { + // Skip other access checks for self. return new PlayerAccess(onlineTarget, viewOnly); } - AccessEqualMode accessMode; - if (Permissions.ACCESS_EQUAL_EDIT.hasPermission(sender)) { - accessMode = AccessEqualMode.ALLOW; - } else if (Permissions.ACCESS_EQUAL_VIEW.hasPermission(sender)) { - accessMode = AccessEqualMode.VIEW; - } else if (Permissions.ACCESS_EQUAL_DENY.hasPermission(sender)) { - accessMode = AccessEqualMode.DENY; - } else { - accessMode = config.getAccessEqualMode(); + // Crossworld check + if (sender instanceof Player player + && !Permissions.ACCESS_CROSSWORLD.hasPermission(sender) + && !onlineTarget.getWorld().equals(player.getWorld())) { + lang.sendMessage( + sender, + "messages.error.permissionCrossWorld", + new Replacement("%target%", onlineTarget.getDisplayName()) + ); + return null; } + AccessEqualMode accessMode = AccessEqualMode.getByPerm(sender, config); + for (int level = 4; level > 0; --level) { String permission = "openinv.access.level." + level; // If the target doesn't have this access level... diff --git a/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java b/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java index 8d36652a..a68914d9 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java +++ b/plugin/src/main/java/com/lishid/openinv/util/AccessEqualMode.java @@ -1,5 +1,7 @@ package com.lishid.openinv.util; +import com.lishid.openinv.util.config.Config; +import org.bukkit.permissions.Permissible; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -20,4 +22,17 @@ public enum AccessEqualMode { }; } + public static @NotNull AccessEqualMode getByPerm(@NotNull Permissible permissible, @NotNull Config config) { + if (Permissions.ACCESS_EQUAL_EDIT.hasPermission(permissible)) { + return AccessEqualMode.ALLOW; + } + if (Permissions.ACCESS_EQUAL_VIEW.hasPermission(permissible)) { + return AccessEqualMode.VIEW; + } + if (Permissions.ACCESS_EQUAL_DENY.hasPermission(permissible)) { + return AccessEqualMode.DENY; + } + return config.getAccessEqualMode(); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java index 671924e3..f5d77139 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java +++ b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java @@ -64,7 +64,7 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { - Version maxSupported = Version.of(1, 21, 10); + Version maxSupported = Version.of(1, 21, 11); Version minSupported = Version.of(1, 21, 1); // Ensure version is in supported range. @@ -84,10 +84,13 @@ public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) { } // Paper or a Paper fork, can use Mojang-mapped internals. - if (BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported) - && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 9))) { // 1.21.9, 1.21.10 + if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.11 return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang); } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 10)) + && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 9))) { // 1.21.9, 1.21.10 + return new com.lishid.openinv.internal.paper1_21_10.InternalAccessor(logger, lang); + } if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 8)) && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7, 1.21.8 return new com.lishid.openinv.internal.paper1_21_8.InternalAccessor(logger, lang); @@ -204,7 +207,10 @@ private String getSpigotReleaseLink() { return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.13"; } if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 9))) { - return "Unsupported; upgrade to 1.21.10: https://github.com/Jikoo/OpenInv/releases"; + return "Unsupported; upgrade to 1.21.10: https://github.com/Jikoo/OpenInv/releases/tag/5.1.15"; + } + if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 10))) { + return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.15"; } return "https://github.com/Jikoo/OpenInv/releases"; diff --git a/resource-pack/openinv-legibility-pack/pack.mcmeta b/resource-pack/openinv-legibility-pack/pack.mcmeta index 17314e78..a8a915fc 100644 --- a/resource-pack/openinv-legibility-pack/pack.mcmeta +++ b/resource-pack/openinv-legibility-pack/pack.mcmeta @@ -2,16 +2,16 @@ "pack": { "description": "Improve OpenInv's legibility", "min_format": 34, - "max_format": [69, 0], + "max_format": [75, 0], "pack_format": 64, - "supported_formats": [ 34, 69 ] + "supported_formats": [ 34, 75 ] }, "overlays": { "entries": [ { "min_format": 44, - "max_format": [69, 0], - "formats": [ 44, 69 ], + "max_format": [75, 0], + "formats": [ 44, 75 ], "directory": "openinv_44" }, { diff --git a/settings.gradle.kts b/settings.gradle.kts index e418ca9d..efbf5eb0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ if (!java.lang.Boolean.getBoolean("jitpack")) { val internals = listOf( "common", + "paper1_21_10", "paper1_21_8", "paper1_21_5", "paper1_21_4", From 4441cdcf0403837078d6ab8e67579a4738577938 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 17 Dec 2025 15:29:23 -0500 Subject: [PATCH 339/340] Bump version to 5.2.0 for release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3ed8b188..0f5f87e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.2.0-SNAPSHOT +version=5.2.0 description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration From c095a78013cd1925c68779c1a79da14dd246480c Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 17 Dec 2025 15:29:26 -0500 Subject: [PATCH 340/340] Bump version to 5.2.1-SNAPSHOT for development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0f5f87e3..79c27754 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project meta group=com.lishid.openinv -version=5.2.0 +version=5.2.1-SNAPSHOT description=A Bukkit plugin for opening normally-inaccessible inventories. # Gradle configuration