Skip to content

Commit 6c6bde5

Browse files
Copilotdmulloy2
andauthored
Add WrappedPositionMoveRotation for 1.21.2+ entity teleport packets (#3610)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dmulloy2 <3039119+dmulloy2@users.noreply.github.com>
1 parent f911ee9 commit 6c6bde5

4 files changed

Lines changed: 327 additions & 0 deletions

File tree

src/main/java/com/comphenix/protocol/events/AbstractStructure.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import com.comphenix.protocol.wrappers.WrappedMessageSignature;
5656
import com.comphenix.protocol.wrappers.WrappedNumberFormat;
5757
import com.comphenix.protocol.wrappers.WrappedParticle;
58+
import com.comphenix.protocol.wrappers.WrappedPositionMoveRotation;
5859
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
5960
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
6061
import com.comphenix.protocol.wrappers.WrappedRegistrable;
@@ -1297,6 +1298,20 @@ public StructureModifier<Iterable<PacketContainer>> getPacketBundles() {
12971298
));
12981299
}
12991300

1301+
/**
1302+
* Retrieve a read/write structure for {@link WrappedPositionMoveRotation} (available since Minecraft 1.21.2).
1303+
* <p>
1304+
* This is used in packets such as {@code ENTITY_TELEPORT} and {@code ENTITY_POSITION_SYNC}.
1305+
*
1306+
* @return A modifier for PositionMoveRotation fields.
1307+
*/
1308+
public StructureModifier<WrappedPositionMoveRotation> getPositionMoveRotation() {
1309+
return structureModifier.withType(
1310+
MinecraftReflection.getPositionMoveRotationClass(),
1311+
WrappedPositionMoveRotation.getConverter()
1312+
);
1313+
}
1314+
13001315
/**
13011316
* Represents an equivalent converter for ItemStack arrays.
13021317
* @author Kristian

src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,4 +1903,13 @@ public static boolean isMojangMapped() {
19031903

19041904
return isMojangMapped;
19051905
}
1906+
1907+
/**
1908+
* Retrieves the PositionMoveRotation class (introduced in 1.21.2).
1909+
*
1910+
* @return The PositionMoveRotation class.
1911+
*/
1912+
public static Class<?> getPositionMoveRotationClass() {
1913+
return getMinecraftClass("world.entity.PositionMoveRotation");
1914+
}
19061915
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package com.comphenix.protocol.wrappers;
2+
3+
import com.comphenix.protocol.reflect.EquivalentConverter;
4+
import com.comphenix.protocol.reflect.StructureModifier;
5+
import com.comphenix.protocol.reflect.accessors.Accessors;
6+
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
7+
import com.comphenix.protocol.utility.MinecraftReflection;
8+
9+
import org.bukkit.util.Vector;
10+
11+
/**
12+
* Wrapper around the NMS {@code PositionMoveRotation} record, introduced in Minecraft 1.21.2.
13+
* <p>
14+
* This type is used in packets such as {@code ENTITY_TELEPORT} and {@code ENTITY_POSITION_SYNC}
15+
* to represent combined position, movement delta and rotation.
16+
* <p>
17+
* Example usage:
18+
* <pre>{@code
19+
* WrappedPositionMoveRotation posRot = WrappedPositionMoveRotation.create(
20+
* new Vector(x, y, z), // position
21+
* new Vector(0, 0, 0), // deltaMovement
22+
* yaw, // yRot
23+
* pitch // xRot
24+
* );
25+
* packet.getPositionMoveRotation().write(0, posRot);
26+
* }</pre>
27+
*/
28+
public class WrappedPositionMoveRotation extends AbstractWrapper {
29+
30+
private static Class<?> NMS_CLASS;
31+
private static ConstructorAccessor CONSTRUCTOR;
32+
private static StructureModifier<Object> BASE_MODIFIER;
33+
34+
private final StructureModifier<Object> modifier;
35+
36+
private WrappedPositionMoveRotation(Object handle) {
37+
super(getNmsClass());
38+
setHandle(handle);
39+
this.modifier = getBaseModifier().withTarget(handle);
40+
}
41+
42+
private static Class<?> getNmsClass() {
43+
if (NMS_CLASS == null) {
44+
NMS_CLASS = MinecraftReflection.getPositionMoveRotationClass();
45+
}
46+
return NMS_CLASS;
47+
}
48+
49+
private static StructureModifier<Object> getBaseModifier() {
50+
if (BASE_MODIFIER == null) {
51+
BASE_MODIFIER = new StructureModifier<>(getNmsClass());
52+
}
53+
return BASE_MODIFIER;
54+
}
55+
56+
private static ConstructorAccessor getConstructor() {
57+
if (CONSTRUCTOR == null) {
58+
CONSTRUCTOR = Accessors.getConstructorAccessor(
59+
getNmsClass(),
60+
MinecraftReflection.getVec3DClass(),
61+
MinecraftReflection.getVec3DClass(),
62+
float.class,
63+
float.class
64+
);
65+
}
66+
return CONSTRUCTOR;
67+
}
68+
69+
/**
70+
* Creates a new {@code WrappedPositionMoveRotation} from the given NMS handle.
71+
*
72+
* @param handle - the NMS {@code PositionMoveRotation} instance.
73+
* @return the wrapper.
74+
*/
75+
public static WrappedPositionMoveRotation fromHandle(Object handle) {
76+
return new WrappedPositionMoveRotation(handle);
77+
}
78+
79+
/**
80+
* Creates a new {@code WrappedPositionMoveRotation} with the given values.
81+
*
82+
* @param position - the absolute position.
83+
* @param deltaMovement - the movement delta (velocity).
84+
* @param yRot - the yaw rotation.
85+
* @param xRot - the pitch rotation.
86+
* @return a new wrapper instance.
87+
*/
88+
public static WrappedPositionMoveRotation create(Vector position, Vector deltaMovement, float yRot, float xRot) {
89+
EquivalentConverter<Vector> conv = BukkitConverters.getVectorConverter();
90+
Object handle = getConstructor().invoke(
91+
conv.getGeneric(position),
92+
conv.getGeneric(deltaMovement),
93+
yRot,
94+
xRot
95+
);
96+
return new WrappedPositionMoveRotation(handle);
97+
}
98+
99+
/**
100+
* Retrieves the absolute position component.
101+
*
102+
* @return the position as a Bukkit {@link Vector}.
103+
*/
104+
public Vector getPosition() {
105+
return modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
106+
}
107+
108+
/**
109+
* Sets the absolute position component.
110+
*
111+
* @param position - the new position.
112+
*/
113+
public void setPosition(Vector position) {
114+
modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).write(0, position);
115+
}
116+
117+
/**
118+
* Retrieves the movement delta (velocity) component.
119+
*
120+
* @return the delta movement as a Bukkit {@link Vector}.
121+
*/
122+
public Vector getDeltaMovement() {
123+
return modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(1);
124+
}
125+
126+
/**
127+
* Sets the movement delta (velocity) component.
128+
*
129+
* @param deltaMovement - the new delta movement.
130+
*/
131+
public void setDeltaMovement(Vector deltaMovement) {
132+
modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).write(1, deltaMovement);
133+
}
134+
135+
/**
136+
* Retrieves the yaw rotation (y-axis rotation).
137+
*
138+
* @return the yaw as a float.
139+
*/
140+
public float getYRot() {
141+
StructureModifier<Float> floats = modifier.withType(float.class);
142+
return floats.read(0);
143+
}
144+
145+
/**
146+
* Sets the yaw rotation (y-axis rotation).
147+
*
148+
* @param yRot - the new yaw value.
149+
*/
150+
public void setYRot(float yRot) {
151+
modifier.withType(float.class).write(0, yRot);
152+
}
153+
154+
/**
155+
* Retrieves the pitch rotation (x-axis rotation).
156+
*
157+
* @return the pitch as a float.
158+
*/
159+
public float getXRot() {
160+
StructureModifier<Float> floats = modifier.withType(float.class);
161+
return floats.read(1);
162+
}
163+
164+
/**
165+
* Sets the pitch rotation (x-axis rotation).
166+
*
167+
* @param xRot - the new pitch value.
168+
*/
169+
public void setXRot(float xRot) {
170+
modifier.withType(float.class).write(1, xRot);
171+
}
172+
173+
/**
174+
* Returns an {@link EquivalentConverter} that converts between {@code WrappedPositionMoveRotation}
175+
* and the underlying NMS handle.
176+
*
177+
* @return the converter.
178+
*/
179+
public static EquivalentConverter<WrappedPositionMoveRotation> getConverter() {
180+
return Converters.handle(AbstractWrapper::getHandle,
181+
WrappedPositionMoveRotation::fromHandle, WrappedPositionMoveRotation.class);
182+
}
183+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package com.comphenix.protocol.wrappers;
2+
3+
import com.comphenix.protocol.BukkitInitialization;
4+
import com.comphenix.protocol.PacketType;
5+
import com.comphenix.protocol.events.PacketContainer;
6+
7+
import net.minecraft.world.entity.PositionMoveRotation;
8+
import net.minecraft.world.phys.Vec3;
9+
import org.bukkit.util.Vector;
10+
import org.junit.jupiter.api.BeforeAll;
11+
import org.junit.jupiter.api.Test;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
15+
public class WrappedPositionMoveRotationTest {
16+
17+
@BeforeAll
18+
public static void beforeClass() {
19+
BukkitInitialization.initializeAll();
20+
}
21+
22+
@Test
23+
public void testCreateAndRead() {
24+
Vector position = new Vector(1.5, 64.0, -3.5);
25+
Vector delta = new Vector(0.1, -0.05, 0.2);
26+
float yRot = 45.0f;
27+
float xRot = -10.0f;
28+
29+
WrappedPositionMoveRotation created = WrappedPositionMoveRotation.create(position, delta, yRot, xRot);
30+
31+
assertEquals(position.getX(), created.getPosition().getX(), 1e-6);
32+
assertEquals(position.getY(), created.getPosition().getY(), 1e-6);
33+
assertEquals(position.getZ(), created.getPosition().getZ(), 1e-6);
34+
35+
assertEquals(delta.getX(), created.getDeltaMovement().getX(), 1e-6);
36+
assertEquals(delta.getY(), created.getDeltaMovement().getY(), 1e-6);
37+
assertEquals(delta.getZ(), created.getDeltaMovement().getZ(), 1e-6);
38+
39+
assertEquals(yRot, created.getYRot(), 1e-6f);
40+
assertEquals(xRot, created.getXRot(), 1e-6f);
41+
}
42+
43+
@Test
44+
public void testEntityPositionSyncPacket() {
45+
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_POSITION_SYNC);
46+
47+
Vector position = new Vector(10.0, 70.0, -5.0);
48+
Vector delta = new Vector(0.0, 0.0, 0.0);
49+
float yRot = 90.0f;
50+
float xRot = 0.0f;
51+
52+
packet.getPositionMoveRotation().write(0,
53+
WrappedPositionMoveRotation.create(position, delta, yRot, xRot));
54+
55+
WrappedPositionMoveRotation result = packet.getPositionMoveRotation().read(0);
56+
57+
assertNotNull(result);
58+
59+
assertEquals(position.getX(), result.getPosition().getX(), 1e-6);
60+
assertEquals(position.getY(), result.getPosition().getY(), 1e-6);
61+
assertEquals(position.getZ(), result.getPosition().getZ(), 1e-6);
62+
63+
assertEquals(delta.getX(), result.getDeltaMovement().getX(), 1e-6);
64+
assertEquals(delta.getY(), result.getDeltaMovement().getY(), 1e-6);
65+
assertEquals(delta.getZ(), result.getDeltaMovement().getZ(), 1e-6);
66+
67+
assertEquals(yRot, result.getYRot(), 1e-6f);
68+
assertEquals(xRot, result.getXRot(), 1e-6f);
69+
}
70+
71+
@Test
72+
public void testEntityTeleportPacket() {
73+
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT);
74+
75+
Vector position = new Vector(-100.5, 200.0, 300.75);
76+
Vector delta = new Vector(0.0, 0.0, 0.0);
77+
float yRot = 180.0f;
78+
float xRot = 30.0f;
79+
80+
packet.getPositionMoveRotation().write(0,
81+
WrappedPositionMoveRotation.create(position, delta, yRot, xRot));
82+
83+
WrappedPositionMoveRotation result = packet.getPositionMoveRotation().read(0);
84+
85+
assertNotNull(result);
86+
87+
assertEquals(position.getX(), result.getPosition().getX(), 1e-6);
88+
assertEquals(position.getY(), result.getPosition().getY(), 1e-6);
89+
assertEquals(position.getZ(), result.getPosition().getZ(), 1e-6);
90+
91+
assertEquals(delta.getX(), result.getDeltaMovement().getX(), 1e-6);
92+
assertEquals(delta.getY(), result.getDeltaMovement().getY(), 1e-6);
93+
assertEquals(delta.getZ(), result.getDeltaMovement().getZ(), 1e-6);
94+
95+
assertEquals(yRot, result.getYRot(), 1e-6f);
96+
assertEquals(xRot, result.getXRot(), 1e-6f);
97+
}
98+
99+
@Test
100+
public void testFromHandle() {
101+
PositionMoveRotation nmsHandle = new PositionMoveRotation(
102+
new Vec3(5.0, 65.0, 5.0),
103+
new Vec3(0.0, -0.1, 0.0),
104+
270.0f,
105+
-45.0f);
106+
107+
WrappedPositionMoveRotation wrapper = WrappedPositionMoveRotation.fromHandle(nmsHandle);
108+
109+
assertEquals(5.0, wrapper.getPosition().getX(), 1e-6);
110+
assertEquals(65.0, wrapper.getPosition().getY(), 1e-6);
111+
assertEquals(5.0, wrapper.getPosition().getZ(), 1e-6);
112+
113+
assertEquals(0.0, wrapper.getDeltaMovement().getX(), 1e-6);
114+
assertEquals(-0.1, wrapper.getDeltaMovement().getY(), 1e-6);
115+
assertEquals(0.0, wrapper.getDeltaMovement().getZ(), 1e-6);
116+
117+
assertEquals(270.0f, wrapper.getYRot(), 1e-6f);
118+
assertEquals(-45.0f, wrapper.getXRot(), 1e-6f);
119+
}
120+
}

0 commit comments

Comments
 (0)