From a5255179d855df48096480ad194d0ba60ff00ca4 Mon Sep 17 00:00:00 2001 From: twme-ai <299940106+twme-ai@users.noreply.github.com> Date: Tue, 18 Aug 2026 19:50:13 +0000 Subject: [PATCH 1/2] feat: support VirtualEntities across server platforms --- .github/workflows/maven.yml | 59 +- .gitignore | 1 + README.md | 19 +- integration/mineflayer/package-lock.json | 961 ++++++++++++++++++ integration/mineflayer/package.json | 11 + integration/mineflayer/run-server-e2e.sh | 148 +++ integration/mineflayer/run-spigot-e2e.sh | 34 + integration/mineflayer/test.mjs | 130 +++ pom.xml | 67 +- .../dev/twme/debugstickpro/DebugStickPro.java | 44 +- .../blockdatautil/BlockDataSeparater.java | 228 +++-- .../ReflectiveBlockDataProperty.java | 256 +++++ .../blockdatautil/subdata/BrushableData.java | 59 -- .../subdata/CopperGolemStatuePoseData.java | 66 -- .../subdata/CrafterCraftingData.java | 48 - .../subdata/CrafterOrientationData.java | 68 -- .../subdata/CrafterTriggerData.java | 49 - .../subdata/CreakingHeartNaturalData.java | 48 - .../subdata/CreakingHeartStateData.java | 52 - .../subdata/DecoratedPotCrackedData.java | 48 - .../subdata/DirectionalData.java | 4 +- .../subdata/DriedGhastHydrationData.java | 61 -- .../blockdatautil/subdata/FlowerBedData.java | 61 -- .../subdata/HangingMossTipData.java | 48 - .../blockdatautil/subdata/HatchableData.java | 60 -- .../blockdatautil/subdata/JigsawData.java | 66 -- .../subdata/MossyCarpetBottomData.java | 48 - .../subdata/NoteBlockInstrumentData.java | 4 +- .../blockdatautil/subdata/OrientableData.java | 16 +- .../subdata/PotentSulfurStateData.java | 51 - .../subdata/SegmentableData.java | 61 -- .../subdata/SideChainingData.java | 66 -- .../subdata/SpeleothemThicknessData.java | 51 - .../SpeleothemVerticalDirectionData.java | 57 -- .../blockdatautil/subdata/TestBlockData.java | 63 -- .../subdata/TrialSpawnerData.java | 58 -- .../subdata/VaultOminousData.java | 49 - .../blockdatautil/subdata/VaultStateData.java | 67 -- .../MossyCarpetHeightData.java | 55 - .../MossyCarpetHeightEastData.java | 22 - .../MossyCarpetHeightNorthData.java | 22 - .../MossyCarpetHeightSouthData.java | 22 - .../MossyCarpetHeightWestData.java | 22 - .../commands/subcommands/GiveCommand.java | 9 +- .../commands/subcommands/HelpCommand.java | 5 +- .../commands/subcommands/ModeCommand.java | 13 +- .../commands/subcommands/ReloadCommand.java | 5 +- .../console/ConsoleGiveCommand.java | 9 +- .../console/ConsoleHelpCommand.java | 5 +- .../console/ConsoleReloadCommand.java | 5 +- .../display/ActionBarDisplayTask.java | 68 +- .../debugstickpro/display/ActionbarUtil.java | 8 +- .../listeners/ChunkLoadEventListener.java | 2 +- .../listeners/ChunkUnloadEventListener.java | 2 +- .../PlayerChangedWorldEventListener.java | 2 +- .../listeners/PlayerJoinListener.java | 2 +- .../PlayerLocaleChangeEventListener.java | 2 +- .../listeners/WorldUnloadEventListener.java | 2 +- .../localization/PlayerLanguageManager.java | 4 +- .../mode/copy/CopyLeftClick.java | 2 +- .../mode/copy/CopyRightClick.java | 8 +- .../mode/freeze/FreezeBlockManager.java | 115 ++- .../mode/freeze/SpecialBlockFilter.java | 33 +- .../debugstickpro/playerdata/PlayerData.java | 2 +- .../playerdata/PlayerDataManager.java | 12 +- .../scheduler/PlatformScheduler.java | 65 ++ .../debugstickpro/utils/DebugStickItem.java | 12 +- .../dev/twme/debugstickpro/utils/Log.java | 4 +- .../twme/debugstickpro/utils/TextUtil.java | 64 ++ src/main/resources/plugin.yml | 3 +- .../ReflectiveBlockDataPropertyTest.java | 76 ++ 71 files changed, 2204 insertions(+), 1765 deletions(-) create mode 100644 integration/mineflayer/package-lock.json create mode 100644 integration/mineflayer/package.json create mode 100755 integration/mineflayer/run-server-e2e.sh create mode 100755 integration/mineflayer/run-spigot-e2e.sh create mode 100644 integration/mineflayer/test.mjs create mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataProperty.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/BrushableData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CopperGolemStatuePoseData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterCraftingData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterOrientationData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterTriggerData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartNaturalData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartStateData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DecoratedPotCrackedData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DriedGhastHydrationData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/FlowerBedData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HangingMossTipData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HatchableData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/JigsawData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/MossyCarpetBottomData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/PotentSulfurStateData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SegmentableData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SideChainingData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemThicknessData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemVerticalDirectionData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TestBlockData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TrialSpawnerData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultOminousData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultStateData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightEastData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightNorthData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightSouthData.java delete mode 100644 src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightWestData.java create mode 100644 src/main/java/dev/twme/debugstickpro/scheduler/PlatformScheduler.java create mode 100644 src/main/java/dev/twme/debugstickpro/utils/TextUtil.java create mode 100644 src/test/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataPropertyTest.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4a210ff..5edf86f 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -16,7 +16,6 @@ on: jobs: build: - runs-on: ubuntu-latest steps: @@ -33,4 +32,60 @@ jobs: uses: actions/upload-artifact@v7 with: name: DebugStickPro - path: target/*.jar + path: target/DebugStickPro-0.5.3.jar + + runtime-test: + needs: build + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - platform: paper + minecraft: 1.19.4 + java: 17 + mode: mineflayer + - platform: folia + minecraft: 1.19.4 + java: 17 + mode: mineflayer + - platform: paper + minecraft: '26.2' + java: 25 + mode: startup + - platform: folia + minecraft: '26.2' + java: 25 + mode: startup + + name: ${{ matrix.platform }} ${{ matrix.minecraft }} + steps: + - uses: actions/checkout@v6 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v5 + with: + java-version: ${{ matrix.java }} + distribution: temurin + - name: Set up Node.js + if: matrix.mode == 'mineflayer' + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: integration/mineflayer/package-lock.json + - name: Download plugin JAR + uses: actions/download-artifact@v8 + with: + name: DebugStickPro + path: target + - name: Cache test servers + uses: actions/cache@v5 + with: + path: ~/.cache/debugstickpro-e2e + key: debugstickpro-${{ matrix.platform }}-${{ matrix.minecraft }} + - name: Run server integration test + env: + DSP_E2E_PLATFORM: ${{ matrix.platform }} + DSP_E2E_VERSION: ${{ matrix.minecraft }} + DSP_E2E_MODE: ${{ matrix.mode }} + run: integration/mineflayer/run-server-e2e.sh diff --git a/.gitignore b/.gitignore index 34bd79d..d560aad 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,7 @@ $RECYCLE.BIN/ *.lnk target/ +node_modules/ pom.xml.tag pom.xml.releaseBackup diff --git a/README.md b/README.md index d854de8..c7ae49b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,12 @@ > **⚠️ v0.5.0+ Requirement**: This plugin now requires [PacketEvents](https://github.com/retrooper/packetevents) to function properly. Please install PacketEvents before using v0.5.0+. -> Support for Paper 26.2+ +> Supports Spigot, Paper, and Folia from Minecraft 1.19.4 through 26.2. Paper is the primary platform. + +The compatibility matrix is exercised against both ends of the supported range. +Mineflayer verifies commands, MiniMessage item output, and VirtualEntities display +spawn/removal on 1.19.4; 26.2 receives startup and plugin-enable tests because +Mineflayer does not yet support that protocol. --- ## Features: ### Dynamically display block data: @@ -32,10 +37,10 @@ - **🔧 Packet-Based Block Freezing**: Complete rewrite using PacketEvents for client-side visual effects - **👻 Ghost-Free Experience**: No more server-side `BARRIER` blocks - smoother and cleaner freeze operations - **⚡ Enhanced Performance**: Improved freeze/unfreeze logic with better cleanup and tracking -- **🧹 Simplified Architecture**: Modernized codebase with PacketEvents and EntityLib integration +- **🧹 Simplified Architecture**: Modernized codebase with PacketEvents and VirtualEntities integration ### **Technical Improvements** -- **New Dependencies**: PacketEvents 2.9.4 and EntityLib integration +- **New Dependencies**: PacketEvents 2.13.0 and VirtualEntities integration - **API Modernization**: Updated to use standard Bukkit enums for better compatibility - **Better Physics Handling**: Enhanced BlockPhysicsEventListener for improved freeze functionality - **Advanced Tracking**: Sophisticated duplicate prevention and state management @@ -107,10 +112,14 @@ Same as regular debugging stick operation: ## How to install: ### Requirements -- **Java 25** or higher -- **Paper 26.2+** +- **Java 17-20** for legacy 1.19.4 servers, or the Java version required by your server release +- **JDK 25** to build DebugStickPro (the published JAR targets Java 17 for the full server range) +- **Spigot, Paper, or Folia 1.19.4-26.2** (Paper is recommended) - **[PacketEvents](https://github.com/retrooper/packetevents) 2.13.0+** (Required for v0.5.0+) +Spigot builds are tested locally with the official BuildTools. Paper and Folia +endpoint tests run for every pull request. + ### Installation Steps 1. Install [PacketEvents](https://github.com/retrooper/packetevents) if not already installed 2. Download the latest version of DebugStickPro from the [release page](https://github.com/TWME-TW/DebugStickPro/releases) diff --git a/integration/mineflayer/package-lock.json b/integration/mineflayer/package-lock.json new file mode 100644 index 0000000..17f2331 --- /dev/null +++ b/integration/mineflayer/package-lock.json @@ -0,0 +1,961 @@ +{ + "name": "debugstickpro-mineflayer-e2e", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "debugstickpro-mineflayer-e2e", + "dependencies": { + "mineflayer": "4.37.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@azure/msal-common": { + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.1.tgz", + "integrity": "sha512-nyxsA6NA4SVKh5YyRpbSXiMr7oQbwark7JU9LMeg6tJYTSPyAGkdx61wPT4gyxZfxlSxMMEyAsWaubBlNyIa1w==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.3.tgz", + "integrity": "sha512-CO+SE4weOsfJf+C5LM8argzvotrXw252/ZU6SM2Tz63fEblhH1uuVaaO4ISYFuN4Q6BhTo7I3qIdi8ydUQCqhw==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.16.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@types/node": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/node-rsa": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/node-rsa/-/node-rsa-1.1.4.tgz", + "integrity": "sha512-dB0ECel6JpMnq5ULvpUTunx3yNm8e/dIkv8Zu9p2c8me70xIRUUG3q+qXRwcSf9rN3oqamv4116iHy90dJGRpA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.24.tgz", + "integrity": "sha512-NRvUNC/JFGPJvqdAfEve8oginbM6V08u5NzLWpG8MwA2kTPOLnqk+wpwuPT+mp3aUsxyuT6m2gnrPuHYCruzEg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xboxreplay/xboxlive-auth": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@xboxreplay/xboxlive-auth/-/xboxlive-auth-5.1.0.tgz", + "integrity": "sha512-UngHHsehZbiTjyyNmo8HvdoUDKMID1U9uVfrpFWUK/2UxPuVTKy5n+CzZQ3S488sW5vOhgh0lHqqynT8ouwgvw==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha512-6i37w/+EhlWlGUJff3T/Q8u1RGmP5wgbiwYnOnbOqvtrPxT63/sYFyP9RcpxtxGymtfA075IvmOnL7ycNOWl3w==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/endian-toggle": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/endian-toggle/-/endian-toggle-0.0.0.tgz", + "integrity": "sha512-ShfqhXeHRE4TmggSlHXG8CMGIcsOsqDw/GcoPcosToE59Rm9e4aXaMhEQf2kPBsBRrKem1bbOAv5gOKnkliMFQ==", + "license": "MIT" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", + "license": "MIT" + }, + "node_modules/macaddress": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.5.4.tgz", + "integrity": "sha512-i8xVWoUjj2woYU8kbpQby86Kq7uF7xl2brtKREXUBWpfgqx1fKXEeYzDiVMVxA/IufC1d3xxwJRHtFCX+9IspA==", + "license": "MIT" + }, + "node_modules/minecraft-data": { + "version": "3.113.2", + "resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.113.2.tgz", + "integrity": "sha512-+3/9uO/xHcaY3ZA9iW22EGdGi8SAQKeMvbXZGrsd9zHrNhsx5KDo4nblfdwTFxm2xNJEwF67cFuWGSR8VS13Qw==", + "license": "MIT" + }, + "node_modules/minecraft-folder-path": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minecraft-folder-path/-/minecraft-folder-path-1.2.0.tgz", + "integrity": "sha512-qaUSbKWoOsH9brn0JQuBhxNAzTDMwrOXorwuRxdJKKKDYvZhtml+6GVCUrY5HRiEsieBEjCUnhVpDuQiKsiFaw==", + "license": "MIT" + }, + "node_modules/minecraft-protocol": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/minecraft-protocol/-/minecraft-protocol-1.67.0.tgz", + "integrity": "sha512-PpAQMm/MMiNZGL+uF2A7Gke1mT8R2h08c7ifOyLRNlaNluDFrJ57UarCkMQ+CETbZ/b6puEjkw4hLk4r/XY3hQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/node-rsa": "^1.1.4", + "@types/readable-stream": "^4.0.0", + "aes-js": "^3.1.2", + "buffer-equal": "^1.0.0", + "debug": "^4.3.2", + "endian-toggle": "^0.0.0", + "lodash.merge": "^4.3.0", + "minecraft-data": "^3.109.0", + "minecraft-folder-path": "^1.2.0", + "node-fetch": "^2.6.1", + "node-rsa": "^0.4.2", + "prismarine-auth": "^3.1.1", + "prismarine-chat": "^1.10.0", + "prismarine-nbt": "^2.5.0", + "prismarine-realms": "^1.2.0", + "protodef": "^1.17.0", + "readable-stream": "^4.1.0", + "uuid-1345": "^1.0.1", + "yggdrasil": "^1.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/mineflayer": { + "version": "4.37.1", + "resolved": "https://registry.npmjs.org/mineflayer/-/mineflayer-4.37.1.tgz", + "integrity": "sha512-kchZCJb1znzz8ZhE0+gLQ3e2t/9xUsqUy/IM/sGfceINxi3h6KXKY9luaUEa59vnD/x0OKwYdERY4sscm0ErNQ==", + "license": "MIT", + "dependencies": { + "minecraft-data": "^3.108.0", + "minecraft-protocol": "^1.66.0", + "mojangson": "^2.0.4", + "prismarine-biome": "^1.1.1", + "prismarine-block": "^1.22.0", + "prismarine-chat": "^1.7.1", + "prismarine-chunk": "^1.39.0", + "prismarine-entity": "^2.5.0", + "prismarine-item": "^1.17.0", + "prismarine-nbt": "^2.0.0", + "prismarine-physics": "^1.9.0", + "prismarine-recipe": "^1.5.0", + "prismarine-registry": "^1.10.0", + "prismarine-windows": "^2.9.0", + "prismarine-world": "^3.6.0", + "protodef": "^1.18.0", + "typed-emitter": "^1.0.0", + "uuid-1345": "^1.0.2", + "vec3": "^0.1.7" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/mojangson": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mojangson/-/mojangson-2.0.4.tgz", + "integrity": "sha512-HYmhgDjr1gzF7trGgvcC/huIg2L8FsVbi/KacRe6r1AswbboGVZDS47SOZlomPuMWvZLas8m9vuHHucdZMwTmQ==", + "license": "MIT", + "dependencies": { + "nearley": "^2.19.5" + } + }, + "node_modules/moo": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.3.tgz", + "integrity": "sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA==", + "license": "BSD-3-Clause" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-rsa": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", + "integrity": "sha512-Bvso6Zi9LY4otIZefYrscsUpo2mUpiAVIEmSZV2q41sP8tHZoert3Yu6zv4f/RXJqMNZQKCtnhDugIuCma23YA==", + "license": "MIT", + "dependencies": { + "asn1": "0.2.3" + } + }, + "node_modules/prismarine-auth": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prismarine-auth/-/prismarine-auth-3.1.1.tgz", + "integrity": "sha512-NuNrMGZdoigFKsvi1ZZgAEvNYNuE5qe6lo/tw+bqeNbkhpjHC0u1JNxLEujnfqduXI18e19PvUtWNMDl/gH7yw==", + "license": "MIT", + "dependencies": { + "@azure/msal-node": "^2.0.2", + "@xboxreplay/xboxlive-auth": "^5.1.0", + "debug": "^4.3.3", + "smart-buffer": "^4.1.0", + "uuid-1345": "^1.0.2" + } + }, + "node_modules/prismarine-biome": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prismarine-biome/-/prismarine-biome-1.4.0.tgz", + "integrity": "sha512-fD2WmjN8Zr/xA/jeMInReLgaDlznwA5xlaK529PzWuGzgjpc5ijVu1Lp1oqHyZn3WxOG/bRVtW1bU+tmgCurWA==", + "license": "MIT", + "peerDependencies": { + "prismarine-registry": "^1.1.0" + } + }, + "node_modules/prismarine-block": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/prismarine-block/-/prismarine-block-1.23.0.tgz", + "integrity": "sha512-j2UoU4KbXMvNlBw+aLkMOnEuMayYefznUfbrfv1VIbckG3RA9LpNWltOMHXuOR5YkHp8uIZPOclj95XC88jgGw==", + "license": "MIT", + "dependencies": { + "minecraft-data": "^3.38.0", + "prismarine-biome": "^1.1.0", + "prismarine-chat": "^1.5.0", + "prismarine-item": "^1.10.1", + "prismarine-nbt": "^2.0.0", + "prismarine-registry": "^1.1.0" + } + }, + "node_modules/prismarine-chat": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/prismarine-chat/-/prismarine-chat-1.13.0.tgz", + "integrity": "sha512-tvDbrQmJEoy09yLE5nnedGhQYxnRDaPRePMv7W39dFaHr2LGcA2JfCmH0vG5193+BsEFz3a5+0EpQSK8OW7YmA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.2", + "mojangson": "^2.0.1", + "prismarine-nbt": "^2.0.0", + "prismarine-registry": "^1.4.0" + } + }, + "node_modules/prismarine-chunk": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/prismarine-chunk/-/prismarine-chunk-1.41.0.tgz", + "integrity": "sha512-KldSl3pDzPU7jcttxSYWNJ40rJMdiUxYvTpqXzqiQ3lIdNL1HAuJHPdscHcqlKJRB9Hw/cbFcROR1h5ncavY+Q==", + "license": "MIT", + "dependencies": { + "prismarine-biome": "^1.2.0", + "prismarine-block": "^1.14.1", + "prismarine-nbt": "^2.2.1", + "prismarine-registry": "^1.1.0", + "smart-buffer": "^4.1.0", + "uint4": "^0.1.2", + "vec3": "^0.1.3", + "xxhash-wasm": "^0.4.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/prismarine-entity": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/prismarine-entity/-/prismarine-entity-2.6.0.tgz", + "integrity": "sha512-/LlZRLOpACiXk+GqoaKi0XPBFnNMjb1d4OIzuSCSEgNMK6FUo3Wnin5yeSZ7ff3Ztt7yagN9lX2jSOafn6IIzg==", + "license": "MIT", + "dependencies": { + "prismarine-chat": "^1.4.1", + "prismarine-item": "^1.11.2", + "prismarine-registry": "^1.4.0", + "vec3": "^0.1.4" + } + }, + "node_modules/prismarine-item": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/prismarine-item/-/prismarine-item-1.18.0.tgz", + "integrity": "sha512-8pEq6YfcneVvarvUFnex09a3+MR8/4NCQVyawIKAa3kh/g9dHLexoEcpQEgM3cmpg4gbLmspSiARGwed5uGhlg==", + "license": "MIT", + "dependencies": { + "prismarine-nbt": "^2.0.0", + "prismarine-registry": "^1.4.0" + } + }, + "node_modules/prismarine-nbt": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prismarine-nbt/-/prismarine-nbt-2.8.0.tgz", + "integrity": "sha512-5D6FUZq0PNtf3v/41ImDlwThVesOv5adyqCRMZLzmkUGEmRJNNh5C6AsnvrClBftXs+IF0yqPnZoj8kcNPiMGg==", + "license": "MIT", + "dependencies": { + "protodef": "^1.18.0" + } + }, + "node_modules/prismarine-physics": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/prismarine-physics/-/prismarine-physics-1.11.1.tgz", + "integrity": "sha512-GO9Pqqg5Khs0YxvPqRXYi3Ep3/OoxnL3RtfMknrgddY0ogpT91bTna6HDTQXVeBinfCB3WU9vXaFoTND/3xqyA==", + "license": "MIT", + "dependencies": { + "minecraft-data": "^3.0.0", + "prismarine-nbt": "^2.0.0", + "vec3": "^0.2.0" + } + }, + "node_modules/prismarine-physics/node_modules/vec3": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/vec3/-/vec3-0.2.0.tgz", + "integrity": "sha512-jOjU4zbCNWOKOGLHop1I0J2Ar4h5Zz76455LJhJKhiOJwI21QP6d5TnKklNZDKG+c5h71zWtPAbd9wh+Va57OQ==", + "license": "BSD" + }, + "node_modules/prismarine-realms": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prismarine-realms/-/prismarine-realms-1.6.0.tgz", + "integrity": "sha512-AwemW0vwxG9hQaFtg1twSV7eymB6QtYxGK0jjpxfdA2sdK15kU8jh8uD1o5XF0oxSMU+BbpzZMCmXtXq4QE6bw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.3", + "node-fetch": "^2.6.1" + } + }, + "node_modules/prismarine-recipe": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prismarine-recipe/-/prismarine-recipe-1.5.0.tgz", + "integrity": "sha512-GRZHbsyBIUgVNF10vFRv2YWZj86vokCT5EWX6iK6gfx6h4FapgZT29V2DNkjv5+hmdzBCLZvfx1/RYr8VPeoGQ==", + "license": "MIT", + "peerDependencies": { + "prismarine-registry": "^1.4.0" + } + }, + "node_modules/prismarine-registry": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/prismarine-registry/-/prismarine-registry-1.12.0.tgz", + "integrity": "sha512-OC5U6YrflY6OcAWRZEqe2HGZuNp0bIuP7H+oKEHD6rLfKNDxo8Ymx5eh2VvrZWnMVugpwID1Qj/UjA4MoCzNDw==", + "license": "MIT", + "dependencies": { + "minecraft-data": "^3.70.0", + "prismarine-block": "^1.17.1", + "prismarine-nbt": "^2.0.0" + } + }, + "node_modules/prismarine-windows": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/prismarine-windows/-/prismarine-windows-2.10.0.tgz", + "integrity": "sha512-ssXLGAr7W9JLvvLjYMoo1j4j6AdJaoIb0/HlqkWMWlQqvZJeiS4zyBjJY6+GtR4OzpjkEf6IvF5cNXhHFpbcZQ==", + "license": "MIT", + "dependencies": { + "prismarine-item": "^1.12.2", + "prismarine-registry": "^1.7.0", + "typed-emitter": "^2.1.0" + } + }, + "node_modules/prismarine-windows/node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "license": "MIT", + "optionalDependencies": { + "rxjs": "*" + } + }, + "node_modules/prismarine-world": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/prismarine-world/-/prismarine-world-3.7.0.tgz", + "integrity": "sha512-M5euvNjQ3vIk689BSa0YC6PBwpVY35Oc6q6KyZ0IqyFtI+cQ9em+8l5OTAK/uu9/gzDDhR7cmm9L2WXgTXBQCw==", + "license": "MIT", + "dependencies": { + "vec3": "^0.1.7" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/protodef": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/protodef/-/protodef-1.19.0.tgz", + "integrity": "sha512-94f3GR7pk4Qi5YVLaLvWBfTGUIzzO8hyo7vFVICQuu5f5nwKtgGDaeC1uXIu49s5to/49QQhEYeL0aigu1jEGA==", + "license": "MIT", + "dependencies": { + "lodash.reduce": "^4.6.0", + "protodef-validator": "^1.3.0", + "readable-stream": "^4.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/protodef-validator": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/protodef-validator/-/protodef-validator-1.4.0.tgz", + "integrity": "sha512-2y2coBolqCEuk5Kc3QwO7ThR+/7TZiOit4FrpAgl+vFMvq8w76nDhh09z08e2NQOdrgPLsN2yzXsvRvtADgUZQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.5.4" + }, + "bin": { + "protodef-validator": "cli.js" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "license": "CC0-1.0" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "license": "MIT", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/typed-emitter": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.4.0.tgz", + "integrity": "sha512-weBmoo3HhpKGgLBOYwe8EB31CzDFuaK7CCL+axXhUYhn4jo6DSkHnbefboCF5i4DQ2aMFe0C/FdTWcPdObgHyg==", + "license": "MIT" + }, + "node_modules/uint4": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uint4/-/uint4-0.1.2.tgz", + "integrity": "sha512-lhEx78gdTwFWG+mt6cWAZD/R6qrIj0TTBeH5xwyuDJyswLNlGe+KVlUPQ6+mx5Ld332pS0AMUTo9hIly7YsWxQ==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uuid-1345": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uuid-1345/-/uuid-1345-1.0.2.tgz", + "integrity": "sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==", + "license": "MIT", + "dependencies": { + "macaddress": "^0.5.1" + } + }, + "node_modules/vec3": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/vec3/-/vec3-0.1.10.tgz", + "integrity": "sha512-Sr1U3mYtMqCOonGd3LAN9iqy0qF6C+Gjil92awyK/i2OwiUo9bm7PnLgFpafymun50mOjnDcg4ToTgRssrlTcw==", + "license": "BSD" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/xxhash-wasm": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", + "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==", + "license": "MIT" + }, + "node_modules/yggdrasil": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/yggdrasil/-/yggdrasil-1.8.0.tgz", + "integrity": "sha512-r5bKOhkZ52DJ6q034uSkdsdZLoFVhOmfDOagRs6h/JX5W7+XIPOMb+peCbElhLEoIckwt43NCUoNQbydOzuPcQ==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "uuid": "^10.0.0" + } + }, + "node_modules/yggdrasil/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + } + } +} diff --git a/integration/mineflayer/package.json b/integration/mineflayer/package.json new file mode 100644 index 0000000..3bf7ac3 --- /dev/null +++ b/integration/mineflayer/package.json @@ -0,0 +1,11 @@ +{ + "name": "debugstickpro-mineflayer-e2e", + "private": true, + "type": "module", + "engines": { + "node": ">=22" + }, + "dependencies": { + "mineflayer": "4.37.1" + } +} diff --git a/integration/mineflayer/run-server-e2e.sh b/integration/mineflayer/run-server-e2e.sh new file mode 100755 index 0000000..4f63e2d --- /dev/null +++ b/integration/mineflayer/run-server-e2e.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +set -euo pipefail + +readonly PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +readonly PLATFORM="${DSP_E2E_PLATFORM:-paper}" +readonly MINECRAFT_VERSION="${DSP_E2E_VERSION:-1.19.4}" +readonly TEST_MODE="${DSP_E2E_MODE:-mineflayer}" +readonly MINEFLAYER_VERSION="${DSP_MINEFLAYER_VERSION:-${MINECRAFT_VERSION}}" +readonly JAVA_COMMAND="${DSP_E2E_JAVA:-java}" +readonly USERNAME="DSPTest" +readonly PORT="${DSP_E2E_PORT:-$(node -e 'const net=require("net");const server=net.createServer();server.listen(0,"127.0.0.1",()=>{console.log(server.address().port);server.close()})')}" +readonly TEMP_DIR="$(mktemp -d)" +readonly SERVER_DIR="${TEMP_DIR}/server" +readonly CACHE_DIR="${DSP_E2E_CACHE:-${HOME}/.cache/debugstickpro-e2e}" +readonly SERVER_JAR="${DSP_E2E_SERVER_JAR:-${CACHE_DIR}/${PLATFORM}-${MINECRAFT_VERSION}.jar}" +readonly PACKETEVENTS_JAR="${CACHE_DIR}/packetevents-spigot-2.13.0.jar" +readonly PACKETEVENTS_URL="https://github.com/retrooper/packetevents/releases/download/v2.13.0/packetevents-spigot-2.13.0.jar" +readonly PACKETEVENTS_SHA256="6d9ece0d87ee727a79a20b7ffbd432021609c6f52bafcb654fc2d3e9b6f064c5" +SERVER_PID="" + +cleanup() { + if [[ -n "${SERVER_PID}" ]] && kill -0 "${SERVER_PID}" 2>/dev/null; then + kill "${SERVER_PID}" 2>/dev/null || true + for _ in $(seq 1 10); do + if ! kill -0 "${SERVER_PID}" 2>/dev/null; then break; fi + sleep 1 + done + if kill -0 "${SERVER_PID}" 2>/dev/null; then kill -KILL "${SERVER_PID}" 2>/dev/null || true; fi + wait "${SERVER_PID}" 2>/dev/null || true + fi + rm -rf "${TEMP_DIR}" +} +trap cleanup EXIT + +if [[ "${PLATFORM}" != "paper" && "${PLATFORM}" != "folia" && "${PLATFORM}" != "spigot" ]]; then + echo "DSP_E2E_PLATFORM must be paper, folia, or spigot" >&2 + exit 2 +fi +if [[ "${TEST_MODE}" != "mineflayer" && "${TEST_MODE}" != "startup" ]]; then + echo "DSP_E2E_MODE must be mineflayer or startup" >&2 + exit 2 +fi + +mkdir -p "${CACHE_DIR}" "${SERVER_DIR}/plugins" +if [[ "${PLATFORM}" == "spigot" ]]; then + if [[ ! -f "${SERVER_JAR}" ]]; then + echo "Set DSP_E2E_SERVER_JAR to a Spigot jar built with BuildTools" >&2 + exit 2 + fi +else + server_metadata="$(curl --fail --silent --show-error --location \ + "https://fill.papermc.io/v3/projects/${PLATFORM}/versions/${MINECRAFT_VERSION}/builds/latest")" + server_url="$(jq -er '.downloads["server:default"].url' <<<"${server_metadata}")" + server_sha256="$(jq -er '.downloads["server:default"].checksums.sha256' <<<"${server_metadata}")" + if [[ ! -f "${SERVER_JAR}" ]] || ! echo "${server_sha256} ${SERVER_JAR}" | sha256sum --check --status; then + curl --fail --silent --show-error --location "${server_url}" --output "${SERVER_JAR}" + fi + echo "${server_sha256} ${SERVER_JAR}" | sha256sum --check --status +fi + +if [[ ! -f "${PACKETEVENTS_JAR}" ]] || ! echo "${PACKETEVENTS_SHA256} ${PACKETEVENTS_JAR}" | sha256sum --check --status; then + curl --fail --silent --show-error --location "${PACKETEVENTS_URL}" --output "${PACKETEVENTS_JAR}" +fi +echo "${PACKETEVENTS_SHA256} ${PACKETEVENTS_JAR}" | sha256sum --check --status + +if [[ ! -f "${PROJECT_DIR}/target/DebugStickPro-0.5.3.jar" ]]; then + mvn -B -f "${PROJECT_DIR}/pom.xml" package +fi +cp "${SERVER_JAR}" "${SERVER_DIR}/server.jar" +cp "${PACKETEVENTS_JAR}" "${SERVER_DIR}/plugins/packetevents.jar" +cp "${PROJECT_DIR}/target/DebugStickPro-0.5.3.jar" "${SERVER_DIR}/plugins/DebugStickPro.jar" + +printf 'eula=true\n' >"${SERVER_DIR}/eula.txt" +printf '%s\n' \ + "server-port=${PORT}" \ + 'online-mode=false' \ + 'use-native-transport=false' \ + 'spawn-protection=0' \ + 'view-distance=4' \ + 'simulation-distance=4' \ + 'level-type=minecraft:flat' \ + 'generate-structures=false' \ + 'difficulty=peaceful' \ + 'motd=DebugStickPro integration test' \ + >"${SERVER_DIR}/server.properties" + +offline_uuid="$(node -e ' + const { createHash } = require("node:crypto") + const bytes = createHash("md5").update(`OfflinePlayer:${process.argv[1]}`).digest() + bytes[6] = (bytes[6] & 0x0f) | 0x30 + bytes[8] = (bytes[8] & 0x3f) | 0x80 + const hex = bytes.toString("hex") + console.log(`${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`) +' "${USERNAME}")" +printf '[{"uuid":"%s","name":"%s","level":4,"bypassesPlayerLimit":false}]\n' \ + "${offline_uuid}" "${USERNAME}" >"${SERVER_DIR}/ops.json" + +pushd "${SERVER_DIR}" >/dev/null +"${JAVA_COMMAND}" \ + --add-opens=java.base/java.nio=ALL-UNNAMED \ + --add-opens=java.base/sun.nio.ch=ALL-UNNAMED \ + -DPaper.IgnoreJavaVersion=true \ + -Dio.netty.tryReflectionSetAccessible=true \ + -Xms512M -Xmx1G -jar server.jar --nogui >server.log 2>&1 & +SERVER_PID="$!" +popd >/dev/null + +for _ in $(seq 1 240); do + if grep -q 'Done (' "${SERVER_DIR}/server.log" 2>/dev/null; then break; fi + if ! kill -0 "${SERVER_PID}" 2>/dev/null; then + tail -200 "${SERVER_DIR}/server.log" >&2 + exit 1 + fi + sleep 1 +done +if ! grep -q 'Done (' "${SERVER_DIR}/server.log"; then + tail -200 "${SERVER_DIR}/server.log" >&2 + echo "${PLATFORM} ${MINECRAFT_VERSION} did not start within 240 seconds" >&2 + exit 1 +fi +if ! grep -q 'Enabling DebugStickPro' "${SERVER_DIR}/server.log" || \ + grep -Eq 'Error occurred while enabling DebugStickPro|Could not load.*DebugStickPro|Could not load plugin.*DebugStickPro' "${SERVER_DIR}/server.log"; then + tail -200 "${SERVER_DIR}/server.log" >&2 + echo "DebugStickPro did not enable cleanly" >&2 + exit 1 +fi + +if [[ "${TEST_MODE}" == "mineflayer" ]]; then + ( + flock 9 + if ! node -e "require.resolve('mineflayer', { paths: ['${PROJECT_DIR}/integration/mineflayer'] })" >/dev/null 2>&1; then + npm ci --prefix "${PROJECT_DIR}/integration/mineflayer" --silent + fi + ) 9>"${CACHE_DIR}/npm-install.lock" + if ! DSP_E2E_PORT="${PORT}" DSP_MINEFLAYER_VERSION="${MINEFLAYER_VERSION}" \ + DSP_E2E_USERNAME="${USERNAME}" node "${PROJECT_DIR}/integration/mineflayer/test.mjs"; then + tail -240 "${SERVER_DIR}/server.log" >&2 + exit 1 + fi +fi + +if grep -Eq 'ThreadedRegionizer.*(ERROR|WARN)|not owned by current region|IllegalStateException:.*region' "${SERVER_DIR}/server.log"; then + tail -200 "${SERVER_DIR}/server.log" >&2 + echo "Folia region-thread violation detected" >&2 + exit 1 +fi + +echo "${PLATFORM} ${MINECRAFT_VERSION} ${TEST_MODE} E2E passed" diff --git a/integration/mineflayer/run-spigot-e2e.sh b/integration/mineflayer/run-spigot-e2e.sh new file mode 100755 index 0000000..a738729 --- /dev/null +++ b/integration/mineflayer/run-spigot-e2e.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +readonly PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +readonly MINECRAFT_VERSION="${DSP_E2E_VERSION:-1.19.4}" +readonly CACHE_DIR="${DSP_E2E_CACHE:-${HOME}/.cache/debugstickpro-e2e}" +readonly BUILD_DIR="${CACHE_DIR}/buildtools-${MINECRAFT_VERSION}" +readonly BUILD_TOOLS_JAR="${CACHE_DIR}/BuildTools.jar" +readonly SPIGOT_JAR="${CACHE_DIR}/spigot-${MINECRAFT_VERSION}.jar" +readonly BUILD_TOOLS_URL="https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" +readonly BUILD_JAVA_COMMAND="${DSP_BUILDTOOLS_JAVA:-java}" + +mkdir -p "${CACHE_DIR}" "${BUILD_DIR}" +if [[ ! -f "${BUILD_TOOLS_JAR}" ]]; then + curl --fail --silent --show-error --location "${BUILD_TOOLS_URL}" --output "${BUILD_TOOLS_JAR}" +fi + +if [[ ! -f "${SPIGOT_JAR}" ]]; then + pushd "${BUILD_DIR}" >/dev/null + "${BUILD_JAVA_COMMAND}" -jar "${BUILD_TOOLS_JAR}" --rev "${MINECRAFT_VERSION}" --compile SPIGOT + popd >/dev/null + built_jar="$(find "${BUILD_DIR}" -maxdepth 1 -type f -name "spigot-${MINECRAFT_VERSION}*.jar" -print -quit)" + if [[ -z "${built_jar}" ]]; then + echo "BuildTools did not produce Spigot ${MINECRAFT_VERSION}" >&2 + exit 1 + fi + cp "${built_jar}" "${SPIGOT_JAR}" +fi + +DSP_E2E_PLATFORM=spigot \ +DSP_E2E_VERSION="${MINECRAFT_VERSION}" \ +DSP_E2E_SERVER_JAR="${SPIGOT_JAR}" \ +DSP_E2E_JAVA="${DSP_SPIGOT_JAVA:-${BUILD_JAVA_COMMAND}}" \ +"${PROJECT_DIR}/integration/mineflayer/run-server-e2e.sh" diff --git a/integration/mineflayer/test.mjs b/integration/mineflayer/test.mjs new file mode 100644 index 0000000..2ba9d39 --- /dev/null +++ b/integration/mineflayer/test.mjs @@ -0,0 +1,130 @@ +import assert from 'node:assert/strict' +import mineflayer from 'mineflayer' + +const port = Number(process.env.DSP_E2E_PORT) +const version = process.env.DSP_MINEFLAYER_VERSION +const username = process.env.DSP_E2E_USERNAME ?? 'DSPTest' +const timeoutMs = 45_000 + +if (!Number.isInteger(port) || !version) { + throw new Error('DSP_E2E_PORT and DSP_MINEFLAYER_VERSION are required') +} + +const bot = mineflayer.createBot({ + host: '127.0.0.1', + port, + username, + version, + auth: 'offline' +}) + +const messages = [] +const entityPackets = [] +bot.on('messagestr', message => messages.push(message)) +bot._client.on('packet', (data, metadata) => { + if (metadata.name.includes('spawn_entity') || metadata.name === 'entity_metadata' || metadata.name === 'entity_destroy') { + entityPackets.push({ + name: metadata.name, + entityId: data?.entityId, + entityIds: data?.entityIds, + type: data?.type + }) + } +}) + +function waitFor(predicate, description, timeout = timeoutMs) { + return new Promise((resolve, reject) => { + const started = Date.now() + const timer = setInterval(() => { + try { + const result = predicate() + if (result) { + clearInterval(timer) + resolve(result) + } else if (Date.now() - started >= timeout) { + clearInterval(timer) + reject(new Error( + `Timed out waiting for ${description}. Messages: ${messages.join(' | ')}. ` + + `Recent entity packets: ${JSON.stringify(entityPackets.slice(-80))}` + )) + } + } catch (error) { + clearInterval(timer) + reject(error) + } + }, 50) + }) +} + +try { + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error('Timed out waiting for Mineflayer spawn')), timeoutMs) + bot.once('spawn', () => { + clearTimeout(timeout) + resolve() + }) + bot.once('error', reject) + bot.once('kicked', reason => reject(new Error(`Mineflayer was kicked: ${reason}`))) + }) + + bot.chat('/dsp give') + const debugStick = await waitFor( + () => bot.inventory.items().find(item => item.name === 'blaze_rod'), + 'DebugStickPro item' + ) + await bot.equip(debugStick, 'hand') + await bot.waitForTicks(20) + + bot.chat('/dsp mode freeze') + await waitFor( + () => messages.some(message => /freeze/i.test(message)), + 'freeze mode confirmation' + ) + await bot.waitForTicks(10) + + const targetPosition = bot.entity.position.floored().offset(1, 0, 0) + bot.chat(`/setblock ${targetPosition.x} ${targetPosition.y} ${targetPosition.z} minecraft:stone`) + const target = await waitFor(() => { + const block = bot.blockAt(targetPosition) + return block?.name === 'stone' ? block : null + }, 'integration-test stone block') + assert.ok(target) + await bot.waitForTicks(10) + await bot.lookAt(target.position.offset(0.5, 0.5, 0.5), true) + + const packetOffset = entityPackets.length + await bot.activateBlock(target) + + const displays = await waitFor(() => { + const packets = entityPackets.slice(packetOffset) + const itemDisplay = packets.find(packet => + packet.name.includes('spawn_entity') && packet.type === bot.registry.entitiesByName.item_display.id + ) + const blockDisplay = packets.find(packet => + packet.name.includes('spawn_entity') && packet.type === bot.registry.entitiesByName.block_display.id + ) + return itemDisplay && blockDisplay ? { itemDisplay, blockDisplay } : null + }, 'VirtualEntities Item Display and Block Display') + + await waitFor( + () => { + const destroyPackets = entityPackets.slice(packetOffset) + .filter(packet => packet.name === 'entity_destroy') + const removed = entityId => destroyPackets.some(packet => + Array.from(packet.entityIds ?? []).includes(entityId) + ) + return removed(displays.itemDisplay.entityId) && removed(displays.blockDisplay.entityId) + }, + 'VirtualEntities removal' + ) + + console.log(JSON.stringify({ + version, + command: true, + miniMessageItem: debugStick.customName?.toString() ?? debugStick.displayName, + virtualEntities: ['item_display', 'block_display'], + removal: true + })) +} finally { + bot.quit('DebugStickPro E2E complete') +} diff --git a/pom.xml b/pom.xml index 6b94a40..d98cbcb 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,10 @@ 25 + 17 + 26.2.build.112-stable + 5.2.0 + 4.26.1 UTF-8 @@ -23,14 +27,42 @@ maven-compiler-plugin 3.15.0 - ${java.version} - ${java.version} + ${java.release} + + org.apache.maven.plugins + maven-enforcer-plugin + 3.6.2 + + + + enforce + + + + + [${java.version},26) + + + + + + org.apache.maven.plugins maven-shade-plugin 3.6.1 + + + net.kyori:adventure-api:${adventure.runtime.version} + net.kyori:adventure-key:${adventure.runtime.version} + net.kyori:adventure-text-minimessage:${adventure.runtime.version} + net.kyori:adventure-text-serializer-legacy:${adventure.runtime.version} + net.kyori:examination-api:1.3.0 + net.kyori:examination-string:1.3.0 + + package @@ -83,6 +115,10 @@ codemc-snapshots https://repo.codemc.io/repository/maven-snapshots/ + + jitpack + https://jitpack.io + twme-repo-releases TWME Repository @@ -99,7 +135,7 @@ io.papermc.paper paper-api - 26.2.build.48-alpha + ${paper.version} provided @@ -121,10 +157,27 @@ provided - io.github.tofaa2 - spigot - 3.3.3+119e9e1-SNAPSHOT - compile + com.github.twme-ai + VirtualEntities + v0.9.0 + + + net.kyori + adventure-api + ${adventure.compile.version} + provided + + + net.kyori + adventure-text-minimessage + ${adventure.compile.version} + provided + + + net.kyori + adventure-text-serializer-legacy + ${adventure.compile.version} + provided junit diff --git a/src/main/java/dev/twme/debugstickpro/DebugStickPro.java b/src/main/java/dev/twme/debugstickpro/DebugStickPro.java index 2200e98..95429ee 100644 --- a/src/main/java/dev/twme/debugstickpro/DebugStickPro.java +++ b/src/main/java/dev/twme/debugstickpro/DebugStickPro.java @@ -7,7 +7,6 @@ import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitScheduler; import com.github.retrooper.packetevents.PacketEvents; @@ -41,12 +40,12 @@ import dev.twme.debugstickpro.mode.freeze.FreezePacketLayer; import dev.twme.debugstickpro.playerdata.PlayerData; import dev.twme.debugstickpro.playerdata.PlayerDataManager; +import dev.twme.debugstickpro.scheduler.PlatformScheduler; import dev.twme.debugstickpro.utils.DebugStickItem; import dev.twme.debugstickpro.utils.Log; import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; -import me.tofaa.entitylib.APIConfig; -import me.tofaa.entitylib.EntityLib; -import me.tofaa.entitylib.spigot.SpigotEntityLibPlatform; +import io.github.twme.virtualentities.VirtualEntities; +import io.github.twme.virtualentities.VirtualEntityManager; public final class DebugStickPro extends JavaPlugin { /** @@ -54,11 +53,12 @@ public final class DebugStickPro extends JavaPlugin { */ private static DebugStickPro instance; + private VirtualEntityManager virtualEntityManager; /** * This is the task ID of the action bar task */ - private int actionBarTaskID; + private PlatformScheduler.Cancellable actionBarTask; /** * This is the version of the plugin @@ -92,13 +92,7 @@ public void onEnable() { //Initialize! PacketEvents.getAPI().init(); FreezePacketLayer.initialize(); - - SpigotEntityLibPlatform platform = new SpigotEntityLibPlatform(this); - APIConfig settings = new APIConfig(PacketEvents.getAPI()) - .tickTickables() - .usePlatformLogger(); - - EntityLib.init(platform, settings); + virtualEntityManager = VirtualEntities.create(); boolean isCoreProtectLoaded = CoreProtectUtil.initCoreProtect(); if (!isCoreProtectLoaded) { @@ -144,8 +138,13 @@ public void onReload() { @Override public void onDisable() { + unregisterTasks(); FreezePacketLayer.shutdown(); FreezeBlockManager.removeOnServerClose(); + if (virtualEntityManager != null) { + virtualEntityManager.close(); + virtualEntityManager = null; + } //Terminate the instance (clean up process) PacketEvents.getAPI().terminate(); } @@ -155,7 +154,7 @@ public void onDisable() { */ public void onServerReloadCommand() { for (Player player : Bukkit.getOnlinePlayers()) { - PlayerLanguageManager.setPlayerLocale(player.getUniqueId(), player.locale().toString()); + PlayerLanguageManager.setPlayerLocale(player.getUniqueId(), player.getLocale()); UUID playerUUID = player.getUniqueId(); PlayerDataManager.setPlayerData(playerUUID, new PlayerData()); @@ -218,8 +217,11 @@ private void registerListener(Listener listener) { private void registerTasks() { // this is a task that will display the action bar - BukkitScheduler scheduler = Bukkit.getServer().getScheduler(); - actionBarTaskID = scheduler.scheduleSyncRepeatingTask(this, new ActionBarDisplayTask(), 0L, ConfigFile.ActionBarDisplay.UpdateInterval); + actionBarTask = PlatformScheduler.runGlobalAtFixedRate( + this, + new ActionBarDisplayTask(this), + ConfigFile.ActionBarDisplay.UpdateInterval + ); } /** @@ -227,7 +229,10 @@ private void registerTasks() { */ private void unregisterTasks() { - Bukkit.getScheduler().cancelTask(actionBarTaskID); + if (actionBarTask != null) { + actionBarTask.cancel(); + actionBarTask = null; + } } /** @@ -237,4 +242,11 @@ private void unregisterTasks() { public static DebugStickPro getInstance() { return instance; } + + public VirtualEntityManager getVirtualEntityManager() { + if (virtualEntityManager == null) { + throw new IllegalStateException("VirtualEntities is not initialized"); + } + return virtualEntityManager; + } } diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java index 2a06af7..9117249 100644 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java +++ b/src/main/java/dev/twme/debugstickpro/blockdatautil/BlockDataSeparater.java @@ -5,7 +5,6 @@ import dev.twme.debugstickpro.blockdatautil.subdata.brewingstandbottle.BrewingStandBottle_1; import dev.twme.debugstickpro.blockdatautil.subdata.brewingstandbottle.BrewingStandBottle_2; import dev.twme.debugstickpro.blockdatautil.subdata.chiseledbookshelfslot.*; -import dev.twme.debugstickpro.blockdatautil.subdata.mossycarpetheight.*; import dev.twme.debugstickpro.blockdatautil.subdata.multiplefacing.*; import dev.twme.debugstickpro.blockdatautil.subdata.redstonewire.RedstoneWireEastData; import dev.twme.debugstickpro.blockdatautil.subdata.redstonewire.RedstoneWireNorthData; @@ -13,6 +12,7 @@ import dev.twme.debugstickpro.blockdatautil.subdata.redstonewire.RedstoneWireWestData; import dev.twme.debugstickpro.blockdatautil.subdata.wallheight.*; import dev.twme.debugstickpro.config.ConfigFile; +import dev.twme.debugstickpro.localization.Lang; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; @@ -22,11 +22,12 @@ import org.bukkit.entity.Player; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; /** * Separate BlockData into SubBlockData. @@ -37,7 +38,7 @@ public class BlockDataSeparater { * This is a cache for results of BlockDataSeparater.separate(BlockData). */ - private static final HashMap> cache = new HashMap<>(); + private static final Map> cache = new ConcurrentHashMap<>(); /** * Separate BlockData into SubBlockData. @@ -69,25 +70,6 @@ public static void clearCache() { cache.clear(); } - /** - * Check if the material is valid. - * @param material name of the material. - * @return true if the material is valid. - */ - - private static boolean isValidMaterial(String material) { - if (material == null) { - return false; - } else { - try { - Material.valueOf(material.toUpperCase()); - return true; - } catch (IllegalArgumentException var3) { - return false; - } - } - } - // filter SubBlockData /** @@ -236,11 +218,9 @@ private static ArrayList separateRaw(BlockData blockData) { blockDataList.add(brewingStandBottle_2); } - if (isValidMaterial("suspicious_sand")) { - if (blockData instanceof Brushable) { - SubBlockData brushable = new BrushableData(blockData); - blockDataList.add(brushable); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.Brushable")) { + blockDataList.add(rangedProperty(blockData, "BrushableData", Lang.DataKeyName.BrushableDataName, + "getDusted", "setDusted", null, "getMaximumDusted")); } if (blockData instanceof BubbleColumn) { SubBlockData BubbleColumnData = new BubbleColumnData(blockData); @@ -324,33 +304,29 @@ private static ArrayList separateRaw(BlockData blockData) { } */ - if (isValidMaterial("crafter")) { - if (blockData instanceof Crafter) { - SubBlockData crafterOrientation = new CrafterOrientationData(blockData); - blockDataList.add(crafterOrientation); - SubBlockData crafterCrafting = new CrafterCraftingData(blockData); - blockDataList.add(crafterCrafting); - SubBlockData crafterTriggered = new CrafterTriggerData(blockData); - blockDataList.add(crafterTriggered); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.Crafter")) { + blockDataList.add(property(blockData, "CrafterOrientationData", Lang.DataKeyName.CrafterOrientationDataName, + "getOrientation", "setOrientation")); + blockDataList.add(property(blockData, "CrafterCraftingData", Lang.DataKeyName.CrafterCraftingDataName, + "isCrafting", "setCrafting")); + blockDataList.add(property(blockData, "CrafterTriggerData", Lang.DataKeyName.CrafterTriggerDataName, + "isTriggered", "setTriggered")); } - if (isValidMaterial("creaking_heart")) { - if (blockData instanceof CreakingHeart) { - SubBlockData creakingHeartNatural = new CreakingHeartNaturalData(blockData); - blockDataList.add(creakingHeartNatural); - SubBlockData creakingHeartState = new CreakingHeartStateData(blockData); - blockDataList.add(creakingHeartState); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.CreakingHeart")) { + blockDataList.add(property(blockData, "CreakingHeartNaturalData", Lang.DataKeyName.CreakingHeartNaturalDataName, + "isNatural", "setNatural")); + blockDataList.add(property(blockData, "CreakingHeartStateData", Lang.DataKeyName.CreakingHeartStateDataName, + "getCreakingHeartState", "setCreakingHeartState")); } if (blockData instanceof DaylightDetector) { SubBlockData daylightDetectorData = new DaylightDetectorData(blockData); blockDataList.add(daylightDetectorData); } - if (blockData instanceof DecoratedPot) { - SubBlockData decoratedPotCracked = new DecoratedPotCrackedData(blockData); - blockDataList.add(decoratedPotCracked); + if (blockData instanceof DecoratedPot && hasMethod(blockData, "isCracked")) { + blockDataList.add(property(blockData, "DecoratedPotCrackedData", + Lang.DataKeyName.DecoratedPotCrackedDataName, "isCracked", "setCracked")); } if (blockData instanceof Directional) { SubBlockData directionalData = new DirectionalData(blockData); @@ -429,18 +405,18 @@ private static ArrayList separateRaw(BlockData blockData) { SubBlockData hangable = new HangableData(blockData); blockDataList.add(hangable); } - if (blockData instanceof HangingMoss) { - SubBlockData hangingMossTip = new HangingMossTipData(blockData); - blockDataList.add(hangingMossTip); + if (isBlockDataType(blockData, "org.bukkit.block.data.type.HangingMoss")) { + blockDataList.add(property(blockData, "HangingMossTipData", Lang.DataKeyName.HangingMossTipDataName, + "isTip", "setTip")); } /* NOTE: Not Used if (blockData instanceof HangingSign) { } */ - if (blockData instanceof Hatchable) { - SubBlockData hatchable = new HatchableData(blockData); - blockDataList.add(hatchable); + if (isBlockDataType(blockData, "org.bukkit.block.data.Hatchable")) { + blockDataList.add(rangedProperty(blockData, "HatchableData", Lang.DataKeyName.HatchableDataName, + "getHatch", "setHatch", null, "getMaximumHatch")); } if (blockData instanceof Hopper) { @@ -449,8 +425,8 @@ private static ArrayList separateRaw(BlockData blockData) { } if (blockData instanceof Jigsaw) { - SubBlockData jigsaw = new JigsawData(blockData); - blockDataList.add(jigsaw); + blockDataList.add(property(blockData, "JigsawData", Lang.DataKeyName.JigsawDataName, + "getOrientation", "setOrientation")); } if (blockData instanceof Jukebox) { @@ -583,17 +559,17 @@ private static ArrayList separateRaw(BlockData blockData) { } } - if (blockData instanceof MossyCarpet) { - SubBlockData mossyCarpetBottom = new MossyCarpetBottomData(blockData); - blockDataList.add(mossyCarpetBottom); - SubBlockData mossyCarpetHeightEast = new MossyCarpetHeightEastData(blockData); - blockDataList.add(mossyCarpetHeightEast); - SubBlockData mossyCarpetHeightNorth = new MossyCarpetHeightNorthData(blockData); - blockDataList.add(mossyCarpetHeightNorth); - SubBlockData mossyCarpetHeightSouth = new MossyCarpetHeightSouthData(blockData); - blockDataList.add(mossyCarpetHeightSouth); - SubBlockData mossyCarpetHeightWest = new MossyCarpetHeightWestData(blockData); - blockDataList.add(mossyCarpetHeightWest); + if (isBlockDataType(blockData, "org.bukkit.block.data.type.MossyCarpet")) { + blockDataList.add(property(blockData, "MossyCarpetBottomData", Lang.DataKeyName.MossyCarpetBottomDataName, + "isBottom", "setBottom")); + blockDataList.add(keyedProperty(blockData, "MossyCarpetHeightEastData", Lang.DataKeyName.MossyCarpetHeightEastDataName, + BlockFace.EAST)); + blockDataList.add(keyedProperty(blockData, "MossyCarpetHeightNorthData", Lang.DataKeyName.MossyCarpetHeightNorthDataName, + BlockFace.NORTH)); + blockDataList.add(keyedProperty(blockData, "MossyCarpetHeightSouthData", Lang.DataKeyName.MossyCarpetHeightSouthDataName, + BlockFace.SOUTH)); + blockDataList.add(keyedProperty(blockData, "MossyCarpetHeightWestData", Lang.DataKeyName.MossyCarpetHeightWestDataName, + BlockFace.WEST)); } if (blockData instanceof NoteBlock) { @@ -617,9 +593,9 @@ private static ArrayList separateRaw(BlockData blockData) { blockDataList.add(orientable); } - if (blockData instanceof FlowerBed) { - SubBlockData flowerBedPetals = new FlowerBedData(blockData); - blockDataList.add(flowerBedPetals); + if (isBlockDataType(blockData, "org.bukkit.block.data.type.FlowerBed")) { + blockDataList.add(rangedProperty(blockData, "FlowerBedData", Lang.DataKeyName.PinkPetalsDataname, + "getFlowerAmount", "setFlowerAmount", "getMinimumFlowerAmount", "getMaximumFlowerAmount")); } if (blockData instanceof Piston) { @@ -631,9 +607,9 @@ private static ArrayList separateRaw(BlockData blockData) { SubBlockData pistonHeadShort = new PistonHeadData(blockData); blockDataList.add(pistonHeadShort); } - if (blockData instanceof PotentSulfur) { - SubBlockData potentSulfurState = new PotentSulfurStateData(blockData); - blockDataList.add(potentSulfurState); + if (isBlockDataType(blockData, "org.bukkit.block.data.type.PotentSulfur")) { + blockDataList.add(property(blockData, "PotentSulfurStateData", Lang.DataKeyName.PotentSulfurStateDataName, + "getPotentSulfurState", "setPotentSulfurState")); } /* NOTE: Not Used if (blockData instanceof PitcherCrop) { @@ -645,11 +621,12 @@ private static ArrayList separateRaw(BlockData blockData) { blockDataList.add(pointedDripstoneThickness); SubBlockData pointedDripstoneVerticalDirection = new PointedDripstoneVerticalDirectionData(blockData); blockDataList.add(pointedDripstoneVerticalDirection); - } else if (blockData instanceof Speleothem) { - SubBlockData speleothemThickness = new SpeleothemThicknessData(blockData); - blockDataList.add(speleothemThickness); - SubBlockData speleothemVerticalDirection = new SpeleothemVerticalDirectionData(blockData); - blockDataList.add(speleothemVerticalDirection); + } else if (isBlockDataType(blockData, "org.bukkit.block.data.type.Speleothem")) { + blockDataList.add(property(blockData, "SpeleothemThicknessData", Lang.DataKeyName.SpeleothemThicknessDataName, + "getThickness", "setThickness")); + blockDataList.add(constrainedProperty(blockData, "SpeleothemVerticalDirectionData", + Lang.DataKeyName.SpeleothemVerticalDirectionDataName, "getVerticalDirection", "setVerticalDirection", + "getVerticalDirections")); } if (blockData instanceof Powerable) { @@ -790,11 +767,9 @@ private static ArrayList separateRaw(BlockData blockData) { } */ - if (isValidMaterial("trial_spawner")) { - if (blockData instanceof TrialSpawner) { - SubBlockData trialSpawnerDelay = new TrialSpawnerData(blockData); - blockDataList.add(trialSpawnerDelay); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.TrialSpawner")) { + blockDataList.add(property(blockData, "TrialSpawnerData", Lang.DataKeyName.TrialSpawnerDataName, + "getTrialSpawnerState", "setTrialSpawnerState")); } if (blockData instanceof Tripwire) { SubBlockData tripwire = new TripwireData(blockData); @@ -810,13 +785,11 @@ private static ArrayList separateRaw(BlockData blockData) { blockDataList.add(turtleEggCount); } - if(isValidMaterial("vault")) { - if (blockData instanceof Vault) { - SubBlockData vaultOminous = new VaultOminousData(blockData); - blockDataList.add(vaultOminous); - SubBlockData vaultState = new VaultStateData(blockData); - blockDataList.add(vaultState); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.Vault")) { + blockDataList.add(property(blockData, "VaultOminousData", Lang.DataKeyName.VaultOminousDataName, + "isOminous", "setOminous")); + blockDataList.add(property(blockData, "VaultStateData", Lang.DataKeyName.VaultStateDataName, + "getVaultState", "setVaultState")); } if (blockData instanceof Wall) { @@ -847,34 +820,27 @@ private static ArrayList separateRaw(BlockData blockData) { } // 1.21.9 - if (isValidMaterial("copper_chain")) { - if (blockData instanceof CopperGolemStatue) { - SubBlockData copperGolemStatuePose = new CopperGolemStatuePoseData(blockData); - blockDataList.add(copperGolemStatuePose); - } - if (blockData instanceof SideChaining) { - SubBlockData sideChaining = new SideChainingData(blockData); - blockDataList.add(sideChaining); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.CopperGolemStatue")) { + blockDataList.add(property(blockData, "CopperGolemStatuePoseData", + Lang.DataKeyName.CopperGolemStatuePoseDataName, "getCopperGolemPose", "setCopperGolemPose")); } - - if (isValidMaterial("leaf_litter")) { - if (blockData instanceof Segmentable) { - SubBlockData segmentable = new SegmentableData(blockData); - blockDataList.add(segmentable); - } - - if (blockData instanceof TestBlock) { - SubBlockData testBlock = new TestBlockData(blockData); - blockDataList.add(testBlock); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.SideChaining")) { + blockDataList.add(property(blockData, "SideChainingData", Lang.DataKeyName.SideChainingDataName, + "getSideChain", "setSideChain")); } - if (isValidMaterial("dried_ghast")) { - if (blockData instanceof DriedGhast) { - SubBlockData driedGhastState = new DriedGhastHydrationData(blockData); - blockDataList.add(driedGhastState); - } + if (isBlockDataType(blockData, "org.bukkit.block.data.Segmentable")) { + blockDataList.add(rangedProperty(blockData, "SegmentableData", Lang.DataKeyName.SegmentableDataName, + "getSegmentAmount", "setSegmentAmount", "getMinimumSegmentAmount", "getMaximumSegmentAmount")); + } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.TestBlock")) { + blockDataList.add(property(blockData, "TestBlockData", Lang.DataKeyName.TestBlockDataName, + "getMode", "setMode")); + } + if (isBlockDataType(blockData, "org.bukkit.block.data.type.DriedGhast")) { + blockDataList.add(rangedProperty(blockData, "DriedGhastHydrationData", + Lang.DataKeyName.DriedGhastHydrationDataName, "getHydration", "setHydration", null, + "getMaximumHydration")); } cache.put(blockData.getMaterial(), blockDataList); @@ -893,4 +859,40 @@ static boolean isBisectedHalfSafeToModify(BlockData blockData) { return blockData instanceof Bisected && (blockData instanceof Stairs || blockData instanceof TrapDoor); } + + private static boolean isBlockDataType(BlockData blockData, String className) { + return ReflectiveBlockDataProperty.isType(blockData, className); + } + + private static SubBlockData property(BlockData blockData, String name, String displayName, + String getter, String setter) { + return ReflectiveBlockDataProperty.property(blockData, name, displayName, getter, setter); + } + + private static SubBlockData rangedProperty(BlockData blockData, String name, String displayName, + String getter, String setter, String minimumGetter, + String maximumGetter) { + return ReflectiveBlockDataProperty.rangedProperty( + blockData, name, displayName, getter, setter, minimumGetter, maximumGetter); + } + + private static SubBlockData constrainedProperty(BlockData blockData, String name, String displayName, + String getter, String setter, String valuesGetter) { + return ReflectiveBlockDataProperty.constrainedProperty( + blockData, name, displayName, getter, setter, valuesGetter); + } + + private static SubBlockData keyedProperty(BlockData blockData, String name, String displayName, BlockFace face) { + return ReflectiveBlockDataProperty.keyedProperty( + blockData, name, displayName, "getHeight", "setHeight", face); + } + + private static boolean hasMethod(BlockData blockData, String methodName) { + for (java.lang.reflect.Method method : blockData.getClass().getMethods()) { + if (method.getName().equals(methodName)) { + return true; + } + } + return false; + } } diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataProperty.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataProperty.java new file mode 100644 index 0000000..816680e --- /dev/null +++ b/src/main/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataProperty.java @@ -0,0 +1,256 @@ +package dev.twme.debugstickpro.blockdatautil; + +import org.bukkit.block.data.BlockData; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Adapts block-data properties added after the 1.19.4 API baseline. + */ +public final class ReflectiveBlockDataProperty extends SubBlockData { + private final String filterName; + private final String displayName; + private final String getterName; + private final String setterName; + private final String minimumGetterName; + private final String maximumGetterName; + private final String valuesGetterName; + private final Object[] getterArguments; + private final Object[] setterPrefixArguments; + private Object value; + private List values; + + private ReflectiveBlockDataProperty( + BlockData blockData, + String filterName, + String displayName, + String getterName, + String setterName, + String minimumGetterName, + String maximumGetterName, + String valuesGetterName, + Object[] getterArguments, + Object[] setterPrefixArguments + ) { + this.blockData = Objects.requireNonNull(blockData, "blockData"); + this.filterName = Objects.requireNonNull(filterName, "filterName"); + this.displayName = Objects.requireNonNull(displayName, "displayName"); + this.getterName = Objects.requireNonNull(getterName, "getterName"); + this.setterName = Objects.requireNonNull(setterName, "setterName"); + this.minimumGetterName = minimumGetterName; + this.maximumGetterName = maximumGetterName; + this.valuesGetterName = valuesGetterName; + this.getterArguments = getterArguments.clone(); + this.setterPrefixArguments = setterPrefixArguments.clone(); + readValueAndChoices(); + } + + public static boolean isType(BlockData blockData, String className) { + try { + ClassLoader classLoader = blockData.getClass().getClassLoader(); + return Class.forName(className, false, classLoader).isInstance(blockData); + } catch (ClassNotFoundException ignored) { + return false; + } + } + + public static ReflectiveBlockDataProperty property( + BlockData blockData, + String filterName, + String displayName, + String getterName, + String setterName + ) { + return new ReflectiveBlockDataProperty( + blockData, filterName, displayName, getterName, setterName, + null, null, null, new Object[0], new Object[0] + ); + } + + public static ReflectiveBlockDataProperty rangedProperty( + BlockData blockData, + String filterName, + String displayName, + String getterName, + String setterName, + String minimumGetterName, + String maximumGetterName + ) { + return new ReflectiveBlockDataProperty( + blockData, filterName, displayName, getterName, setterName, + minimumGetterName, maximumGetterName, null, new Object[0], new Object[0] + ); + } + + public static ReflectiveBlockDataProperty constrainedProperty( + BlockData blockData, + String filterName, + String displayName, + String getterName, + String setterName, + String valuesGetterName + ) { + return new ReflectiveBlockDataProperty( + blockData, filterName, displayName, getterName, setterName, + null, null, valuesGetterName, new Object[0], new Object[0] + ); + } + + public static ReflectiveBlockDataProperty keyedProperty( + BlockData blockData, + String filterName, + String displayName, + String getterName, + String setterName, + Object key + ) { + return new ReflectiveBlockDataProperty( + blockData, filterName, displayName, getterName, setterName, + null, null, null, new Object[]{key}, new Object[]{key} + ); + } + + @Override + public String name() { + return filterName; + } + + @Override + public String dataName() { + return displayName; + } + + @Override + public String getDataAsString() { + return value instanceof Enum enumValue ? enumValue.name() : String.valueOf(value); + } + + @Override + public SubBlockData nextData() { + return move(1); + } + + @Override + public SubBlockData previousData() { + return move(-1); + } + + @Override + public BlockData copyTo(BlockData target) { + setValue(target, value); + return target; + } + + @Override + public SubBlockData fromBlockData(BlockData blockData) { + return new ReflectiveBlockDataProperty( + blockData, filterName, displayName, getterName, setterName, + minimumGetterName, maximumGetterName, valuesGetterName, + getterArguments, setterPrefixArguments + ); + } + + private SubBlockData move(int offset) { + int currentIndex = values.indexOf(value); + if (currentIndex < 0) { + throw new IllegalStateException("Current value is not allowed for " + filterName + ": " + value); + } + value = values.get(Math.floorMod(currentIndex + offset, values.size())); + setValue(blockData, value); + return this; + } + + private void readValueAndChoices() { + value = invoke(blockData, getterName, (Object[]) getterArguments); + if (valuesGetterName != null) { + Object choices = invoke(blockData, valuesGetterName); + if (!(choices instanceof Collection collection) || collection.isEmpty()) { + throw new IllegalStateException(valuesGetterName + " did not return any values"); + } + values = new ArrayList<>(collection); + return; + } + if (maximumGetterName != null) { + int minimum = minimumGetterName == null + ? 0 + : ((Number) invoke(blockData, minimumGetterName)).intValue(); + int maximum = ((Number) invoke(blockData, maximumGetterName)).intValue(); + values = new ArrayList<>(); + for (int candidate = minimum; candidate <= maximum; candidate++) { + values.add(candidate); + } + return; + } + if (value instanceof Boolean) { + values = List.of(false, true); + return; + } + if (value instanceof Enum enumValue) { + values = new ArrayList<>(Arrays.asList((Object[]) enumValue.getDeclaringClass().getEnumConstants())); + return; + } + throw new IllegalArgumentException("Unsupported reflective property type: " + value.getClass().getName()); + } + + private void setValue(BlockData target, Object newValue) { + Object[] arguments = Arrays.copyOf(setterPrefixArguments, setterPrefixArguments.length + 1); + arguments[arguments.length - 1] = newValue; + invoke(target, setterName, arguments); + } + + private static Object invoke(Object target, String methodName, Object... arguments) { + Method method = findMethod(target.getClass(), methodName, arguments); + try { + return method.invoke(target, arguments); + } catch (IllegalAccessException exception) { + throw new IllegalStateException("Cannot access " + method, exception); + } catch (InvocationTargetException exception) { + Throwable cause = exception.getCause(); + if (cause instanceof RuntimeException runtimeException) { + throw runtimeException; + } + throw new IllegalStateException("Failed to invoke " + method, cause); + } + } + + private static Method findMethod(Class type, String methodName, Object[] arguments) { + for (Method method : type.getMethods()) { + Class[] parameterTypes = method.getParameterTypes(); + if (!method.getName().equals(methodName) || parameterTypes.length != arguments.length) { + continue; + } + boolean compatible = true; + for (int index = 0; index < parameterTypes.length; index++) { + if (!wrap(parameterTypes[index]).isInstance(arguments[index])) { + compatible = false; + break; + } + } + if (compatible) { + return method; + } + } + throw new IllegalStateException("No compatible method " + methodName + " on " + type.getName()); + } + + private static Class wrap(Class type) { + if (!type.isPrimitive()) { + return type; + } + if (type == boolean.class) return Boolean.class; + if (type == byte.class) return Byte.class; + if (type == short.class) return Short.class; + if (type == int.class) return Integer.class; + if (type == long.class) return Long.class; + if (type == float.class) return Float.class; + if (type == double.class) return Double.class; + if (type == char.class) return Character.class; + return type; + } +} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/BrushableData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/BrushableData.java deleted file mode 100644 index 498489d..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/BrushableData.java +++ /dev/null @@ -1,59 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Brushable; - -public class BrushableData extends SubBlockData { - private int dusted; - - public BrushableData(BlockData blockData) { - this.blockData = blockData; - this.dusted = ((Brushable) blockData).getDusted(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.BrushableDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(dusted); - } - - public SubBlockData nextData() { - Brushable brushable = ((Brushable) blockData); - if (brushable.getDusted() >= brushable.getMaximumDusted()) { - brushable.setDusted(0); - } else { - brushable.setDusted(brushable.getDusted() + 1); - } - this.dusted = brushable.getDusted(); - return this; - } - - @Override - public SubBlockData previousData() { - Brushable brushable = ((Brushable) blockData); - if (brushable.getDusted() <= 0) { - brushable.setDusted(brushable.getMaximumDusted()); - } else { - brushable.setDusted(brushable.getDusted() - 1); - } - this.dusted = brushable.getDusted(); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Brushable) blockData).setDusted(dusted); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new BrushableData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CopperGolemStatuePoseData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CopperGolemStatuePoseData.java deleted file mode 100644 index bf4506e..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CopperGolemStatuePoseData.java +++ /dev/null @@ -1,66 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import java.util.Arrays; -import java.util.List; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.CopperGolemStatue; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; - -public class CopperGolemStatuePoseData extends SubBlockData { - private CopperGolemStatue.Pose pose; - - public CopperGolemStatuePoseData(BlockData blockData) { - this.blockData = blockData; - this.pose = ((CopperGolemStatue) blockData).getCopperGolemPose(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.CopperGolemStatuePoseDataName; - } - - @Override - public String getDataAsString() { - return pose.name(); - } - - @Override - public SubBlockData nextData() { - CopperGolemStatue copperGolemStatue = ((CopperGolemStatue) blockData); - List poses = Arrays.stream(CopperGolemStatue.Pose.values()).toList(); - if (poses.indexOf(pose) == poses.size() - 1) { - pose = poses.getFirst(); - } else { - pose = poses.get(poses.indexOf(pose) + 1); - } - copperGolemStatue.setCopperGolemPose(pose); - return this; - } - - @Override - public SubBlockData previousData() { - CopperGolemStatue copperGolemStatue = ((CopperGolemStatue) blockData); - List poses = Arrays.stream(CopperGolemStatue.Pose.values()).toList(); - if (poses.indexOf(pose) == 0) { - pose = poses.getLast(); - } else { - pose = poses.get(poses.indexOf(pose) - 1); - } - copperGolemStatue.setCopperGolemPose(pose); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((CopperGolemStatue) blockData).setCopperGolemPose(pose); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new CopperGolemStatuePoseData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterCraftingData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterCraftingData.java deleted file mode 100644 index 9fcce1d..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterCraftingData.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Crafter; - -public class CrafterCraftingData extends SubBlockData { - private boolean crafting; - - public CrafterCraftingData(BlockData blockData) { - this.blockData = blockData; - this.crafting = ((Crafter) blockData).isCrafting(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.CrafterCraftingDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(crafting); - } - - public SubBlockData nextData() { - Crafter crafter = ((Crafter) blockData); - crafter.setCrafting(!crafter.isCrafting()); - this.crafting = crafter.isCrafting(); - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Crafter) blockData).setCrafting(crafting); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new CrafterCraftingData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterOrientationData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterOrientationData.java deleted file mode 100644 index 2484768..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterOrientationData.java +++ /dev/null @@ -1,68 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.Orientation; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Crafter; -import org.bukkit.entity.LightningStrike; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class CrafterOrientationData extends SubBlockData { - private @NotNull Orientation orientation; - private final static List orientations = List.of( - Orientation.DOWN_EAST - , Orientation.DOWN_NORTH - , Orientation.DOWN_SOUTH - , Orientation.DOWN_WEST - , Orientation.EAST_UP - , Orientation.NORTH_UP - , Orientation.SOUTH_UP - , Orientation.UP_EAST - , Orientation.UP_NORTH - , Orientation.UP_SOUTH - , Orientation.UP_WEST - , Orientation.WEST_UP); - - public CrafterOrientationData(BlockData blockData) { - this.blockData = blockData; - this.orientation = ((Crafter) blockData).getOrientation(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.CrafterOrientationDataName; - } - - @Override - public String getDataAsString() { - return orientation.name(); - } - - @Override - public SubBlockData nextData() { - @NotNull Orientation orientation = ((Crafter) blockData).getOrientation(); - this.orientation = orientations.get((orientations.indexOf(orientation) + 1) % orientations.size()); - return this; - } - - @Override - public SubBlockData previousData() { - Orientation orientation = ((Crafter) blockData).getOrientation(); - this.orientation = orientations.get((orientations.indexOf(orientation) - 1 + orientations.size()) % orientations.size()); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Crafter) blockData).setOrientation(orientation); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new CrafterOrientationData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterTriggerData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterTriggerData.java deleted file mode 100644 index 0aacf77..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CrafterTriggerData.java +++ /dev/null @@ -1,49 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Crafter; - -public class CrafterTriggerData extends SubBlockData { - private boolean triggered; - - public CrafterTriggerData(BlockData blockData) { - this.blockData = blockData; - this.triggered = ((Crafter) blockData).isTriggered(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.CrafterTriggerDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(triggered); - } - - @Override - public SubBlockData nextData() { - Crafter crafter = ((Crafter) blockData); - crafter.setTriggered(!crafter.isTriggered()); - this.triggered = crafter.isTriggered(); - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Crafter) blockData).setTriggered(triggered); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new CrafterTriggerData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartNaturalData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartNaturalData.java deleted file mode 100644 index 5156b71..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartNaturalData.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.CreakingHeart; - -public class CreakingHeartNaturalData extends SubBlockData { - private boolean isNatural; - - public CreakingHeartNaturalData(BlockData blockData) { - this.blockData = blockData; - this.isNatural = ((CreakingHeart) blockData).isNatural(); - - } - - @Override - public String dataName() { - return Lang.DataKeyName.CreakingHeartNaturalDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(isNatural); - } - - @Override - public SubBlockData nextData() { - this.isNatural = !this.isNatural; - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((CreakingHeart) blockData).setNatural(this.isNatural); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new CreakingHeartNaturalData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartStateData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartStateData.java deleted file mode 100644 index bc6428d..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/CreakingHeartStateData.java +++ /dev/null @@ -1,52 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import com.github.retrooper.packetevents.protocol.world.states.enums.CreakingHeartState; -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.CreakingHeart; - -public class CreakingHeartStateData extends SubBlockData { - private CreakingHeart.State state; - private final static CreakingHeart.State[] states = CreakingHeart.State.values(); - - public CreakingHeartStateData(BlockData blockData) { - this.blockData = blockData; - this.state = ((CreakingHeart) blockData).getCreakingHeartState(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.CreakingHeartStateDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(state); - } - - @Override - public SubBlockData nextData() { - this.state = states[(state.ordinal() + 1) % states.length]; - ((CreakingHeart) blockData).setCreakingHeartState(state); - return this; - } - - @Override - public SubBlockData previousData() { - this.state = states[(state.ordinal() - 1 + states.length) % states.length]; - ((CreakingHeart) blockData).setCreakingHeartState(state); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((CreakingHeart) blockData).setCreakingHeartState(state); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new CreakingHeartStateData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DecoratedPotCrackedData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DecoratedPotCrackedData.java deleted file mode 100644 index 7896a14..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DecoratedPotCrackedData.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.DecoratedPot; - -public class DecoratedPotCrackedData extends SubBlockData { - private boolean cracked; - - public DecoratedPotCrackedData(BlockData blockData) { - this.blockData = blockData; - this.cracked = ((DecoratedPot) blockData).isCracked(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.DecoratedPotCrackedDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(cracked); - } - - @Override - public SubBlockData nextData() { - cracked = !cracked; - ((DecoratedPot) blockData).setCracked(cracked); - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((DecoratedPot) blockData).setCracked(cracked); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new DecoratedPotCrackedData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DirectionalData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DirectionalData.java index 8866de0..54387cb 100644 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DirectionalData.java +++ b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DirectionalData.java @@ -31,7 +31,7 @@ public SubBlockData nextData() { List blockFaces = ((Directional) blockData).getFaces().stream().toList(); int index = blockFaces.indexOf(direction); if (index >= blockFaces.size() - 1) { - ((Directional) blockData).setFacing(blockFaces.getFirst()); + ((Directional) blockData).setFacing(blockFaces.get(0)); } else { ((Directional) blockData).setFacing(blockFaces.get(index + 1)); } @@ -44,7 +44,7 @@ public SubBlockData previousData() { List blockFaces = ((Directional) blockData).getFaces().stream().toList(); int index = blockFaces.indexOf(direction); if (index <= 0) { - ((Directional) blockData).setFacing(blockFaces.getLast()); + ((Directional) blockData).setFacing(blockFaces.get(blockFaces.size() - 1)); } else { ((Directional) blockData).setFacing(blockFaces.get(index - 1)); } diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DriedGhastHydrationData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DriedGhastHydrationData.java deleted file mode 100644 index 007cc3f..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/DriedGhastHydrationData.java +++ /dev/null @@ -1,61 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.DriedGhast; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; - -public class DriedGhastHydrationData extends SubBlockData { - private int hydration; - - public DriedGhastHydrationData(BlockData blockData) { - this.blockData = blockData; - this.hydration = ((DriedGhast) blockData).getHydration(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.DriedGhastHydrationDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(hydration); - } - - @Override - public SubBlockData nextData() { - DriedGhast driedGhast = ((DriedGhast) blockData); - if (hydration >= driedGhast.getMaximumHydration()) { - hydration = 0; - } else { - hydration++; - } - driedGhast.setHydration(hydration); - return this; - } - - @Override - public SubBlockData previousData() { - DriedGhast driedGhast = ((DriedGhast) blockData); - if (hydration <= 0) { - hydration = driedGhast.getMaximumHydration(); - } else { - hydration--; - } - driedGhast.setHydration(hydration); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((DriedGhast) blockData).setHydration(hydration); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new DriedGhastHydrationData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/FlowerBedData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/FlowerBedData.java deleted file mode 100644 index e719fb8..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/FlowerBedData.java +++ /dev/null @@ -1,61 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.FlowerBed; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; - -public class FlowerBedData extends SubBlockData { - private int flowerAmount; - - public FlowerBedData(BlockData blockData) { - this.blockData = blockData; - this.flowerAmount = ((FlowerBed) blockData).getFlowerAmount(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.PinkPetalsDataname; - } - - @Override - public String getDataAsString() { - return String.valueOf(flowerAmount); - } - - @Override - public SubBlockData nextData() { - FlowerBed flowerBeds = ((FlowerBed) blockData); - if (flowerAmount >= flowerBeds.getMaximumFlowerAmount()) { - flowerAmount = flowerBeds.getMinimumFlowerAmount(); - } else { - flowerAmount++; - } - flowerBeds.setFlowerAmount(flowerAmount); - return this; - } - - @Override - public SubBlockData previousData() { - FlowerBed flowerBeds = ((FlowerBed) blockData); - if (flowerAmount <= flowerBeds.getMinimumFlowerAmount()) { - flowerAmount = flowerBeds.getMaximumFlowerAmount(); - } else { - flowerAmount--; - } - flowerBeds.setFlowerAmount(flowerAmount); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((FlowerBed) blockData).setFlowerAmount(flowerAmount); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new FlowerBedData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HangingMossTipData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HangingMossTipData.java deleted file mode 100644 index 78e30e4..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HangingMossTipData.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.HangingMoss; - -public class HangingMossTipData extends SubBlockData { - private boolean tip; - - public HangingMossTipData(BlockData blockData) { - this.blockData = blockData; - this.tip = ((HangingMoss) blockData).isTip(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.HangingMossTipDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(tip); - } - - @Override - public SubBlockData nextData() { - tip = !tip; - ((HangingMoss) blockData).setTip(tip); - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((HangingMoss) blockData).setTip(tip); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new HangingMossTipData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HatchableData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HatchableData.java deleted file mode 100644 index ac8c327..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/HatchableData.java +++ /dev/null @@ -1,60 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Hatchable; - -public class HatchableData extends SubBlockData { - private int hatch; - - public HatchableData(BlockData blockData) { - this.blockData = blockData; - this.hatch = ((Hatchable) blockData).getHatch(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.HatchableDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(hatch); - } - - @Override - public SubBlockData nextData() { - Hatchable hatchable = ((Hatchable) blockData); - if (hatchable.getHatch() >= hatchable.getMaximumHatch()) { - hatchable.setHatch(0); - } else { - hatchable.setHatch(hatchable.getHatch() + 1); - } - this.hatch = hatchable.getHatch(); - return this; - } - - @Override - public SubBlockData previousData() { - Hatchable hatchable = ((Hatchable) blockData); - if (hatchable.getHatch() <= 0) { - hatchable.setHatch(hatchable.getMaximumHatch()); - } else { - hatchable.setHatch(hatchable.getHatch() - 1); - } - this.hatch = hatchable.getHatch(); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Hatchable) blockData).setHatch(hatch); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new HatchableData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/JigsawData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/JigsawData.java deleted file mode 100644 index b0c03d8..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/JigsawData.java +++ /dev/null @@ -1,66 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.Orientation; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Jigsaw; - -import java.util.List; - -public class JigsawData extends SubBlockData { - private Orientation orientation; - private final static List orientations = List.of( - Orientation.DOWN_EAST - , Orientation.DOWN_NORTH - , Orientation.DOWN_SOUTH - , Orientation.DOWN_WEST - , Orientation.EAST_UP - , Orientation.NORTH_UP - , Orientation.SOUTH_UP - , Orientation.UP_EAST - , Orientation.UP_NORTH - , Orientation.UP_SOUTH - , Orientation.UP_WEST - , Orientation.WEST_UP); - - public JigsawData(BlockData blockData) { - this.blockData = blockData; - this.orientation = ((Jigsaw) blockData).getOrientation(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.JigsawDataName; - } - - @Override - public String getDataAsString() { - return orientation.name(); - } - - @Override - public SubBlockData nextData() { - orientation = orientations.get((orientations.indexOf(orientation) + 1) % orientations.size()); - ((Jigsaw) blockData).setOrientation(orientation); - return this; - } - - @Override - public SubBlockData previousData() { - orientation = orientations.get((orientations.indexOf(orientation) - 1 + orientations.size()) % orientations.size()); - ((Jigsaw) blockData).setOrientation(orientation); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Jigsaw) blockData).setOrientation(orientation); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new JigsawData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/MossyCarpetBottomData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/MossyCarpetBottomData.java deleted file mode 100644 index 77410e9..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/MossyCarpetBottomData.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.MossyCarpet; - -public class MossyCarpetBottomData extends SubBlockData { - private boolean bottom; - - public MossyCarpetBottomData(BlockData blockData) { - this.blockData = blockData; - this.bottom = ((MossyCarpet) blockData).isBottom(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.MossyCarpetBottomDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(bottom); - } - - @Override - public SubBlockData nextData() { - bottom = !bottom; - ((MossyCarpet) blockData).setBottom(bottom); - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((MossyCarpet) blockData).setBottom(bottom); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new MossyCarpetBottomData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/NoteBlockInstrumentData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/NoteBlockInstrumentData.java index 7125823..9424dd9 100644 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/NoteBlockInstrumentData.java +++ b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/NoteBlockInstrumentData.java @@ -32,7 +32,7 @@ public SubBlockData nextData() { NoteBlock noteBlock = ((NoteBlock) blockData); List instruments = Arrays.stream(Instrument.values()).toList(); if (instruments.indexOf(instrument) == instruments.size() - 1) { - instrument = instruments.getFirst(); + instrument = instruments.get(0); } else { instrument = instruments.get(instruments.indexOf(instrument) + 1); } @@ -45,7 +45,7 @@ public SubBlockData previousData() { NoteBlock noteBlock = ((NoteBlock) blockData); List instruments = Arrays.stream(Instrument.values()).toList(); if (instruments.indexOf(instrument) == 0) { - instrument = instruments.getLast(); + instrument = instruments.get(instruments.size() - 1); } else { instrument = instruments.get(instruments.indexOf(instrument) - 1); } diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/OrientableData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/OrientableData.java index ef9a52b..f7087e6 100644 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/OrientableData.java +++ b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/OrientableData.java @@ -31,13 +31,13 @@ public SubBlockData nextData() { Orientable orientable = (Orientable) blockData; List axisList = orientable.getAxes().stream().toList(); if (axisList.size() == 1) { - orientable.setAxis(axisList.getFirst()); - this.axis = axisList.getFirst(); + orientable.setAxis(axisList.get(0)); + this.axis = axisList.get(0); } else { int index = axisList.indexOf(axis); if (index == axisList.size() - 1) { - orientable.setAxis(axisList.getFirst()); - this.axis = axisList.getFirst(); + orientable.setAxis(axisList.get(0)); + this.axis = axisList.get(0); } else { orientable.setAxis(axisList.get(index + 1)); this.axis = axisList.get(index + 1); @@ -52,13 +52,13 @@ public SubBlockData previousData() { Orientable orientable = (Orientable) blockData; List axisList = orientable.getAxes().stream().toList(); if (axisList.size() == 1) { - orientable.setAxis(axisList.getFirst()); - this.axis = axisList.getFirst(); + orientable.setAxis(axisList.get(0)); + this.axis = axisList.get(0); } else { int index = axisList.indexOf(axis); if (index == 0) { - orientable.setAxis(axisList.getLast()); - this.axis = axisList.getLast(); + orientable.setAxis(axisList.get(axisList.size() - 1)); + this.axis = axisList.get(axisList.size() - 1); } else { orientable.setAxis(axisList.get(index - 1)); this.axis = axisList.get(index - 1); diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/PotentSulfurStateData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/PotentSulfurStateData.java deleted file mode 100644 index b64f085..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/PotentSulfurStateData.java +++ /dev/null @@ -1,51 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.PotentSulfur; - -public class PotentSulfurStateData extends SubBlockData { - private static final PotentSulfur.State[] states = PotentSulfur.State.values(); - private PotentSulfur.State state; - - public PotentSulfurStateData(BlockData blockData) { - this.blockData = blockData; - this.state = ((PotentSulfur) blockData).getPotentSulfurState(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.PotentSulfurStateDataName; - } - - @Override - public String getDataAsString() { - return state.name(); - } - - @Override - public SubBlockData nextData() { - state = states[(state.ordinal() + 1) % states.length]; - ((PotentSulfur) blockData).setPotentSulfurState(state); - return this; - } - - @Override - public SubBlockData previousData() { - state = states[(state.ordinal() - 1 + states.length) % states.length]; - ((PotentSulfur) blockData).setPotentSulfurState(state); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((PotentSulfur) blockData).setPotentSulfurState(state); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new PotentSulfurStateData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SegmentableData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SegmentableData.java deleted file mode 100644 index cf4a6e7..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SegmentableData.java +++ /dev/null @@ -1,61 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Segmentable; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; - -public class SegmentableData extends SubBlockData { - private int segmentAmount; - - public SegmentableData(BlockData blockData) { - this.blockData = blockData; - this.segmentAmount = ((Segmentable) blockData).getSegmentAmount(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.SegmentableDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(segmentAmount); - } - - @Override - public SubBlockData nextData() { - Segmentable segmentable = ((Segmentable) blockData); - if (segmentAmount >= segmentable.getMaximumSegmentAmount()) { - segmentAmount = segmentable.getMinimumSegmentAmount(); - } else { - segmentAmount++; - } - segmentable.setSegmentAmount(segmentAmount); - return this; - } - - @Override - public SubBlockData previousData() { - Segmentable segmentable = ((Segmentable) blockData); - if (segmentAmount <= segmentable.getMinimumSegmentAmount()) { - segmentAmount = segmentable.getMaximumSegmentAmount(); - } else { - segmentAmount--; - } - segmentable.setSegmentAmount(segmentAmount); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Segmentable) blockData).setSegmentAmount(segmentAmount); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new SegmentableData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SideChainingData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SideChainingData.java deleted file mode 100644 index 1d69914..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SideChainingData.java +++ /dev/null @@ -1,66 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import java.util.Arrays; -import java.util.List; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.SideChaining; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; - -public class SideChainingData extends SubBlockData { - private SideChaining.ChainPart chainPart; - - public SideChainingData(BlockData blockData) { - this.blockData = blockData; - this.chainPart = ((SideChaining) blockData).getSideChain(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.SideChainingDataName; - } - - @Override - public String getDataAsString() { - return chainPart.name(); - } - - @Override - public SubBlockData nextData() { - List chainParts = Arrays.asList(SideChaining.ChainPart.values()); - int index = chainParts.indexOf(chainPart); - if (index >= chainParts.size() - 1) { - ((SideChaining) blockData).setSideChain(chainParts.getFirst()); - } else { - ((SideChaining) blockData).setSideChain(chainParts.get(index + 1)); - } - this.chainPart = ((SideChaining) blockData).getSideChain(); - return this; - } - - @Override - public SubBlockData previousData() { - List chainParts = Arrays.asList(SideChaining.ChainPart.values()); - int index = chainParts.indexOf(chainPart); - if (index <= 0) { - ((SideChaining) blockData).setSideChain(chainParts.getLast()); - } else { - ((SideChaining) blockData).setSideChain(chainParts.get(index - 1)); - } - this.chainPart = ((SideChaining) blockData).getSideChain(); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((SideChaining) blockData).setSideChain(chainPart); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new SideChainingData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemThicknessData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemThicknessData.java deleted file mode 100644 index 502b839..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemThicknessData.java +++ /dev/null @@ -1,51 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Speleothem; - -public class SpeleothemThicknessData extends SubBlockData { - private static final Speleothem.Thickness[] thicknesses = Speleothem.Thickness.values(); - private Speleothem.Thickness thickness; - - public SpeleothemThicknessData(BlockData blockData) { - this.blockData = blockData; - this.thickness = ((Speleothem) blockData).getThickness(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.SpeleothemThicknessDataName; - } - - @Override - public String getDataAsString() { - return thickness.name(); - } - - @Override - public SubBlockData nextData() { - thickness = thicknesses[(thickness.ordinal() + 1) % thicknesses.length]; - ((Speleothem) blockData).setThickness(thickness); - return this; - } - - @Override - public SubBlockData previousData() { - thickness = thicknesses[(thickness.ordinal() - 1 + thicknesses.length) % thicknesses.length]; - ((Speleothem) blockData).setThickness(thickness); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Speleothem) blockData).setThickness(thickness); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new SpeleothemThicknessData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemVerticalDirectionData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemVerticalDirectionData.java deleted file mode 100644 index ef20a08..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/SpeleothemVerticalDirectionData.java +++ /dev/null @@ -1,57 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Speleothem; - -import java.util.List; - -public class SpeleothemVerticalDirectionData extends SubBlockData { - private BlockFace verticalDirection; - - public SpeleothemVerticalDirectionData(BlockData blockData) { - this.blockData = blockData; - this.verticalDirection = ((Speleothem) blockData).getVerticalDirection(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.SpeleothemVerticalDirectionDataName; - } - - @Override - public String getDataAsString() { - return verticalDirection.name(); - } - - @Override - public SubBlockData nextData() { - Speleothem speleothem = (Speleothem) blockData; - List blockFaces = speleothem.getVerticalDirections().stream().toList(); - verticalDirection = blockFaces.get((blockFaces.indexOf(verticalDirection) + 1) % blockFaces.size()); - speleothem.setVerticalDirection(verticalDirection); - return this; - } - - @Override - public SubBlockData previousData() { - Speleothem speleothem = (Speleothem) blockData; - List blockFaces = speleothem.getVerticalDirections().stream().toList(); - verticalDirection = blockFaces.get((blockFaces.indexOf(verticalDirection) - 1 + blockFaces.size()) % blockFaces.size()); - speleothem.setVerticalDirection(verticalDirection); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Speleothem) blockData).setVerticalDirection(verticalDirection); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new SpeleothemVerticalDirectionData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TestBlockData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TestBlockData.java deleted file mode 100644 index 7c2c9f0..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TestBlockData.java +++ /dev/null @@ -1,63 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.TestBlock; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; - -public class TestBlockData extends SubBlockData { - private TestBlock.Mode mode; - - public TestBlockData(BlockData blockData) { - this.blockData = blockData; - this.mode = ((TestBlock) blockData).getMode(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.TestBlockDataName; - } - - @Override - public String getDataAsString() { - return mode.name(); - } - - @Override - public SubBlockData nextData() { - TestBlock testBlock = ((TestBlock) blockData); - switch (mode) { - case START -> mode = TestBlock.Mode.LOG; - case LOG -> mode = TestBlock.Mode.FAIL; - case FAIL -> mode = TestBlock.Mode.ACCEPT; - case ACCEPT -> mode = TestBlock.Mode.START; - } - testBlock.setMode(mode); - return this; - } - - @Override - public SubBlockData previousData() { - TestBlock testBlock = ((TestBlock) blockData); - switch (mode) { - case LOG -> mode = TestBlock.Mode.START; - case FAIL -> mode = TestBlock.Mode.LOG; - case ACCEPT -> mode = TestBlock.Mode.FAIL; - case START -> mode = TestBlock.Mode.ACCEPT; - } - testBlock.setMode(mode); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((TestBlock) blockData).setMode(mode); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new TestBlockData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TrialSpawnerData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TrialSpawnerData.java deleted file mode 100644 index 57fea61..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/TrialSpawnerData.java +++ /dev/null @@ -1,58 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.TrialSpawner; - -import java.util.List; - -public class TrialSpawnerData extends SubBlockData { - private TrialSpawner.State state; - private final static List states = List.of( - TrialSpawner.State.ACTIVE, - TrialSpawner.State.COOLDOWN, - TrialSpawner.State.EJECTING_REWARD, - TrialSpawner.State.INACTIVE, - TrialSpawner.State.WAITING_FOR_PLAYERS, - TrialSpawner.State.WAITING_FOR_REWARD_EJECTION); - - public TrialSpawnerData(BlockData blockData) { - this.blockData = blockData; - this.state = ((TrialSpawner) blockData).getTrialSpawnerState(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.TrialSpawnerDataName; - } - - @Override - public String getDataAsString() { - return state.name(); - } - - public SubBlockData nextData() { - state = states.get((states.indexOf(state) + 1) % states.size()); - ((TrialSpawner) blockData).setTrialSpawnerState(state); - return this; - } - - @Override - public SubBlockData previousData() { - state = states.get((states.indexOf(state) - 1 + states.size()) % states.size()); - ((TrialSpawner) blockData).setTrialSpawnerState(state); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((TrialSpawner) blockData).setTrialSpawnerState(state); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new TrialSpawnerData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultOminousData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultOminousData.java deleted file mode 100644 index 0aa86d4..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultOminousData.java +++ /dev/null @@ -1,49 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Vault; - -public class VaultOminousData extends SubBlockData { - private boolean ominous; - - public VaultOminousData(BlockData blockData) { - this.blockData = blockData; - this.ominous = ((Vault) blockData).isOminous(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.VaultOminousDataName; - } - - @Override - public String getDataAsString() { - return String.valueOf(ominous); - } - - @Override - public SubBlockData nextData() { - Vault vault = ((Vault) blockData); - vault.setOminous(!ominous); - ominous = vault.isOminous(); - return this; - } - - @Override - public SubBlockData previousData() { - return nextData(); - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Vault) blockData).setOminous(ominous); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new VaultOminousData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultStateData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultStateData.java deleted file mode 100644 index 44b943c..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/VaultStateData.java +++ /dev/null @@ -1,67 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Vault; - -public class VaultStateData extends SubBlockData { - private Vault.State state; - - public VaultStateData(BlockData blockData) { - this.blockData = blockData; - this.state = ((Vault) blockData).getVaultState(); - } - - @Override - public String dataName() { - return Lang.DataKeyName.VaultStateDataName; - } - - @Override - public String getDataAsString() { - return state.name(); - } - - @Override - public SubBlockData nextData() { - if (state == Vault.State.ACTIVE) { - state = Vault.State.EJECTING; - } else if (state == Vault.State.EJECTING) { - state = Vault.State.INACTIVE; - } else if (state == Vault.State.INACTIVE) { - state = Vault.State.UNLOCKING; - } else if (state == Vault.State.UNLOCKING) { - state = Vault.State.ACTIVE; - } - ((Vault) blockData).setVaultState(state); - return this; - } - - @Override - public SubBlockData previousData() { - if (state == Vault.State.ACTIVE) { - state = Vault.State.UNLOCKING; - } else if (state == Vault.State.UNLOCKING) { - state = Vault.State.INACTIVE; - } else if (state == Vault.State.INACTIVE) { - state = Vault.State.EJECTING; - } else if (state == Vault.State.EJECTING) { - state = Vault.State.ACTIVE; - } - ((Vault) blockData).setVaultState(state); - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((Vault) blockData).setVaultState(state); - return blockData; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new VaultStateData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightData.java deleted file mode 100644 index f31f018..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightData.java +++ /dev/null @@ -1,55 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata.mossycarpetheight; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.MossyCarpet; -import org.bukkit.block.data.type.Wall; - -import java.util.List; - -public abstract class MossyCarpetHeightData extends SubBlockData { - private static final List heights = List.of( - Wall.Height.NONE, - Wall.Height.LOW, - Wall.Height.TALL - ); - - private final BlockFace face; - private Wall.Height height; - - public MossyCarpetHeightData(BlockData blockData, BlockFace face) { - this.blockData = blockData; - this.face = face; - this.height = ((MossyCarpet) blockData).getHeight(face); - } - - @Override - public String getDataAsString() { - return height.name(); - } - - @Override - public SubBlockData nextData() { - MossyCarpet mossyCarpet = (MossyCarpet) blockData; - height = heights.get((heights.indexOf(height) + 1) % heights.size()); - mossyCarpet.setHeight(face, height); - this.blockData = mossyCarpet; - return this; - } - - @Override - public SubBlockData previousData() { - MossyCarpet mossyCarpet = (MossyCarpet) blockData; - height = heights.get((heights.indexOf(height) - 1 + heights.size()) % heights.size()); - mossyCarpet.setHeight(face, height); - this.blockData = mossyCarpet; - return this; - } - - @Override - public BlockData copyTo(BlockData blockData) { - ((MossyCarpet) blockData).setHeight(face, height); - return blockData; - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightEastData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightEastData.java deleted file mode 100644 index 7b9557b..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightEastData.java +++ /dev/null @@ -1,22 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata.mossycarpetheight; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; - -public class MossyCarpetHeightEastData extends MossyCarpetHeightData { - public MossyCarpetHeightEastData(BlockData blockData) { - super(blockData, BlockFace.EAST); - } - - @Override - public String dataName() { - return Lang.DataKeyName.MossyCarpetHeightEastDataName; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new MossyCarpetHeightEastData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightNorthData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightNorthData.java deleted file mode 100644 index e5e3381..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightNorthData.java +++ /dev/null @@ -1,22 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata.mossycarpetheight; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; - -public class MossyCarpetHeightNorthData extends MossyCarpetHeightData { - public MossyCarpetHeightNorthData(BlockData blockData) { - super(blockData, BlockFace.NORTH); - } - - @Override - public String dataName() { - return Lang.DataKeyName.MossyCarpetHeightNorthDataName; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new MossyCarpetHeightNorthData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightSouthData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightSouthData.java deleted file mode 100644 index 256127d..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightSouthData.java +++ /dev/null @@ -1,22 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata.mossycarpetheight; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; - -public class MossyCarpetHeightSouthData extends MossyCarpetHeightData { - public MossyCarpetHeightSouthData(BlockData blockData) { - super(blockData, BlockFace.SOUTH); - } - - @Override - public String dataName() { - return Lang.DataKeyName.MossyCarpetHeightSouthDataName; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new MossyCarpetHeightSouthData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightWestData.java b/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightWestData.java deleted file mode 100644 index 5f34645..0000000 --- a/src/main/java/dev/twme/debugstickpro/blockdatautil/subdata/mossycarpetheight/MossyCarpetHeightWestData.java +++ /dev/null @@ -1,22 +0,0 @@ -package dev.twme.debugstickpro.blockdatautil.subdata.mossycarpetheight; - -import dev.twme.debugstickpro.blockdatautil.SubBlockData; -import dev.twme.debugstickpro.localization.Lang; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; - -public class MossyCarpetHeightWestData extends MossyCarpetHeightData { - public MossyCarpetHeightWestData(BlockData blockData) { - super(blockData, BlockFace.WEST); - } - - @Override - public String dataName() { - return Lang.DataKeyName.MossyCarpetHeightWestDataName; - } - - @Override - public SubBlockData fromBlockData(BlockData blockData) { - return new MossyCarpetHeightWestData(blockData); - } -} diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/GiveCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/GiveCommand.java index 8041b2a..1af44dd 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/GiveCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/GiveCommand.java @@ -5,6 +5,7 @@ import dev.twme.debugstickpro.playerdata.PlayerDataManager; import dev.twme.debugstickpro.utils.CustomModelDataManager; import dev.twme.debugstickpro.utils.DebugStickItem; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; @@ -18,7 +19,7 @@ public static boolean onGiveCommand(Player player, String[] args) { UUID playerUUID = player.getUniqueId(); if (!player.hasPermission("debugstickpro.give")) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.NoPermission)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } @@ -27,7 +28,7 @@ public static boolean onGiveCommand(Player player, String[] args) { player.getInventory().addItem(DebugStickItem.getDebugStickItem()); Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Give.Success).replace("%player%", player.getName())); - player.sendMessage(parsed); + TextUtil.send(player, parsed); if (DebugStickItem.checkPlayer(player)) { PlayerDataManager.addPlayerToDisplayList(player.getUniqueId()); } @@ -43,7 +44,7 @@ public static boolean onGiveCommand(Player player, String[] args) { // Check if player not exist if (onlinePlayer == null) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Give.NoPlayer)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } @@ -51,7 +52,7 @@ public static boolean onGiveCommand(Player player, String[] args) { onlinePlayer.getInventory().addItem(DebugStickItem.getDebugStickItem()); CustomModelDataManager.updatePlayerMode(onlinePlayer); Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Give.Success).replace("%player%", onlinePlayer.getName())); - player.sendMessage(parsed); + TextUtil.send(player, parsed); if (DebugStickItem.checkPlayer(onlinePlayer)) { PlayerDataManager.addPlayerToDisplayList(onlinePlayer.getUniqueId()); diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/HelpCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/HelpCommand.java index 7b2179f..d84697e 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/HelpCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/HelpCommand.java @@ -2,6 +2,7 @@ import dev.twme.debugstickpro.localization.I18n; import dev.twme.debugstickpro.localization.Lang; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Player; @@ -17,11 +18,11 @@ public static boolean onHelpCommand(Player player, String[] args) { } if (!player.hasPermission("debugstickpro.help")) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.NoPermission)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } for (String message : I18n.list(playerUUID, Lang.CommandsMessages.Help.HelpMessage)) { - player.sendMessage(mm.deserialize(message)); + TextUtil.send(player, mm.deserialize(message)); } return true; } diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/ModeCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/ModeCommand.java index fbc9a74..7ad0c66 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/ModeCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/ModeCommand.java @@ -8,6 +8,7 @@ import dev.twme.debugstickpro.playerdata.PlayerDataManager; import dev.twme.debugstickpro.utils.CustomModelDataManager; import dev.twme.debugstickpro.utils.DebugStickItem; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; @@ -31,14 +32,14 @@ public static boolean onModeCommand(Player player, String[] args) { UUID playerUUID = player.getUniqueId(); if (!player.hasPermission("debugstickpro.mode")) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.NoPermission)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } // If no mode is specified, display the usage message if (args.length == 1) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Mode.Usage)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } @@ -50,19 +51,19 @@ public static boolean onModeCommand(Player player, String[] args) { if (newMode == null) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Mode.Usage)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } if (!hasModePermission(player, newMode)) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.NoPermission)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } if (!DebugStickItem.checkPlayer(player)) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Mode.MustHoldDebugStick)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } @@ -77,7 +78,7 @@ public static boolean onModeCommand(Player player, String[] args) { CustomModelDataManager.updateItem(player, newMode); Component parsed = mm.deserialize(I18n.string(playerUUID, successMessageKey(newMode))); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/ReloadCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/ReloadCommand.java index 92c1cd3..6da04fe 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/ReloadCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/ReloadCommand.java @@ -3,6 +3,7 @@ import dev.twme.debugstickpro.DebugStickPro; import dev.twme.debugstickpro.localization.I18n; import dev.twme.debugstickpro.localization.Lang; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Player; @@ -18,12 +19,12 @@ public static boolean onReloadCommand(Player player, String[] args) { } if (!player.hasPermission("debugstickpro.reload")) { Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.NoPermission)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } DebugStickPro.getInstance().onReload(); Component parsed = mm.deserialize(I18n.string(playerUUID, Lang.CommandsMessages.Reload.Success)); - player.sendMessage(parsed); + TextUtil.send(player, parsed); return true; } } diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleGiveCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleGiveCommand.java index 2f0ec8c..e483788 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleGiveCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleGiveCommand.java @@ -4,6 +4,7 @@ import dev.twme.debugstickpro.localization.Lang; import dev.twme.debugstickpro.playerdata.PlayerDataManager; import dev.twme.debugstickpro.utils.DebugStickItem; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; @@ -16,7 +17,7 @@ public static boolean onGiveCommand(CommandSender sender, String[] args) { if (!sender.hasPermission("debugstickpro.give")) { Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.NoPermission)); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); return true; } @@ -24,7 +25,7 @@ public static boolean onGiveCommand(CommandSender sender, String[] args) { if (args.length == 1) { if (!(sender instanceof Player)) { Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.YouAreNotPlayer)); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); return true; } return true; @@ -39,14 +40,14 @@ public static boolean onGiveCommand(CommandSender sender, String[] args) { // Check if player not exist if (onlinePlayer == null) { Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.Give.NoPlayer)); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); return true; } // Give another player a debug stick onlinePlayer.getInventory().addItem(DebugStickItem.getDebugStickItem()); Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.Give.Success).replace("%player%", onlinePlayer.getName())); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); if (DebugStickItem.checkPlayer(onlinePlayer)) { PlayerDataManager.addPlayerToDisplayList(onlinePlayer.getUniqueId()); diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleHelpCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleHelpCommand.java index 3027ea0..775cdd8 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleHelpCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleHelpCommand.java @@ -2,6 +2,7 @@ import dev.twme.debugstickpro.localization.I18n; import dev.twme.debugstickpro.localization.Lang; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.command.CommandSender; @@ -15,11 +16,11 @@ public static boolean onHelpCommand(CommandSender sender, String[] args) { } if (!sender.hasPermission("debugstickpro.help")) { Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.NoPermission)); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); return true; } for (String message : I18n.list(Lang.CommandsMessages.Help.HelpMessage)) { - sender.sendMessage(mm.deserialize(message)); + TextUtil.send(sender, mm.deserialize(message)); } return true; } diff --git a/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleReloadCommand.java b/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleReloadCommand.java index 011001a..e0f03d4 100644 --- a/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleReloadCommand.java +++ b/src/main/java/dev/twme/debugstickpro/commands/subcommands/console/ConsoleReloadCommand.java @@ -3,6 +3,7 @@ import dev.twme.debugstickpro.DebugStickPro; import dev.twme.debugstickpro.localization.I18n; import dev.twme.debugstickpro.localization.Lang; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.command.CommandSender; @@ -16,12 +17,12 @@ public static boolean onReloadCommand(CommandSender sender, String[] args) { } if (!sender.hasPermission("debugstickpro.reload")) { Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.NoPermission)); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); return true; } DebugStickPro.getInstance().onReload(); Component parsed = mm.deserialize(I18n.string(Lang.CommandsMessages.Reload.Success)); - sender.sendMessage(parsed); + TextUtil.send(sender, parsed); return true; } } diff --git a/src/main/java/dev/twme/debugstickpro/display/ActionBarDisplayTask.java b/src/main/java/dev/twme/debugstickpro/display/ActionBarDisplayTask.java index 4f10725..8570061 100644 --- a/src/main/java/dev/twme/debugstickpro/display/ActionBarDisplayTask.java +++ b/src/main/java/dev/twme/debugstickpro/display/ActionBarDisplayTask.java @@ -1,61 +1,65 @@ package dev.twme.debugstickpro.display; +import dev.twme.debugstickpro.DebugStickPro; import dev.twme.debugstickpro.mode.classic.ClassicActionBarDisplay; import dev.twme.debugstickpro.mode.copy.CopyActionBarDisplay; import dev.twme.debugstickpro.mode.freeze.FreezeActionBarDisplay; import dev.twme.debugstickpro.playerdata.PlayerData; import dev.twme.debugstickpro.playerdata.PlayerDataManager; +import dev.twme.debugstickpro.scheduler.PlatformScheduler; import dev.twme.debugstickpro.utils.DebugStickItem; import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.entity.Player; -import java.util.Iterator; import java.util.UUID; public class ActionBarDisplayTask implements Runnable { + private final DebugStickPro plugin; + + public ActionBarDisplayTask(DebugStickPro plugin) { + this.plugin = plugin; + } + @Override public void run() { - Iterator iterator = PlayerDataManager.getDisplaySet().iterator(); - while (iterator.hasNext()) { - UUID uuid = iterator.next(); + for (UUID uuid : PlayerDataManager.getDisplaySetSnapshot()) { Player player = Bukkit.getPlayer(uuid); - // player is offline or something if (player == null) { - iterator.remove(); + PlayerDataManager.removePlayerFromDisplayList(uuid); continue; } - if (!player.hasPermission("debugstickpro.use")) { - removeFromDisplayList(iterator, uuid); - continue; - } + PlatformScheduler.runForPlayer(plugin, player, () -> displayForPlayer(player)); + } + } - if (!DebugStickItem.checkPlayer(player)) { - removeFromDisplayList(iterator, uuid); - continue; - } + private void displayForPlayer(Player player) { + UUID uuid = player.getUniqueId(); + if (!player.hasPermission("debugstickpro.use")) { + PlayerDataManager.removePlayerFromDisplayList(uuid); + return; + } - PlayerData playerData = PlayerDataManager.getOrCreatePlayerData(uuid); - - switch (playerData.getDebugStickMode()) { - case CLASSIC: - Block block = player.getTargetBlockExact(5); - ActionbarUtil.sendActionBar(player, ClassicActionBarDisplay.getDisplay(uuid, block == null ? null : block.getBlockData())); - continue; - case COPY: - ActionbarUtil.sendActionBar(player, CopyActionBarDisplay.getDisplay(uuid)); - continue; - case FREEZE: - ActionbarUtil.sendActionBar(player, FreezeActionBarDisplay.getDisplay(uuid)); - continue; - } + if (!DebugStickItem.checkPlayer(player)) { + PlayerDataManager.removePlayerFromDisplayList(uuid); + return; } - } - private void removeFromDisplayList(Iterator iterator, UUID uuid) { - iterator.remove(); - ActionbarUtil.removeActionBar(uuid); + PlayerData playerData = PlayerDataManager.getOrCreatePlayerData(uuid); + + switch (playerData.getDebugStickMode()) { + case CLASSIC: + Block block = player.getTargetBlockExact(5); + ActionbarUtil.sendActionBar(player, ClassicActionBarDisplay.getDisplay(uuid, block == null ? null : block.getBlockData())); + break; + case COPY: + ActionbarUtil.sendActionBar(player, CopyActionBarDisplay.getDisplay(uuid)); + break; + case FREEZE: + ActionbarUtil.sendActionBar(player, FreezeActionBarDisplay.getDisplay(uuid)); + break; + } } } diff --git a/src/main/java/dev/twme/debugstickpro/display/ActionbarUtil.java b/src/main/java/dev/twme/debugstickpro/display/ActionbarUtil.java index 6d78f0c..8c2ca1e 100644 --- a/src/main/java/dev/twme/debugstickpro/display/ActionbarUtil.java +++ b/src/main/java/dev/twme/debugstickpro/display/ActionbarUtil.java @@ -1,5 +1,6 @@ package dev.twme.debugstickpro.display; +import dev.twme.debugstickpro.utils.TextUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; @@ -11,7 +12,7 @@ public class ActionbarUtil { private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - private static final Set lastIsRemove = new java.util.HashSet<>(); + private static final Set lastIsRemove = java.util.concurrent.ConcurrentHashMap.newKeySet(); public static void removeActionBar(UUID uuid) { Player player = Bukkit.getPlayer(uuid); @@ -19,14 +20,13 @@ public static void removeActionBar(UUID uuid) { return; } if (player != null) { - player.sendActionBar(Component.text(" ")); + TextUtil.sendActionBar(player, Component.text(" ")); lastIsRemove.add(uuid); } } public static void sendActionBar(Player player, String message) { - Component parsed = MINI_MESSAGE.deserialize(message); - player.sendActionBar(parsed); + TextUtil.sendActionBar(player, MINI_MESSAGE.deserialize(message)); lastIsRemove.remove(player.getUniqueId()); } } diff --git a/src/main/java/dev/twme/debugstickpro/listeners/ChunkLoadEventListener.java b/src/main/java/dev/twme/debugstickpro/listeners/ChunkLoadEventListener.java index 81a6309..40622bd 100644 --- a/src/main/java/dev/twme/debugstickpro/listeners/ChunkLoadEventListener.java +++ b/src/main/java/dev/twme/debugstickpro/listeners/ChunkLoadEventListener.java @@ -12,7 +12,7 @@ public class ChunkLoadEventListener implements Listener { public void onChunkLoad(ChunkLoadEvent event) { Entity[] entities = event.getChunk().getEntities(); for (Entity entity : entities) { - if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY)) { + if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY, org.bukkit.persistence.PersistentDataType.STRING)) { FreezeBlockManager.removeOnChunkLoadOrUnload(entity); } } diff --git a/src/main/java/dev/twme/debugstickpro/listeners/ChunkUnloadEventListener.java b/src/main/java/dev/twme/debugstickpro/listeners/ChunkUnloadEventListener.java index 31b4e01..b601f75 100644 --- a/src/main/java/dev/twme/debugstickpro/listeners/ChunkUnloadEventListener.java +++ b/src/main/java/dev/twme/debugstickpro/listeners/ChunkUnloadEventListener.java @@ -14,7 +14,7 @@ public void onChunkUnload(ChunkUnloadEvent event) { Entity[] entities = event.getChunk().getEntities(); for (Entity entity : entities) { - if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY)){ + if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY, org.bukkit.persistence.PersistentDataType.STRING)){ FreezeBlockManager.removeOnChunkLoadOrUnload(entity); } } diff --git a/src/main/java/dev/twme/debugstickpro/listeners/PlayerChangedWorldEventListener.java b/src/main/java/dev/twme/debugstickpro/listeners/PlayerChangedWorldEventListener.java index b094711..fca0a85 100644 --- a/src/main/java/dev/twme/debugstickpro/listeners/PlayerChangedWorldEventListener.java +++ b/src/main/java/dev/twme/debugstickpro/listeners/PlayerChangedWorldEventListener.java @@ -10,7 +10,7 @@ public class PlayerChangedWorldEventListener implements Listener { @EventHandler public void onPlayerChangedWorldEvent(PlayerChangedWorldEvent event) { event.getFrom().getEntities().forEach(entity -> { - if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY)) { + if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY, org.bukkit.persistence.PersistentDataType.STRING)) { FreezeBlockManager.removeOnChunkLoadOrUnload(entity); } }); diff --git a/src/main/java/dev/twme/debugstickpro/listeners/PlayerJoinListener.java b/src/main/java/dev/twme/debugstickpro/listeners/PlayerJoinListener.java index ad4754f..bfca1cb 100644 --- a/src/main/java/dev/twme/debugstickpro/listeners/PlayerJoinListener.java +++ b/src/main/java/dev/twme/debugstickpro/listeners/PlayerJoinListener.java @@ -17,7 +17,7 @@ public class PlayerJoinListener implements Listener { public void onPlayerJoinEvent(PlayerJoinEvent event) { Player player = event.getPlayer(); - PlayerLanguageManager.setPlayerLocale(player.getUniqueId(), player.locale().toString()); + PlayerLanguageManager.setPlayerLocale(player.getUniqueId(), player.getLocale()); UUID playerUUID = event.getPlayer().getUniqueId(); PlayerDataManager.setPlayerData(playerUUID, new PlayerData()); diff --git a/src/main/java/dev/twme/debugstickpro/listeners/PlayerLocaleChangeEventListener.java b/src/main/java/dev/twme/debugstickpro/listeners/PlayerLocaleChangeEventListener.java index 9eef867..6e469ac 100644 --- a/src/main/java/dev/twme/debugstickpro/listeners/PlayerLocaleChangeEventListener.java +++ b/src/main/java/dev/twme/debugstickpro/listeners/PlayerLocaleChangeEventListener.java @@ -10,6 +10,6 @@ public class PlayerLocaleChangeEventListener implements Listener { @EventHandler public void onPlayerLocaleChangeEvent(PlayerLocaleChangeEvent event) { Player player = event.getPlayer(); - PlayerLanguageManager.setPlayerLocale(player.getUniqueId(), player.locale().toString()); + PlayerLanguageManager.setPlayerLocale(player.getUniqueId(), player.getLocale()); } } diff --git a/src/main/java/dev/twme/debugstickpro/listeners/WorldUnloadEventListener.java b/src/main/java/dev/twme/debugstickpro/listeners/WorldUnloadEventListener.java index 1c7d597..3cc1a61 100644 --- a/src/main/java/dev/twme/debugstickpro/listeners/WorldUnloadEventListener.java +++ b/src/main/java/dev/twme/debugstickpro/listeners/WorldUnloadEventListener.java @@ -10,7 +10,7 @@ public class WorldUnloadEventListener implements Listener { @EventHandler public void onWorldUnloadEvent(WorldUnloadEvent event) { event.getWorld().getEntities().forEach(entity -> { - if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY)) { + if (entity.getPersistentDataContainer().has(PersistentKeys.FREEZE_BLOCK_DISPLAY, org.bukkit.persistence.PersistentDataType.STRING)) { FreezeBlockManager.removeOnChunkLoadOrUnload(entity); } }); diff --git a/src/main/java/dev/twme/debugstickpro/localization/PlayerLanguageManager.java b/src/main/java/dev/twme/debugstickpro/localization/PlayerLanguageManager.java index 4f7634f..956ae5a 100644 --- a/src/main/java/dev/twme/debugstickpro/localization/PlayerLanguageManager.java +++ b/src/main/java/dev/twme/debugstickpro/localization/PlayerLanguageManager.java @@ -2,15 +2,15 @@ import dev.twme.debugstickpro.config.ConfigFile; -import java.util.HashMap; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class PlayerLanguageManager { /** * This is the map of the player language */ - private static final HashMap playerLang = new HashMap<>(); + private static final ConcurrentHashMap playerLang = new ConcurrentHashMap<>(); /** * Get the locale of the player diff --git a/src/main/java/dev/twme/debugstickpro/mode/copy/CopyLeftClick.java b/src/main/java/dev/twme/debugstickpro/mode/copy/CopyLeftClick.java index e58dbd6..42a7735 100644 --- a/src/main/java/dev/twme/debugstickpro/mode/copy/CopyLeftClick.java +++ b/src/main/java/dev/twme/debugstickpro/mode/copy/CopyLeftClick.java @@ -45,7 +45,7 @@ public static void onLeftClick(UUID playerUUID, PlayerData playerData) { if (block.getType() == Material.PLAYER_HEAD) { if (block.getState() instanceof Skull) { if (((Skull) block.getState()).hasOwner()) { - playerData.setCopiedSkullBlockPlayerProfile(((Skull) block.getState()).getPlayerProfile()); + playerData.setCopiedSkullBlockPlayerProfile(((Skull) block.getState()).getOwnerProfile()); } } } else { diff --git a/src/main/java/dev/twme/debugstickpro/mode/copy/CopyRightClick.java b/src/main/java/dev/twme/debugstickpro/mode/copy/CopyRightClick.java index 38033d3..01b4128 100644 --- a/src/main/java/dev/twme/debugstickpro/mode/copy/CopyRightClick.java +++ b/src/main/java/dev/twme/debugstickpro/mode/copy/CopyRightClick.java @@ -1,6 +1,5 @@ package dev.twme.debugstickpro.mode.copy; -import com.destroystokyo.paper.profile.PlayerProfile; import dev.twme.debugstickpro.blockdatautil.BlockDataSeparater; import dev.twme.debugstickpro.blockdatautil.SubBlockData; import dev.twme.debugstickpro.events.CopyModeChangingBlockEvent; @@ -14,6 +13,7 @@ import org.bukkit.block.Skull; import org.bukkit.entity.Player; import org.bukkit.profile.PlayerTextures; +import org.bukkit.profile.PlayerProfile; import java.util.List; import java.util.UUID; @@ -47,14 +47,14 @@ public static void onRightClick(UUID playerUUID, PlayerData playerData) { if (block.getState() instanceof Skull) { Skull skull = (Skull) block.getState(); if (playerData.getCopiedSkullBlockPlayerProfile() != null) { - skull.setPlayerProfile(playerData.getCopiedSkullBlockPlayerProfile()); + skull.setOwnerProfile(playerData.getCopiedSkullBlockPlayerProfile()); } else { if (skull.hasOwner()) { - PlayerProfile playerProfile = skull.getPlayerProfile(); + PlayerProfile playerProfile = skull.getOwnerProfile(); PlayerTextures textures = playerProfile.getTextures(); textures.clear(); playerProfile.setTextures(textures); - skull.setPlayerProfile(playerProfile); + skull.setOwnerProfile(playerProfile); } } skull.update(true); diff --git a/src/main/java/dev/twme/debugstickpro/mode/freeze/FreezeBlockManager.java b/src/main/java/dev/twme/debugstickpro/mode/freeze/FreezeBlockManager.java index 3284a8f..210a59c 100644 --- a/src/main/java/dev/twme/debugstickpro/mode/freeze/FreezeBlockManager.java +++ b/src/main/java/dev/twme/debugstickpro/mode/freeze/FreezeBlockManager.java @@ -5,12 +5,14 @@ import com.github.retrooper.packetevents.protocol.item.type.ItemTypes; import com.github.retrooper.packetevents.util.Vector3f; import dev.twme.debugstickpro.DebugStickPro; +import dev.twme.debugstickpro.scheduler.PlatformScheduler; import dev.twme.debugstickpro.utils.PersistentKeys; import io.github.retrooper.packetevents.util.SpigotConversionUtil; -import me.tofaa.entitylib.EntityLib; -import me.tofaa.entitylib.meta.display.BlockDisplayMeta; -import me.tofaa.entitylib.meta.display.ItemDisplayMeta; -import me.tofaa.entitylib.wrapper.WrapperEntity; +import io.github.twme.virtualentities.VirtualEntity; +import io.github.twme.virtualentities.VirtualEntityManager; +import io.github.twme.virtualentities.VirtualViewer; +import io.github.twme.virtualentities.metadata.EntityMetadataFlags; +import io.github.twme.virtualentities.metadata.GeneratedEntityMetadataKeys; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -27,21 +29,20 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; public class FreezeBlockManager { private static final BlockFace[] ADJACENT_FACES = { BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN }; - private static final Set freezeBlockLocations = new HashSet<>(); - private static final Map> playerFrozenBlockData = new HashMap<>(); + private static final Set freezeBlockLocations = ConcurrentHashMap.newKeySet(); + private static final Map> playerFrozenBlockData = new ConcurrentHashMap<>(); private static final Map protectedDependentBlocks = new ConcurrentHashMap<>(); public static void freezeBlock(UUID playerUUID, Block block) { @@ -50,7 +51,10 @@ public static void freezeBlock(UUID playerUUID, Block block) { return; } - ArrayList freezeBlockList = playerFrozenBlockData.computeIfAbsent(playerUUID, ignored -> new ArrayList<>()); + CopyOnWriteArrayList freezeBlockList = playerFrozenBlockData.computeIfAbsent( + playerUUID, + ignored -> new CopyOnWriteArrayList<>() + ); FreezeBlockData freezeBlock = freezeBlockBuilder(block.getLocation()); freezeBlockList.add(freezeBlock); freezeBlockLocations.add(freezeLocation); @@ -59,7 +63,7 @@ public static void freezeBlock(UUID playerUUID, Block block) { } public static void removeOneBlock(UUID playerUUID, Block block) { - ArrayList freezeBlocks = playerFrozenBlockData.get(playerUUID); + CopyOnWriteArrayList freezeBlocks = playerFrozenBlockData.get(playerUUID); if (freezeBlocks == null) { return; } @@ -69,9 +73,7 @@ public static void removeOneBlock(UUID playerUUID, Block block) { return; } - Iterator iterator = freezeBlocks.iterator(); - while (iterator.hasNext()) { - FreezeBlockData frozenData = iterator.next(); + for (FreezeBlockData frozenData : freezeBlocks) { if (!frozenData.getBlock().getLocation().equals(block.getLocation())) { continue; } @@ -88,7 +90,7 @@ public static void removeOneBlock(UUID playerUUID, Block block) { removeDisplayEntity(frozenData.getBlockDisplay()); freezeBlockLocations.remove(freezeLocation); - iterator.remove(); + freezeBlocks.remove(frozenData); if (freezeBlocks.isEmpty()) { playerFrozenBlockData.remove(playerUUID); } @@ -97,7 +99,7 @@ public static void removeOneBlock(UUID playerUUID, Block block) { } public static void removeAllPlayerFrozenBlock(UUID playerUUID) { - ArrayList freezeBlocks = playerFrozenBlockData.remove(playerUUID); + CopyOnWriteArrayList freezeBlocks = playerFrozenBlockData.remove(playerUUID); if (freezeBlocks == null) { return; } @@ -144,7 +146,7 @@ public static void removeOnServerClose() { } public static int getFreezeBlockCount(UUID playerUUID) { - ArrayList freezeBlocks = playerFrozenBlockData.get(playerUUID); + CopyOnWriteArrayList freezeBlocks = playerFrozenBlockData.get(playerUUID); return freezeBlocks == null ? 0 : freezeBlocks.size(); } @@ -195,28 +197,43 @@ private static FreezeBlockData freezeBlockBuilder(Location location) { UUID itemDisplayUUID = UUID.randomUUID(); UUID blockDisplayUUID = UUID.randomUUID(); - WrapperEntity wrapperItemDisplayEntity = new WrapperEntity(itemDisplayUUID, EntityTypes.ITEM_DISPLAY); - ItemDisplayMeta itemDisplayMeta = (ItemDisplayMeta) wrapperItemDisplayEntity.getEntityMeta(); + VirtualEntityManager entityManager = DebugStickPro.getInstance().getVirtualEntityManager(); + VirtualEntity itemDisplay = entityManager.entity(EntityTypes.ITEM_DISPLAY) + .uuid(itemDisplayUUID) + .metadata() + .build(); ItemStack itemStack = ItemStack.builder().type(ItemTypes.TINTED_GLASS).amount(1).build(); - itemDisplayMeta.setItem(itemStack); - itemDisplayMeta.setScale(new Vector3f(1.001F, 1.001F, 1.001F)); - itemDisplayMeta.setGlowing(true); - addViewer(wrapperItemDisplayEntity); - wrapperItemDisplayEntity.spawn(SpigotConversionUtil.fromBukkitLocation(entityLocation)); + itemDisplay.metadata() + .set(GeneratedEntityMetadataKeys.ItemDisplay.ITEM_STACK, itemStack) + .set(GeneratedEntityMetadataKeys.Display.SCALE, new Vector3f(1.001F, 1.001F, 1.001F)) + .setFlag(EntityMetadataFlags.GLOWING, true); + addViewers(itemDisplay); + itemDisplay.spawn(SpigotConversionUtil.fromBukkitLocation(entityLocation)); Location blockDisplayLocation = SpecialBlockFilter.filter(block.getType(), location); - WrapperEntity wrapperBlockDisplayEntity = new WrapperEntity(blockDisplayUUID, EntityTypes.BLOCK_DISPLAY); - BlockDisplayMeta blockDisplayMeta = (BlockDisplayMeta) wrapperBlockDisplayEntity.getEntityMeta(); - blockDisplayMeta.setBlockId(SpigotConversionUtil.fromBukkitBlockData(block.getBlockData()).getGlobalId()); - wrapperBlockDisplayEntity.spawn(SpigotConversionUtil.fromBukkitLocation(blockDisplayLocation)); - addViewer(wrapperBlockDisplayEntity); + VirtualEntity blockDisplay = entityManager.entity(EntityTypes.BLOCK_DISPLAY) + .uuid(blockDisplayUUID) + .metadata() + .build(); + blockDisplay.metadata().set( + GeneratedEntityMetadataKeys.BlockDisplay.BLOCK_STATE, + SpigotConversionUtil.fromBukkitBlockData(block.getBlockData()).getGlobalId() + ); + addViewers(blockDisplay); + blockDisplay.spawn(SpigotConversionUtil.fromBukkitLocation(blockDisplayLocation)); return new FreezeBlockData(itemDisplayUUID, blockDisplayUUID, block, originalState); } - private static void addViewer(WrapperEntity wrapperEntity) { + private static void addViewers(VirtualEntity virtualEntity) { + var playerManager = com.github.retrooper.packetevents.PacketEvents.getAPI().getPlayerManager(); for (Player player : Bukkit.getOnlinePlayers()) { - wrapperEntity.addViewer(player.getUniqueId()); + VirtualViewer viewer = VirtualViewer.of( + player.getUniqueId(), + playerManager.getClientVersion(player), + packet -> playerManager.sendPacket(player, packet) + ); + virtualEntity.addViewer(viewer); } } @@ -266,21 +283,28 @@ private static void restoreDependentNeighborSnapshotLater(Map { - restoreDependentNeighborSnapshot(snapshot); - if (cleanupAfter) { - cleanupVerifiedDependents(snapshot); - } - }, delayTicks); + for (Map.Entry entry : snapshot.entrySet()) { + Location location = entry.getKey().getLocation(); + PlatformScheduler.runAtLocationLater(plugin, location, () -> { + restoreDependentNeighbor(entry); + if (cleanupAfter) { + cleanupVerifiedDependent(entry); + } + }, delayTicks); + } } - private static void cleanupVerifiedDependents(Map snapshot) { - for (Map.Entry entry : snapshot.entrySet()) { - String expected = entry.getValue(); - Block block = entry.getKey().getLocation().getBlock(); - if (expected.equals(block.getBlockData().getAsString())) { - protectedDependentBlocks.remove(entry.getKey()); - } + private static void restoreDependentNeighbor(Map.Entry entry) { + Block block = entry.getKey().getLocation().getBlock(); + String expectedData = entry.getValue(); + if (!expectedData.equals(block.getBlockData().getAsString())) { + block.setBlockData(Bukkit.createBlockData(expectedData), false); + } + } + + private static void cleanupVerifiedDependent(Map.Entry entry) { + if (entry.getValue().equals(entry.getKey().getLocation().getBlock().getBlockData().getAsString())) { + protectedDependentBlocks.remove(entry.getKey()); } } @@ -289,9 +313,6 @@ private static void registerProtectedDependentSnapshot(Map playerDataMap = new HashMap<>(); + private static final ConcurrentHashMap playerDataMap = new ConcurrentHashMap<>(); /** * player enable display set */ - private static final HashSet playerEnableDisplaySet = new HashSet<>(); + private static final Set playerEnableDisplaySet = ConcurrentHashMap.newKeySet(); /** * set player data @@ -97,8 +97,8 @@ public static void removePlayerFromDisplayList(UUID uuid) { * * @return display set */ - public static HashSet getDisplaySet() { - return playerEnableDisplaySet; + public static Set getDisplaySetSnapshot() { + return Set.copyOf(playerEnableDisplaySet); } /** diff --git a/src/main/java/dev/twme/debugstickpro/scheduler/PlatformScheduler.java b/src/main/java/dev/twme/debugstickpro/scheduler/PlatformScheduler.java new file mode 100644 index 0000000..d324202 --- /dev/null +++ b/src/main/java/dev/twme/debugstickpro/scheduler/PlatformScheduler.java @@ -0,0 +1,65 @@ +package dev.twme.debugstickpro.scheduler; + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +/** + * Routes work through the scheduler required by the running server. + */ +public final class PlatformScheduler { + private static final boolean FOLIA = detectFolia(); + + private PlatformScheduler() { + } + + public static Cancellable runGlobalAtFixedRate(Plugin plugin, Runnable runnable, long periodTicks) { + long period = Math.max(1L, periodTicks); + if (FOLIA) { + ScheduledTask task = Bukkit.getGlobalRegionScheduler() + .runAtFixedRate(plugin, ignored -> runnable.run(), 1L, period); + return task::cancel; + } + + BukkitTask task = Bukkit.getScheduler().runTaskTimer(plugin, runnable, 1L, period); + return task::cancel; + } + + public static void runForPlayer(Plugin plugin, Player player, Runnable runnable) { + if (FOLIA) { + player.getScheduler().run(plugin, ignored -> runnable.run(), null); + return; + } + runnable.run(); + } + + public static void runAtLocationLater(Plugin plugin, Location location, Runnable runnable, long delayTicks) { + long delay = Math.max(1L, delayTicks); + if (FOLIA) { + Bukkit.getRegionScheduler().runDelayed(plugin, location, ignored -> runnable.run(), delay); + return; + } + Bukkit.getScheduler().runTaskLater(plugin, runnable, delay); + } + + public static boolean isFolia() { + return FOLIA; + } + + private static boolean detectFolia() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException ignored) { + return false; + } + } + + @FunctionalInterface + public interface Cancellable { + void cancel(); + } +} diff --git a/src/main/java/dev/twme/debugstickpro/utils/DebugStickItem.java b/src/main/java/dev/twme/debugstickpro/utils/DebugStickItem.java index ffe567f..872f05e 100644 --- a/src/main/java/dev/twme/debugstickpro/utils/DebugStickItem.java +++ b/src/main/java/dev/twme/debugstickpro/utils/DebugStickItem.java @@ -2,16 +2,12 @@ import dev.twme.debugstickpro.config.ConfigFile; import dev.twme.debugstickpro.playerdata.DebugStickMode; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; public final class DebugStickItem { - private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - public static boolean isDebugStickItem(ItemStack item) { if (item == null) { return false; @@ -23,7 +19,7 @@ public static boolean isDebugStickItem(ItemStack item) { if (itemMeta == null) { return false; } - if (!itemMeta.getPersistentDataContainer().has(PersistentKeys.DEBUG_STICK_ITEM)) { + if (!itemMeta.getPersistentDataContainer().has(PersistentKeys.DEBUG_STICK_ITEM, PersistentDataType.STRING)) { return false; } return true; @@ -37,9 +33,9 @@ public static boolean checkPlayer(Player player) { public static ItemStack getDebugStickItem() { ItemStack itemStack = new ItemStack(ConfigFile.DebugStickItem.Material); ItemMeta itemMeta = itemStack.getItemMeta(); - Component displayName = MINI_MESSAGE.deserialize(ConfigFile.DebugStickItem.DisplayName); - itemMeta.displayName(displayName); - itemMeta.lore(ConfigFile.DebugStickItem.Lore); + TextUtil.setDisplayName(itemMeta, net.kyori.adventure.text.minimessage.MiniMessage.miniMessage() + .deserialize(ConfigFile.DebugStickItem.DisplayName)); + TextUtil.setLore(itemMeta, ConfigFile.DebugStickItem.Lore); itemMeta.getPersistentDataContainer().set(PersistentKeys.DEBUG_STICK_ITEM, PersistentDataType.STRING, "debugstickpro"); if (ConfigFile.DebugStickItem.CustomModelData.Enabled) { itemMeta.setCustomModelData(ConfigFile.DebugStickItem.CustomModelData.ClassicMode); diff --git a/src/main/java/dev/twme/debugstickpro/utils/Log.java b/src/main/java/dev/twme/debugstickpro/utils/Log.java index e98097a..a543bdc 100644 --- a/src/main/java/dev/twme/debugstickpro/utils/Log.java +++ b/src/main/java/dev/twme/debugstickpro/utils/Log.java @@ -3,10 +3,10 @@ import dev.twme.debugstickpro.DebugStickPro; import org.bukkit.Bukkit; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; public final class Log { - private static final HashMap warnCooldown = new HashMap<>(); + private static final ConcurrentHashMap warnCooldown = new ConcurrentHashMap<>(); private static final String prefix = "[DebugStickPro] "; diff --git a/src/main/java/dev/twme/debugstickpro/utils/TextUtil.java b/src/main/java/dev/twme/debugstickpro/utils/TextUtil.java new file mode 100644 index 0000000..c8c6494 --- /dev/null +++ b/src/main/java/dev/twme/debugstickpro/utils/TextUtil.java @@ -0,0 +1,64 @@ +package dev.twme.debugstickpro.utils; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.audience.Audience; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.meta.ItemMeta; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public final class TextUtil { + private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.legacySection(); + + private TextUtil() { + } + + public static void send(CommandSender sender, Component component) { + if (Audience.class.isInstance(sender)) { + Audience audience = Audience.class.cast(sender); + audience.sendMessage(component); + return; + } + sender.sendMessage(LEGACY.serialize(component)); + } + + public static void sendActionBar(Player player, Component component) { + if (Audience.class.isInstance(player)) { + Audience audience = Audience.class.cast(player); + audience.sendActionBar(component); + return; + } + player.spigot().sendMessage( + net.md_5.bungee.api.ChatMessageType.ACTION_BAR, + net.md_5.bungee.api.chat.TextComponent.fromLegacyText(LEGACY.serialize(component)) + ); + } + + public static void setDisplayName(ItemMeta itemMeta, Component component) { + if (!invokePaperItemMeta(itemMeta, "displayName", Component.class, component)) { + itemMeta.setDisplayName(LEGACY.serialize(component)); + } + } + + public static void setLore(ItemMeta itemMeta, List lore) { + if (!invokePaperItemMeta(itemMeta, "lore", List.class, lore)) { + itemMeta.setLore(lore.stream().map(LEGACY::serialize).toList()); + } + } + + private static boolean invokePaperItemMeta(ItemMeta itemMeta, String methodName, Class parameterType, Object value) { + try { + ItemMeta.class.getMethod(methodName, parameterType).invoke(itemMeta, value); + return true; + } catch (NoSuchMethodException ignored) { + return false; + } catch (IllegalAccessException exception) { + throw new IllegalStateException("Cannot access Paper ItemMeta method " + methodName, exception); + } catch (InvocationTargetException exception) { + throw new IllegalStateException("Paper ItemMeta method failed: " + methodName, exception.getCause()); + } + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ef22b32..d69a2e6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ name: DebugStickPro version: '${project.version}' main: dev.twme.debugstickpro.DebugStickPro -api-version: '26.2' +api-version: '1.19' +folia-supported: true prefix: DebugStickPro authors: [TWME] website: https://github.com/TWME-TW/DebugStickPro diff --git a/src/test/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataPropertyTest.java b/src/test/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataPropertyTest.java new file mode 100644 index 0000000..d634441 --- /dev/null +++ b/src/test/java/dev/twme/debugstickpro/blockdatautil/ReflectiveBlockDataPropertyTest.java @@ -0,0 +1,76 @@ +package dev.twme.debugstickpro.blockdatautil; + +import org.bukkit.block.data.BlockData; +import org.junit.Test; + +import java.lang.reflect.Proxy; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static org.junit.Assert.assertEquals; + +public class ReflectiveBlockDataPropertyTest { + @Test + public void cyclesBooleanPropertiesAndCopiesTheirValue() { + Map sourceState = new ConcurrentHashMap<>(); + sourceState.put("enabled", false); + BooleanData source = proxy(BooleanData.class, sourceState); + + SubBlockData property = ReflectiveBlockDataProperty.property( + source, "BooleanData", "Enabled", "isEnabled", "setEnabled" + ); + assertEquals("false", property.getDataAsString()); + assertEquals("true", property.nextData().getDataAsString()); + assertEquals("false", property.previousData().getDataAsString()); + + property.nextData(); + Map targetState = new ConcurrentHashMap<>(); + targetState.put("enabled", false); + property.copyTo(proxy(BooleanData.class, targetState)); + assertEquals(true, targetState.get("enabled")); + } + + @Test + public void wrapsRangedIntegerProperties() { + Map state = new ConcurrentHashMap<>(); + state.put("level", 3); + RangedData data = proxy(RangedData.class, state); + + SubBlockData property = ReflectiveBlockDataProperty.rangedProperty( + data, "RangedData", "Level", "getLevel", "setLevel", "getMinimumLevel", "getMaximumLevel" + ); + assertEquals("1", property.nextData().getDataAsString()); + assertEquals("3", property.previousData().getDataAsString()); + } + + @SuppressWarnings("unchecked") + private static T proxy(Class type, Map state) { + return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, (proxy, method, args) -> { + return switch (method.getName()) { + case "isEnabled" -> state.get("enabled"); + case "setEnabled" -> state.put("enabled", args[0]); + case "getLevel" -> state.get("level"); + case "setLevel" -> state.put("level", args[0]); + case "getMinimumLevel" -> 1; + case "getMaximumLevel" -> 3; + default -> throw new AssertionError("Unexpected BlockData method call: " + method.getName()); + }; + }); + } + + private interface BooleanData extends BlockData { + boolean isEnabled(); + + void setEnabled(boolean enabled); + } + + private interface RangedData extends BlockData { + int getLevel(); + + void setLevel(int level); + + int getMinimumLevel(); + + int getMaximumLevel(); + } +} From 44c78d07ce152b06a1349d07f99969d462cb229b Mon Sep 17 00:00:00 2001 From: twme-ai <299940106+twme-ai@users.noreply.github.com> Date: Tue, 18 Aug 2026 19:51:19 +0000 Subject: [PATCH 2/2] ci: provision Node for all runtime jobs --- .github/workflows/maven.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 5edf86f..5b0ec3b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -67,7 +67,6 @@ jobs: java-version: ${{ matrix.java }} distribution: temurin - name: Set up Node.js - if: matrix.mode == 'mineflayer' uses: actions/setup-node@v6 with: node-version: 24