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
15 changes: 15 additions & 0 deletions src/main/java/com/comphenix/protocol/events/AbstractStructure.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import com.comphenix.protocol.wrappers.WrappedMessageSignature;
import com.comphenix.protocol.wrappers.WrappedNumberFormat;
import com.comphenix.protocol.wrappers.WrappedParticle;
import com.comphenix.protocol.wrappers.WrappedPositionMoveRotation;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import com.comphenix.protocol.wrappers.WrappedRegistrable;
Expand Down Expand Up @@ -1297,6 +1298,20 @@ public StructureModifier<Iterable<PacketContainer>> getPacketBundles() {
));
}

/**
* Retrieve a read/write structure for {@link WrappedPositionMoveRotation} (available since Minecraft 1.21.2).
* <p>
* This is used in packets such as {@code ENTITY_TELEPORT} and {@code ENTITY_POSITION_SYNC}.
*
* @return A modifier for PositionMoveRotation fields.
*/
public StructureModifier<WrappedPositionMoveRotation> getPositionMoveRotation() {
return structureModifier.withType(
MinecraftReflection.getPositionMoveRotationClass(),
WrappedPositionMoveRotation.getConverter()
);
}

/**
* Represents an equivalent converter for ItemStack arrays.
* @author Kristian
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1903,4 +1903,13 @@ public static boolean isMojangMapped() {

return isMojangMapped;
}

/**
* Retrieves the PositionMoveRotation class (introduced in 1.21.2).
*
* @return The PositionMoveRotation class.
*/
public static Class<?> getPositionMoveRotationClass() {
return getMinecraftClass("world.entity.PositionMoveRotation");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package com.comphenix.protocol.wrappers;

import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;

import org.bukkit.util.Vector;

/**
* Wrapper around the NMS {@code PositionMoveRotation} record, introduced in Minecraft 1.21.2.
* <p>
* This type is used in packets such as {@code ENTITY_TELEPORT} and {@code ENTITY_POSITION_SYNC}
* to represent combined position, movement delta and rotation.
* <p>
* Example usage:
* <pre>{@code
* WrappedPositionMoveRotation posRot = WrappedPositionMoveRotation.create(
* new Vector(x, y, z), // position
* new Vector(0, 0, 0), // deltaMovement
* yaw, // yRot
* pitch // xRot
* );
* packet.getPositionMoveRotation().write(0, posRot);
* }</pre>
*/
public class WrappedPositionMoveRotation extends AbstractWrapper {

private static Class<?> NMS_CLASS;
private static ConstructorAccessor CONSTRUCTOR;
private static StructureModifier<Object> BASE_MODIFIER;

private final StructureModifier<Object> modifier;

private WrappedPositionMoveRotation(Object handle) {
super(getNmsClass());
setHandle(handle);
this.modifier = getBaseModifier().withTarget(handle);
}

private static Class<?> getNmsClass() {
if (NMS_CLASS == null) {
NMS_CLASS = MinecraftReflection.getPositionMoveRotationClass();
}
return NMS_CLASS;
}

private static StructureModifier<Object> getBaseModifier() {
if (BASE_MODIFIER == null) {
BASE_MODIFIER = new StructureModifier<>(getNmsClass());
}
return BASE_MODIFIER;
}

private static ConstructorAccessor getConstructor() {
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(
getNmsClass(),
MinecraftReflection.getVec3DClass(),
MinecraftReflection.getVec3DClass(),
float.class,
float.class
);
}
return CONSTRUCTOR;
}

/**
* Creates a new {@code WrappedPositionMoveRotation} from the given NMS handle.
*
* @param handle - the NMS {@code PositionMoveRotation} instance.
* @return the wrapper.
*/
public static WrappedPositionMoveRotation fromHandle(Object handle) {
return new WrappedPositionMoveRotation(handle);
}

/**
* Creates a new {@code WrappedPositionMoveRotation} with the given values.
*
* @param position - the absolute position.
* @param deltaMovement - the movement delta (velocity).
* @param yRot - the yaw rotation.
* @param xRot - the pitch rotation.
* @return a new wrapper instance.
*/
public static WrappedPositionMoveRotation create(Vector position, Vector deltaMovement, float yRot, float xRot) {
EquivalentConverter<Vector> conv = BukkitConverters.getVectorConverter();
Object handle = getConstructor().invoke(
conv.getGeneric(position),
conv.getGeneric(deltaMovement),
yRot,
xRot
);
return new WrappedPositionMoveRotation(handle);
}

/**
* Retrieves the absolute position component.
*
* @return the position as a Bukkit {@link Vector}.
*/
public Vector getPosition() {
return modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
}

/**
* Sets the absolute position component.
*
* @param position - the new position.
*/
public void setPosition(Vector position) {
modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).write(0, position);
}

/**
* Retrieves the movement delta (velocity) component.
*
* @return the delta movement as a Bukkit {@link Vector}.
*/
public Vector getDeltaMovement() {
return modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(1);
}

/**
* Sets the movement delta (velocity) component.
*
* @param deltaMovement - the new delta movement.
*/
public void setDeltaMovement(Vector deltaMovement) {
modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).write(1, deltaMovement);
}

/**
* Retrieves the yaw rotation (y-axis rotation).
*
* @return the yaw as a float.
*/
public float getYRot() {
StructureModifier<Float> floats = modifier.withType(float.class);
return floats.read(0);
}

/**
* Sets the yaw rotation (y-axis rotation).
*
* @param yRot - the new yaw value.
*/
public void setYRot(float yRot) {
modifier.withType(float.class).write(0, yRot);
}

/**
* Retrieves the pitch rotation (x-axis rotation).
*
* @return the pitch as a float.
*/
public float getXRot() {
StructureModifier<Float> floats = modifier.withType(float.class);
return floats.read(1);
}

/**
* Sets the pitch rotation (x-axis rotation).
*
* @param xRot - the new pitch value.
*/
public void setXRot(float xRot) {
modifier.withType(float.class).write(1, xRot);
}

/**
* Returns an {@link EquivalentConverter} that converts between {@code WrappedPositionMoveRotation}
* and the underlying NMS handle.
*
* @return the converter.
*/
public static EquivalentConverter<WrappedPositionMoveRotation> getConverter() {
return Converters.handle(AbstractWrapper::getHandle,
WrappedPositionMoveRotation::fromHandle, WrappedPositionMoveRotation.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.comphenix.protocol.wrappers;

import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;

import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
import org.bukkit.util.Vector;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class WrappedPositionMoveRotationTest {

@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}

@Test
public void testCreateAndRead() {
Vector position = new Vector(1.5, 64.0, -3.5);
Vector delta = new Vector(0.1, -0.05, 0.2);
float yRot = 45.0f;
float xRot = -10.0f;

WrappedPositionMoveRotation created = WrappedPositionMoveRotation.create(position, delta, yRot, xRot);

assertEquals(position.getX(), created.getPosition().getX(), 1e-6);
assertEquals(position.getY(), created.getPosition().getY(), 1e-6);
assertEquals(position.getZ(), created.getPosition().getZ(), 1e-6);

assertEquals(delta.getX(), created.getDeltaMovement().getX(), 1e-6);
assertEquals(delta.getY(), created.getDeltaMovement().getY(), 1e-6);
assertEquals(delta.getZ(), created.getDeltaMovement().getZ(), 1e-6);

assertEquals(yRot, created.getYRot(), 1e-6f);
assertEquals(xRot, created.getXRot(), 1e-6f);
}

@Test
public void testEntityPositionSyncPacket() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_POSITION_SYNC);

Vector position = new Vector(10.0, 70.0, -5.0);
Vector delta = new Vector(0.0, 0.0, 0.0);
float yRot = 90.0f;
float xRot = 0.0f;

packet.getPositionMoveRotation().write(0,
WrappedPositionMoveRotation.create(position, delta, yRot, xRot));

WrappedPositionMoveRotation result = packet.getPositionMoveRotation().read(0);

assertNotNull(result);

assertEquals(position.getX(), result.getPosition().getX(), 1e-6);
assertEquals(position.getY(), result.getPosition().getY(), 1e-6);
assertEquals(position.getZ(), result.getPosition().getZ(), 1e-6);

assertEquals(delta.getX(), result.getDeltaMovement().getX(), 1e-6);
assertEquals(delta.getY(), result.getDeltaMovement().getY(), 1e-6);
assertEquals(delta.getZ(), result.getDeltaMovement().getZ(), 1e-6);

assertEquals(yRot, result.getYRot(), 1e-6f);
assertEquals(xRot, result.getXRot(), 1e-6f);
}

@Test
public void testEntityTeleportPacket() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT);

Vector position = new Vector(-100.5, 200.0, 300.75);
Vector delta = new Vector(0.0, 0.0, 0.0);
float yRot = 180.0f;
float xRot = 30.0f;

packet.getPositionMoveRotation().write(0,
WrappedPositionMoveRotation.create(position, delta, yRot, xRot));

WrappedPositionMoveRotation result = packet.getPositionMoveRotation().read(0);

assertNotNull(result);

assertEquals(position.getX(), result.getPosition().getX(), 1e-6);
assertEquals(position.getY(), result.getPosition().getY(), 1e-6);
assertEquals(position.getZ(), result.getPosition().getZ(), 1e-6);

assertEquals(delta.getX(), result.getDeltaMovement().getX(), 1e-6);
assertEquals(delta.getY(), result.getDeltaMovement().getY(), 1e-6);
assertEquals(delta.getZ(), result.getDeltaMovement().getZ(), 1e-6);

assertEquals(yRot, result.getYRot(), 1e-6f);
assertEquals(xRot, result.getXRot(), 1e-6f);
}

@Test
public void testFromHandle() {
PositionMoveRotation nmsHandle = new PositionMoveRotation(
new Vec3(5.0, 65.0, 5.0),
new Vec3(0.0, -0.1, 0.0),
270.0f,
-45.0f);

WrappedPositionMoveRotation wrapper = WrappedPositionMoveRotation.fromHandle(nmsHandle);

assertEquals(5.0, wrapper.getPosition().getX(), 1e-6);
assertEquals(65.0, wrapper.getPosition().getY(), 1e-6);
assertEquals(5.0, wrapper.getPosition().getZ(), 1e-6);

assertEquals(0.0, wrapper.getDeltaMovement().getX(), 1e-6);
assertEquals(-0.1, wrapper.getDeltaMovement().getY(), 1e-6);
assertEquals(0.0, wrapper.getDeltaMovement().getZ(), 1e-6);

assertEquals(270.0f, wrapper.getYRot(), 1e-6f);
assertEquals(-45.0f, wrapper.getXRot(), 1e-6f);
}
}
Loading