From b55b6f2b83e7161f728f173325f862a60ab009ff Mon Sep 17 00:00:00 2001 From: TWME's AI Assistant <299940106+twme-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 06:15:12 +0000 Subject: [PATCH 1/3] feat: support Minecraft 26.3 - Bump paper-api to 26.3.build.41-alpha and spigot-api to 26.3-R0.1-SNAPSHOT for the paper, spigot, and packet modules. - Bump PacketEvents to 2.14.0, the first release that ships the 26.3 protocol, in the packet module and the integration fixture. - Pin Adventure 5.2.0 on the Paper compile classpaths. PacketEvents declares Adventure 4.26.1 before paper-api, so mediation previously produced a mixed 4.x/5.x classpath and compilation failed on net.kyori.adventure.text.object.ObjectContentsLike. - Build on JDK 25 in CI and JitPack, because the 26.3 APIs are Java 25 class files that javac 21 cannot read. The published modules keep Java 17 bytecode, so the Text Display floor is unchanged. - Ship PacketEvents 2.14.0 in the Mineflayer harness and document the 26.3 dependency matrix in the README. --- .github/workflows/e2e.yml | 4 ++-- .github/workflows/maven.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- README.md | 20 +++++++++++++++----- integration/mineflayer/run-e2e.sh | 6 +++--- integration/paper-plugin/pom.xml | 29 ++++++++++++++++++++++++++--- jitpack.yml | 6 +++--- packet/pom.xml | 28 ++++++++++++++++++++++++++-- paper/pom.xml | 2 +- spigot/pom.xml | 2 +- 10 files changed, 81 insertions(+), 24 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d3a91b2..9ab55dd 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v5 with: - java-version: '21' + java-version: '25' distribution: temurin cache: maven - name: Set up Node.js diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 5b24081..3e7ad7f 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -18,10 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v5 with: - java-version: '21' + java-version: '25' distribution: 'temurin' cache: maven - name: Build with Maven diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1f1eab7..8dc7ac1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v5 with: - java-version: '21' + java-version: '25' distribution: temurin cache: maven server-id: twme-repo-releases diff --git a/README.md b/README.md index c3c60ae..c79f291 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,21 @@ Version 3.0.0 replaces the packet module's EntityLib implementation with Virtual ## Requirements -- Java 17+ -- Minecraft 1.19.4+ for Text Display entities -- PacketEvents 2.13.0+ initialized by the host platform when using the packet module +- Minecraft 26.3 is the compiled and verified target. The modules keep Java 17 bytecode and their API usage is unchanged by this upgrade, so the existing Text Display floor (Minecraft 1.19.4+) is unaffected. +- Java 17+ at runtime: the published modules keep Java 17 bytecode +- Java 25 to build from source, because the 26.3 APIs are Java 25 class files +- PacketEvents 2.14.0+ initialized by the host platform when using the packet module - VirtualEntities v0.9.0 is pulled transitively by the packet module +| Component | Version | +| --- | --- | +| Minecraft | 26.3 | +| Paper API | `26.3.build.41-alpha` | +| Spigot API | `26.3-R0.1-SNAPSHOT` | +| PacketEvents | 2.14.0 | +| Build JDK | 25 | +| Runtime bytecode | Java 17 | + ## Installation ### Official TWME Maven repository (recommended) @@ -128,13 +138,13 @@ Builders provide color, brightness, see-through, view range, double-sided, root- ## Migrating From 2.x - Remove EntityLib initialization and dependencies. -- Use PacketEvents 2.13.0+ and let VirtualEntities manage packet-only entities. +- Use PacketEvents 2.14.0+ and let VirtualEntities manage packet-only entities. - Prefer `new PacketShapeFactory(VirtualEntities.create())` at plugin scope; the no-argument constructor remains available for small standalone uses and closes its manager when the factory is closed. - `PacketLine`, `PacketPolyline`, `PacketTriangle`, and `PacketParallelogram` now expose `List` from `getEntities()` instead of EntityLib wrappers. ## Verification -`integration/mineflayer/run-e2e.sh` launches Paper 1.21.11 with PacketEvents, creates a packet-only root-anchored line, and verifies from Mineflayer that Text Display spawn, metadata rebase, root movement, and packet bundle ordering all work. The same test can run from the manual GitHub Actions E2E workflow. +`integration/mineflayer/run-e2e.sh` launches Paper 1.21.11 with PacketEvents, creates a packet-only root-anchored line, and verifies from Mineflayer that Text Display spawn, metadata rebase, root movement, and packet bundle ordering all work. The same test can run from the manual GitHub Actions E2E workflow. Mineflayer still only speaks up to Minecraft 26.1, so that harness keeps its 1.21.11 server even though the modules target 26.3. ## Credits diff --git a/integration/mineflayer/run-e2e.sh b/integration/mineflayer/run-e2e.sh index 0ffecf3..6a01624 100755 --- a/integration/mineflayer/run-e2e.sh +++ b/integration/mineflayer/run-e2e.sh @@ -3,14 +3,14 @@ set -euo pipefail readonly PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" readonly PAPER_API="https://fill.papermc.io/v3/projects/paper/versions/1.21.11/builds/latest" -readonly PACKETEVENTS_URL="https://github.com/retrooper/packetevents/releases/download/v2.13.0/packetevents-spigot-2.13.0.jar" -readonly PACKETEVENTS_SHA256="6d9ece0d87ee727a79a20b7ffbd432021609c6f52bafcb654fc2d3e9b6f064c5" +readonly PACKETEVENTS_URL="https://github.com/retrooper/packetevents/releases/download/v2.14.0/packetevents-spigot-2.14.0.jar" +readonly PACKETEVENTS_SHA256="060087c58ec268eae7dd0eb1f17ac3bea1eaadda1693414141ff64e506091bd7" readonly PORT="${TDS_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="${PROJECT_DIR}/target/mineflayer-cache" readonly PAPER_JAR="${CACHE_DIR}/paper-1.21.11.jar" -readonly PACKETEVENTS_JAR="${CACHE_DIR}/packetevents-spigot-2.13.0.jar" +readonly PACKETEVENTS_JAR="${CACHE_DIR}/packetevents-spigot-2.14.0.jar" readonly PLUGIN_JAR="${PROJECT_DIR}/integration/paper-plugin/target/TextDisplayShapesIntegration.jar" SERVER_PID="" diff --git a/integration/paper-plugin/pom.xml b/integration/paper-plugin/pom.xml index 12c3f68..506ab12 100644 --- a/integration/paper-plugin/pom.xml +++ b/integration/paper-plugin/pom.xml @@ -9,7 +9,7 @@ 1.0.0 - 21 + 25 UTF-8 @@ -37,13 +37,36 @@ io.papermc.paper paper-api - 1.21.11-R0.1-SNAPSHOT + 26.3.build.41-alpha provided com.github.retrooper packetevents-spigot - 2.13.0 + 2.14.0 + provided + + + + net.kyori + adventure-api + 5.2.0 + provided + + + net.kyori + adventure-text-minimessage + 5.2.0 + provided + + + net.kyori + adventure-text-serializer-legacy + 5.2.0 provided diff --git a/jitpack.yml b/jitpack.yml index 255e0f4..33caed7 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,5 +1,5 @@ jdk: - - openjdk21 + - openjdk25 before_install: - - sdk install java 21-open - - sdk use java 21-open + - sdk install java 25-open + - sdk use java 25-open diff --git a/packet/pom.xml b/packet/pom.xml index 06cc2cb..300e654 100644 --- a/packet/pom.xml +++ b/packet/pom.xml @@ -53,13 +53,37 @@ io.papermc.paper paper-api - 1.21.11-R0.1-SNAPSHOT + 26.3.build.41-alpha provided com.github.retrooper packetevents-spigot - 2.13.0 + 2.14.0 + provided + + + + net.kyori + adventure-api + 5.2.0 + provided + + + net.kyori + adventure-text-minimessage + 5.2.0 + provided + + + net.kyori + adventure-text-serializer-legacy + 5.2.0 provided diff --git a/paper/pom.xml b/paper/pom.xml index 1c7ddb1..2c5c4fc 100644 --- a/paper/pom.xml +++ b/paper/pom.xml @@ -44,7 +44,7 @@ io.papermc.paper paper-api - 1.21.11-R0.1-SNAPSHOT + 26.3.build.41-alpha provided diff --git a/spigot/pom.xml b/spigot/pom.xml index ce2ed88..961d553 100644 --- a/spigot/pom.xml +++ b/spigot/pom.xml @@ -51,7 +51,7 @@ org.spigotmc spigot-api - 1.21.4-R0.1-SNAPSHOT + 26.3-R0.1-SNAPSHOT provided From 5bfe4d38531a0c60c3535759a56582b096ce37bf Mon Sep 17 00:00:00 2001 From: TWME's AI Assistant <299940106+twme-ai@users.noreply.github.com> Date: Sat, 26 Sep 2026 14:41:40 +0000 Subject: [PATCH 2/3] fix: orient text display backgrounds toward requested vertices --- .../util/TextDisplayUtil.java | 16 +++-- .../util/TextDisplayUtilTest.java | 58 +++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java b/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java index 3cc3a70..36cb3b6 100644 --- a/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java +++ b/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java @@ -69,6 +69,12 @@ private static Matrix4f shear(Matrix4fc mat, float yx, float xy) { return new Matrix4f(mat).mul(shearMatrix); } + private static Quaternionf orientation(Vector3f zAxis, Vector3f yAxis) { + // JOML lookAlong uses its direction as local +Z. Negating zAxis mirrors + // the local X axis, extending lines and faces away from their vertices. + return new Quaternionf().lookAlong(zAxis, yAxis).conjugate(); + } + /** * Gets the transformation matrix for a unit square. * @@ -116,7 +122,7 @@ public static TextDisplayTriangleResult textDisplayTriangle( float height = p3.dot(yAxis); float p3Width = p3.dot(xAxis); - Quaternionf rotation = new Quaternionf().lookAlong(new Vector3f(zAxis).mul(-1f), yAxis).conjugate(); + Quaternionf rotation = orientation(zAxis, yAxis); float shear = (width > 0.001f) ? p3Width / width : 0.0f; @@ -174,7 +180,7 @@ public static Matrix4f textDisplayLine(Vector3f point1, Vector3f point2, float t Vector3f xAxis = new Vector3f(direction).normalize(); Vector3f yAxis = new Vector3f(zAxis).cross(xAxis).normalize(); - Quaternionf rotation = new Quaternionf().lookAlong(new Vector3f(zAxis).mul(-1f), yAxis).conjugate(); + Quaternionf rotation = orientation(zAxis, yAxis); // Apply roll rotation around the line axis (X axis in local space) Quaternionf rollRotation = new Quaternionf().rotateX(roll); @@ -216,7 +222,7 @@ public static Matrix4f textDisplayParallelogram(Vector3f point1, Vector3f point2 float height = p3.dot(yAxis); float p3Width = p3.dot(xAxis); - Quaternionf rotation = new Quaternionf().lookAlong(new Vector3f(zAxis).mul(-1f), yAxis).conjugate(); + Quaternionf rotation = orientation(zAxis, yAxis); // Calculate shear amount (transforms rectangle into parallelogram) float shear = (width > 0.001f) ? p3Width / width : 0.0f; @@ -337,7 +343,7 @@ public static TRSResult computeParallelogramTRS(Vector3f point1, Vector3f point2 float height = p3vec.dot(yAxis); float p3Width = p3vec.dot(xAxis); - Quaternionf rotation = new Quaternionf().lookAlong(new Vector3f(zAxis).mul(-1f), yAxis).conjugate(); + Quaternionf rotation = orientation(zAxis, yAxis); float shear = (width > 0.001f) ? p3Width / width : 0.0f; // Inner 2x2 = scale(w,h) * shear(s,0) * unitSquare @@ -374,7 +380,7 @@ public static List computeTriangleTRS(Vector3f point1, Vector3f point float height = p3vec.dot(yAxis); float p3Width = p3vec.dot(xAxis); - Quaternionf rotation = new Quaternionf().lookAlong(new Vector3f(zAxis).mul(-1f), yAxis).conjugate(); + Quaternionf rotation = orientation(zAxis, yAxis); float shear = (width > 0.001f) ? p3Width / width : 0.0f; double w = width; diff --git a/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java b/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java index 7df7c85..d2cf366 100644 --- a/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java +++ b/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java @@ -13,6 +13,59 @@ class TextDisplayUtilTest { private static final float EPSILON = 1.0E-4F; + // A single-space Text Display background has width 4 and height 9. After the + // vanilla renderer's 180-degree Y rotation, -0.025 scale, and offset, these + // are the local coordinates of its corners before the shape transformation. + private static final Vector3f BOTTOM_LEFT = new Vector3f(-0.05f, 0f, 0f); + private static final Vector3f BOTTOM_RIGHT = new Vector3f(0.075f, 0f, 0f); + private static final Vector3f TOP_LEFT = new Vector3f(-0.05f, 0.25f, 0f); + + @Test + void lineBackgroundRunsBetweenItsEndpoints() { + List cases = List.of( + new Vector3f[] {new Vector3f(0f, 0f, 0f), new Vector3f(5f, 0f, 0f)}, + new Vector3f[] {new Vector3f(5f, 0f, 0f), new Vector3f(0f, 0f, 0f)}, + new Vector3f[] {new Vector3f(0f, 0f, 0f), new Vector3f(0f, 5f, 0f)}, + new Vector3f[] {new Vector3f(0f, 0f, 0f), new Vector3f(0f, 0f, 5f)}, + new Vector3f[] {new Vector3f(2f, 3f, 4f), new Vector3f(7f, 5f, 1f)}); + + for (Vector3f[] endpoints : cases) { + Matrix4f transform = TextDisplayUtil.textDisplayLine(endpoints[0], endpoints[1], 0.05f); + Vector3f start = new Vector3f(BOTTOM_LEFT).add(0f, 0.125f, 0f); + Vector3f end = new Vector3f(BOTTOM_RIGHT).add(0f, 0.125f, 0f); + assertVectorEquals(endpoints[0], transform.transformPosition(start)); + assertVectorEquals(endpoints[1], transform.transformPosition(end)); + } + } + + @Test + void parallelogramBackgroundCornersMatchTheirVertices() { + Vector3f[] vertices = points(2f, 3f, 4f, 7f, 3f, 4f, 2f, 3f, 8f); + Matrix4f expected = TextDisplayUtil.textDisplayParallelogram( + vertices[0], vertices[1], vertices[2]); + Matrix4f packetTransform = reconstruct(TextDisplayUtil.computeParallelogramTRS( + vertices[0], vertices[1], vertices[2])); + + for (Matrix4f transform : List.of(expected, packetTransform)) { + assertVectorEquals(vertices[0], transform.transformPosition(new Vector3f(BOTTOM_LEFT))); + assertVectorEquals(vertices[1], transform.transformPosition(new Vector3f(BOTTOM_RIGHT))); + assertVectorEquals(vertices[2], transform.transformPosition(new Vector3f(TOP_LEFT))); + } + } + + @Test + void triangleBackgroundPiecesReachTheRequestedVertices() { + Vector3f[] vertices = points(0f, 0f, 0f, 4f, 0f, 0f, 0f, 3f, 0f); + List pieces = TextDisplayUtil.computeTriangleTRS( + vertices[0], vertices[1], vertices[2]); + + assertVectorEquals(vertices[0], reconstruct(pieces.get(0)) + .transformPosition(new Vector3f(BOTTOM_LEFT))); + assertVectorEquals(vertices[1], reconstruct(pieces.get(1)) + .transformPosition(new Vector3f(BOTTOM_RIGHT))); + assertVectorEquals(vertices[2], reconstruct(pieces.get(2)) + .transformPosition(new Vector3f(TOP_LEFT))); + } @Test void parallelogramTrsReconstructsTransformationMatrix() { @@ -107,4 +160,9 @@ private static Vector3f[] points(float... coordinates) { private static void assertMatrixEquals(Matrix4f expected, Matrix4f actual) { assertTrue(expected.equals(actual, EPSILON), () -> "expected:\n" + expected + "\nbut was:\n" + actual); } + + private static void assertVectorEquals(Vector3f expected, Vector3f actual) { + assertTrue(expected.distance(actual) < EPSILON, + () -> "expected " + expected + " but was " + actual); + } } From 79f954c45dc665157dba93ed23bd0d41809f8d15 Mon Sep 17 00:00:00 2001 From: TWME's AI Assistant <299940106+twme-ai@users.noreply.github.com> Date: Sat, 26 Sep 2026 14:56:54 +0000 Subject: [PATCH 3/3] fix: make TextDisplay orientation stable across JOML versions --- .../util/TextDisplayUtil.java | 13 +++++++--- .../util/TextDisplayUtilTest.java | 24 ++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java b/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java index 36cb3b6..17be651 100644 --- a/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java +++ b/api/src/main/java/dev/twme/textdisplayshape/util/TextDisplayUtil.java @@ -7,6 +7,7 @@ import org.joml.Matrix4f; import org.joml.Matrix4fc; +import org.joml.Matrix3f; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -70,9 +71,15 @@ private static Matrix4f shear(Matrix4fc mat, float yx, float xy) { } private static Quaternionf orientation(Vector3f zAxis, Vector3f yAxis) { - // JOML lookAlong uses its direction as local +Z. Negating zAxis mirrors - // the local X axis, extending lines and faces away from their vertices. - return new Quaternionf().lookAlong(zAxis, yAxis).conjugate(); + // Construct the basis explicitly. Quaternionf.lookAlong has produced + // different handedness for some axes across JOML versions used by + // Minecraft servers; the supplied vertices must determine local +X. + Vector3f xAxis = new Vector3f(yAxis).cross(zAxis).normalize(); + Matrix3f basis = new Matrix3f() + .setColumn(0, xAxis) + .setColumn(1, yAxis) + .setColumn(2, zAxis); + return new Quaternionf().setFromNormalized(basis).normalize(); } /** diff --git a/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java b/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java index d2cf366..022c549 100644 --- a/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java +++ b/api/src/test/java/dev/twme/textdisplayshape/util/TextDisplayUtilTest.java @@ -26,7 +26,9 @@ void lineBackgroundRunsBetweenItsEndpoints() { new Vector3f[] {new Vector3f(0f, 0f, 0f), new Vector3f(5f, 0f, 0f)}, new Vector3f[] {new Vector3f(5f, 0f, 0f), new Vector3f(0f, 0f, 0f)}, new Vector3f[] {new Vector3f(0f, 0f, 0f), new Vector3f(0f, 5f, 0f)}, + new Vector3f[] {new Vector3f(0f, 5f, 0f), new Vector3f(0f, 0f, 0f)}, new Vector3f[] {new Vector3f(0f, 0f, 0f), new Vector3f(0f, 0f, 5f)}, + new Vector3f[] {new Vector3f(0f, 0f, 5f), new Vector3f(0f, 0f, 0f)}, new Vector3f[] {new Vector3f(2f, 3f, 4f), new Vector3f(7f, 5f, 1f)}); for (Vector3f[] endpoints : cases) { @@ -40,16 +42,20 @@ void lineBackgroundRunsBetweenItsEndpoints() { @Test void parallelogramBackgroundCornersMatchTheirVertices() { - Vector3f[] vertices = points(2f, 3f, 4f, 7f, 3f, 4f, 2f, 3f, 8f); - Matrix4f expected = TextDisplayUtil.textDisplayParallelogram( - vertices[0], vertices[1], vertices[2]); - Matrix4f packetTransform = reconstruct(TextDisplayUtil.computeParallelogramTRS( - vertices[0], vertices[1], vertices[2])); + for (Vector3f[] vertices : List.of( + points(2f, 3f, 4f, 7f, 3f, 4f, 2f, 3f, 8f), + points(2f, 3f, 4f, 2f, 3f, 8f, 7f, 3f, 4f), + points(2f, 3f, 8f, 2f, 3f, 4f, 2f, 7f, 8f))) { + Matrix4f expected = TextDisplayUtil.textDisplayParallelogram( + vertices[0], vertices[1], vertices[2]); + Matrix4f packetTransform = reconstruct(TextDisplayUtil.computeParallelogramTRS( + vertices[0], vertices[1], vertices[2])); - for (Matrix4f transform : List.of(expected, packetTransform)) { - assertVectorEquals(vertices[0], transform.transformPosition(new Vector3f(BOTTOM_LEFT))); - assertVectorEquals(vertices[1], transform.transformPosition(new Vector3f(BOTTOM_RIGHT))); - assertVectorEquals(vertices[2], transform.transformPosition(new Vector3f(TOP_LEFT))); + for (Matrix4f transform : List.of(expected, packetTransform)) { + assertVectorEquals(vertices[0], transform.transformPosition(new Vector3f(BOTTOM_LEFT))); + assertVectorEquals(vertices[1], transform.transformPosition(new Vector3f(BOTTOM_RIGHT))); + assertVectorEquals(vertices[2], transform.transformPosition(new Vector3f(TOP_LEFT))); + } } }