Skip to content
Open
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
73 changes: 73 additions & 0 deletions src/main/java/com/comphenix/protocol/events/AbstractStructure.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException;
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 com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.StreamSerializer;
Expand Down Expand Up @@ -664,6 +667,76 @@ public StructureModifier<EnumWrappers.EntityUseAction> getEntityUseActions() {
* @return A modifier for EntityUseAction class fields.
*/
public StructureModifier<WrappedEnumEntityUseAction> getEnumEntityUseActions() {
if (MinecraftVersion.v26_1.atOrAbove()) {
final Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
final ConstructorAccessor constructor = Accessors.getConstructorAccessor(
packetClass,
int.class,
EnumWrappers.getHandClass(),
MinecraftReflection.getVec3DClass(),
boolean.class
);

return new StructureModifier<WrappedEnumEntityUseAction>() {
@Override
public WrappedEnumEntityUseAction read(int fieldIndex) {
if (fieldIndex != 0) {
throw FieldAccessException.fromFormat(
"Field index %d is out of bounds for length %s",
fieldIndex,
1);
}

return WrappedEnumEntityUseAction.fromHandle(handle);
}

@Override
public WrappedEnumEntityUseAction readSafely(int fieldIndex) {
return fieldIndex == 0 && handle != null ? WrappedEnumEntityUseAction.fromHandle(handle) : null;
}

@Override
public StructureModifier<WrappedEnumEntityUseAction> write(int fieldIndex, WrappedEnumEntityUseAction value) {
if (fieldIndex != 0) {
throw FieldAccessException.fromFormat(
"Field index %d is out of bounds for length %s",
fieldIndex,
1);
}

if (value == null) {
return this;
}

if (handle == null) {
throw new IllegalStateException("Cannot write USE_ENTITY action without a packet handle");
}

StructureModifier<Object> current = new StructureModifier<>(packetClass).withTarget(handle);
int entityId = (Integer) current.withType(int.class).read(0);
boolean usingSecondaryAction = (Boolean) current.withType(boolean.class).read(0);

Object genericHand = EnumWrappers.getHandConverter().getGeneric(value.getHand());
Object genericVector = BukkitConverters.getVectorConverter().getGeneric(value.getPosition());

Object newHandle = constructor.invoke(entityId, genericHand, genericVector, usingSecondaryAction);
AbstractStructure.this.handle = newHandle;
AbstractStructure.this.structureModifier = AbstractStructure.this.structureModifier.withTarget(newHandle);
return this;
}

@Override
public StructureModifier<WrappedEnumEntityUseAction> writeSafely(int fieldIndex, WrappedEnumEntityUseAction value) {
return write(fieldIndex, value);
}

@Override
protected com.comphenix.protocol.reflect.accessors.FieldAccessor findFieldAccessor(int fieldIndex) {
return null;
}
};
}

return structureModifier.withType(
MinecraftReflection.getEnumEntityUseActionClass(),
WrappedEnumEntityUseAction.CONVERTER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,10 @@ static Class<?> getOrInferMinecraftClass(String className, Supplier<Class<?>> su
* @return The EntityUseAction class
*/
public static Class<?> getEnumEntityUseActionClass() {
if (MinecraftVersion.v26_1.atOrAbove()) {
return PacketType.Play.Client.USE_ENTITY.getPacketClass();
}

return getOrInferMinecraftClass("ServerboundInteractPacket.Action", () -> {
Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
Expand All @@ -1255,10 +1259,18 @@ public static Class<?> getEnumEntityUseActionClass() {
* @return a method accessor to get the actual use action
*/
public static MethodAccessor getEntityUseActionEnumMethodAccessor() {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(EnumWrappers.getEntityUseActionClass())
.build()));
if (MinecraftVersion.v26_1.atOrAbove()) {
return null;
}

try {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(EnumWrappers.getEntityUseActionClass())
.build()));
} catch (IllegalArgumentException ex) {
return null;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
Expand All @@ -30,19 +31,21 @@ public class WrappedEnumEntityUseAction extends AbstractWrapper implements Clona
private static final Class<?>[] DECLARED_CLASSES = PACKET_CLASS.getDeclaredClasses();

private static final Class<?> HANDLE_TYPE = MinecraftReflection.getEnumEntityUseActionClass();
private static final MethodAccessor ACTION_USE = MinecraftReflection.getEntityUseActionEnumMethodAccessor();

private static final ConstructorAccessor INTERACT = useAction(EnumWrappers.getHandClass());
private static final ConstructorAccessor INTERACT_AT = useAction(EnumWrappers.getHandClass(),
MinecraftReflection.getVec3DClass());

private static final Object ATTACK = Accessors.getFieldAccessor(FuzzyReflection.fromClass(PACKET_CLASS, true)
.getField(FuzzyFieldContract.newBuilder()
.requireModifier(Modifier.STATIC)
.typeExact(MinecraftReflection.getEnumEntityUseActionClass())
.build())
).get(null);
private static final WrappedEnumEntityUseAction ATTACK_WRAPPER = new WrappedEnumEntityUseAction(ATTACK);
private static final MethodAccessor ACTION_USE = getActionUseAccessor();

private static final ConstructorAccessor PACKET_CONSTRUCTOR = MinecraftVersion.v26_1.atOrAbove()
? Accessors.getConstructorAccessor(PACKET_CLASS, int.class, EnumWrappers.getHandClass(),
MinecraftReflection.getVec3DClass(), boolean.class)
: null;
private static final ConstructorAccessor INTERACT = MinecraftVersion.v26_1.atOrAbove()
? null
: useAction(EnumWrappers.getHandClass());
private static final ConstructorAccessor INTERACT_AT = MinecraftVersion.v26_1.atOrAbove()
? null
: useAction(EnumWrappers.getHandClass(), MinecraftReflection.getVec3DClass());

private static final Object ATTACK = initializeAttack();
private static final WrappedEnumEntityUseAction ATTACK_WRAPPER = ATTACK != null ? new WrappedEnumEntityUseAction(ATTACK) : null;

private final EntityUseAction action;
// these fields are only available for interact & interact_at
Expand All @@ -57,7 +60,32 @@ private WrappedEnumEntityUseAction(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);

action = EnumWrappers.getEntityUseActionConverter().getSpecific(ACTION_USE.invoke(handle));
action = resolveAction();
}

private static MethodAccessor getActionUseAccessor() {
return MinecraftReflection.getEntityUseActionEnumMethodAccessor();
}

private static Object initializeAttack() {
if (MinecraftVersion.v26_1.atOrAbove()) {
return null;
}

return Accessors.getFieldAccessor(FuzzyReflection.fromClass(PACKET_CLASS, true)
.getField(FuzzyFieldContract.newBuilder()
.requireModifier(Modifier.STATIC)
.typeExact(MinecraftReflection.getEnumEntityUseActionClass())
.build())
).get(null);
}

private EntityUseAction resolveAction() {
if (ACTION_USE != null) {
return EnumWrappers.getEntityUseActionConverter().getSpecific(ACTION_USE.invoke(handle));
}

return EntityUseAction.INTERACT_AT;
}

/**
Expand Down Expand Up @@ -91,6 +119,10 @@ public static WrappedEnumEntityUseAction fromHandle(Object handle) {
* @return the action for an entity attack.
*/
public static WrappedEnumEntityUseAction attack() {
if (ATTACK_WRAPPER == null) {
throw new UnsupportedOperationException("Attack is no longer part of the USE_ENTITY packet on 26.1+");
}

return ATTACK_WRAPPER;
}

Expand All @@ -100,7 +132,10 @@ public static WrappedEnumEntityUseAction attack() {
* @return the action for an interact.
*/
public static WrappedEnumEntityUseAction interact(Hand hand) {
Object handle = INTERACT.invoke(EnumWrappers.getHandConverter().getGeneric(hand));
Object handle = MinecraftVersion.v26_1.atOrAbove()
? PACKET_CONSTRUCTOR.invoke(0, EnumWrappers.getHandConverter().getGeneric(hand),
BukkitConverters.getVectorConverter().getGeneric(new Vector()), false)
: INTERACT.invoke(EnumWrappers.getHandConverter().getGeneric(hand));
return new WrappedEnumEntityUseAction(handle);
}

Expand All @@ -111,8 +146,11 @@ public static WrappedEnumEntityUseAction interact(Hand hand) {
* @return the action for an interact_at.
*/
public static WrappedEnumEntityUseAction interactAt(Hand hand, Vector vector) {
Object handle = INTERACT_AT.invoke(EnumWrappers.getHandConverter().getGeneric(hand),
BukkitConverters.getVectorConverter().getGeneric(vector));
Object handle = MinecraftVersion.v26_1.atOrAbove()
? PACKET_CONSTRUCTOR.invoke(0, EnumWrappers.getHandConverter().getGeneric(hand),
BukkitConverters.getVectorConverter().getGeneric(vector), false)
: INTERACT_AT.invoke(EnumWrappers.getHandConverter().getGeneric(hand),
BukkitConverters.getVectorConverter().getGeneric(vector));
return new WrappedEnumEntityUseAction(handle);
}

Expand Down Expand Up @@ -148,7 +186,7 @@ public void setHand(Hand hand) {
* @throws IllegalArgumentException if called for attack or interact.
*/
public Vector getPosition() {
return BukkitConverters.getVectorConverter().getSpecific(getPositionAccessor().get(handle));
return getRawPosition(handle);
}

/**
Expand All @@ -162,15 +200,15 @@ public void setPosition(Vector position) {

@Override
public WrappedEnumEntityUseAction deepClone() {
switch (action) {
switch (getAction()) {
case ATTACK:
return WrappedEnumEntityUseAction.attack();
case INTERACT:
return WrappedEnumEntityUseAction.interact(getHand());
case INTERACT_AT:
return WrappedEnumEntityUseAction.interactAt(getHand(), getPosition());
default:
throw new IllegalArgumentException("Invalid EntityUseAction: " + action);
throw new IllegalArgumentException("Invalid EntityUseAction: " + getAction());
}
}

Expand All @@ -197,4 +235,8 @@ public FieldAccessor getPositionAccessor() {
}
return positionAccessor;
}

private Vector getRawPosition(Object target) {
return BukkitConverters.getVectorConverter().getSpecific(getPositionAccessor().get(target));
}
}
101 changes: 62 additions & 39 deletions src/test/java/com/comphenix/protocol/events/PacketContainerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
Expand Down Expand Up @@ -753,50 +754,72 @@ public void testGameStateChange() {
assertEquals(2, (int) packet.getGameStateIDs().read(0));
}

// @Test
// TODO: needs to be fixed for 26.1
@Test
public void testUseEntity() {
PacketContainer packet = new PacketContainer(PacketType.Play.Client.USE_ENTITY);

WrappedEnumEntityUseAction action;
WrappedEnumEntityUseAction clone;
// test attack
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.attack());
action = packet.getEnumEntityUseActions().read(0);
// attack's handle should always be the same
assertEquals(WrappedEnumEntityUseAction.attack(), action);
assertEquals(EntityUseAction.ATTACK, action.getAction());
// hand & position should not be available
assertThrows(IllegalArgumentException.class, action::getHand);
assertThrows(IllegalArgumentException.class, action::getPosition);
// test cloning
clone = action.deepClone();
assertSame(WrappedEnumEntityUseAction.attack(), clone);

// test interact
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interact(Hand.OFF_HAND));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT, action.getAction());
assertEquals(Hand.OFF_HAND, action.getHand());
// position should not be available
assertThrows(IllegalArgumentException.class, action::getPosition);
// test cloning
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT, clone.getAction());
assertEquals(Hand.OFF_HAND, clone.getHand());

// test interact_at
Vector position = new Vector(1, 199, 4);
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interactAt(Hand.MAIN_HAND, position));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT_AT, action.getAction());
assertEquals(Hand.MAIN_HAND, action.getHand());
assertEquals(position, action.getPosition());
// test cloning
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT_AT, clone.getAction());
assertEquals(Hand.MAIN_HAND, clone.getHand());
assertEquals(position, clone.getPosition());
if (MinecraftVersion.v26_1.atOrAbove()) {
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interact(Hand.OFF_HAND));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT_AT, action.getAction());
assertEquals(Hand.OFF_HAND, action.getHand());
assertEquals(new Vector(), action.getPosition());
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT_AT, clone.getAction());
assertEquals(Hand.OFF_HAND, clone.getHand());
assertEquals(new Vector(), clone.getPosition());

Vector position = new Vector(1, 199, 4);
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interactAt(Hand.MAIN_HAND, position));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT_AT, action.getAction());
assertEquals(Hand.MAIN_HAND, action.getHand());
assertEquals(position, action.getPosition());
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT_AT, clone.getAction());
assertEquals(Hand.MAIN_HAND, clone.getHand());
assertEquals(position, clone.getPosition());
} else {
// test attack
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.attack());
action = packet.getEnumEntityUseActions().read(0);
// attack's handle should always be the same
assertEquals(WrappedEnumEntityUseAction.attack(), action);
assertEquals(EntityUseAction.ATTACK, action.getAction());
// hand & position should not be available
assertThrows(IllegalArgumentException.class, action::getHand);
assertThrows(IllegalArgumentException.class, action::getPosition);
// test cloning
clone = action.deepClone();
assertSame(WrappedEnumEntityUseAction.attack(), clone);

// test interact
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interact(Hand.OFF_HAND));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT, action.getAction());
assertEquals(Hand.OFF_HAND, action.getHand());
// position should not be available
assertThrows(IllegalArgumentException.class, action::getPosition);
// test cloning
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT, clone.getAction());
assertEquals(Hand.OFF_HAND, clone.getHand());

// test interact_at
Vector position = new Vector(1, 199, 4);
packet.getEnumEntityUseActions().write(0, WrappedEnumEntityUseAction.interactAt(Hand.MAIN_HAND, position));
action = packet.getEnumEntityUseActions().read(0);
assertEquals(EntityUseAction.INTERACT_AT, action.getAction());
assertEquals(Hand.MAIN_HAND, action.getHand());
assertEquals(position, action.getPosition());
// test cloning
clone = action.deepClone();
assertEquals(EntityUseAction.INTERACT_AT, clone.getAction());
assertEquals(Hand.MAIN_HAND, clone.getHand());
assertEquals(position, clone.getPosition());
}
}

@Test
Expand Down