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