Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<VirtualEntity>` 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;

Expand Down Expand Up @@ -69,6 +70,18 @@ private static Matrix4f shear(Matrix4fc mat, float yx, float xy) {
return new Matrix4f(mat).mul(shearMatrix);
}

private static Quaternionf orientation(Vector3f zAxis, Vector3f yAxis) {
// 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();
}

/**
* Gets the transformation matrix for a unit square.
*
Expand Down Expand Up @@ -116,7 +129,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;

Expand Down Expand Up @@ -174,7 +187,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);
Expand Down Expand Up @@ -216,7 +229,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;
Expand Down Expand Up @@ -337,7 +350,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
Expand Down Expand Up @@ -374,7 +387,7 @@ public static List<TRSResult> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,65 @@
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<Vector3f[]> 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, 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) {
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() {
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)));
}
}
}

@Test
void triangleBackgroundPiecesReachTheRequestedVertices() {
Vector3f[] vertices = points(0f, 0f, 0f, 4f, 0f, 0f, 0f, 3f, 0f);
List<TRSResult> 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() {
Expand Down Expand Up @@ -107,4 +166,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);
}
}
6 changes: 3 additions & 3 deletions integration/mineflayer/run-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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=""

Expand Down
29 changes: 26 additions & 3 deletions integration/paper-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<version>1.0.0</version>

<properties>
<maven.compiler.release>21</maven.compiler.release>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Expand Down Expand Up @@ -37,13 +37,36 @@
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<version>26.3.build.41-alpha</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.retrooper</groupId>
<artifactId>packetevents-spigot</artifactId>
<version>2.13.0</version>
<version>2.14.0</version>
<scope>provided</scope>
</dependency>
<!--
Same Adventure 5.2.0 pins as the packet module: PacketEvents pull in Adventure
4.26.1 and would otherwise win Maven's nearest-wins mediation for the
compile classpath. See packet/pom.xml.
-->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
6 changes: 3 additions & 3 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -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
28 changes: 26 additions & 2 deletions packet/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,37 @@
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<version>26.3.build.41-alpha</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.retrooper</groupId>
<artifactId>packetevents-spigot</artifactId>
<version>2.13.0</version>
<version>2.14.0</version>
<scope>provided</scope>
</dependency>
<!--
PacketEvents still ships Adventure 4.26.1 while Paper 26.3 builds against 5.2.0.
These pins keep the compile classpath on Paper's version instead of Maven's
nearest-wins pick, which fails with "cannot access
net.kyori.adventure.text.object.ObjectContentsLike".
-->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion paper/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<version>26.3.build.41-alpha</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
2 changes: 1 addition & 1 deletion spigot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.4-R0.1-SNAPSHOT</version>
<version>26.3-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
Loading