ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedEnumEntityUseAction....

201 lines
7.2 KiB
Java

package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
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.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import org.bukkit.util.Vector;
/**
* Represents an entity used action used in the UseEntity packet sent by the client.
* @author derklaro
*/
public class WrappedEnumEntityUseAction extends AbstractWrapper implements ClonableWrapper {
public static final EquivalentConverter<WrappedEnumEntityUseAction> CONVERTER = Converters.handle(AbstractWrapper::getHandle,
WrappedEnumEntityUseAction::fromHandle, WrappedEnumEntityUseAction.class);
private static final Class<?> PACKET_CLASS = PacketType.Play.Client.USE_ENTITY.getPacketClass();
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 final EntityUseAction action;
// these fields are only available for interact & interact_at
private FieldAccessor handAccessor;
private FieldAccessor positionAccessor;
/**
* Construct a new wrapper for the entity use action class in the UseEntity packet.
* @param handle - the NMS handle.
*/
private WrappedEnumEntityUseAction(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);
action = EnumWrappers.getEntityUseActionConverter().getSpecific(ACTION_USE.invoke(handle));
}
/**
* Finds a constructor of a declared class in the UseEntity class. Used to find the action class implementations.
* @param parameterTypes - the types the constructor of the class must have.
* @return a constructor for a matching class.
* @throws IllegalArgumentException if no constructor was found.
*/
private static ConstructorAccessor useAction(Class<?>... parameterTypes) {
for (Class<?> subClass : DECLARED_CLASSES) {
ConstructorAccessor accessor = Accessors.getConstructorAccessorOrNull(subClass, parameterTypes);
if (accessor != null) {
return accessor;
}
}
throw new IllegalArgumentException(
"No constructor with " + Arrays.toString(parameterTypes) + " in " + PACKET_CLASS);
}
/**
* Construct a new wrapper for the entity use action class in the UseEntity packet.
* @param handle - the NMS handle.
* @return the created wrapper.
*/
public static WrappedEnumEntityUseAction fromHandle(Object handle) {
return new WrappedEnumEntityUseAction(handle);
}
/**
* Get the jvm static action for attacking an entity.
* @return the action for an entity attack.
*/
public static WrappedEnumEntityUseAction attack() {
return ATTACK_WRAPPER;
}
/**
* Get an action for interacting with an entity.
* @param hand - the hand used for the interact.
* @return the action for an interact.
*/
public static WrappedEnumEntityUseAction interact(Hand hand) {
Object handle = INTERACT.invoke(EnumWrappers.getHandConverter().getGeneric(hand));
return new WrappedEnumEntityUseAction(handle);
}
/**
* Get an action for interacting with an entity at a specific location.
* @param hand - the hand used for the interact.
* @param vector - the position of the interact.
* @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));
return new WrappedEnumEntityUseAction(handle);
}
/**
* Get the action used for the interact.
* @return the interact action.
*/
public EntityUseAction getAction() {
return action;
}
/**
* Get the hand used for the interact. Only available if this represents interact or interact_at.
* @return the hand used for the interact.
* @throws IllegalArgumentException if called for attack.
*/
public Hand getHand() {
return EnumWrappers.getHandConverter().getSpecific(getHandAccessor().get(handle));
}
/**
* Sets the hand used for the interact.
* @param hand the used hand.
* @throws IllegalArgumentException if called for attack.
*/
public void setHand(Hand hand) {
getHandAccessor().set(handle, EnumWrappers.getHandConverter().getGeneric(hand));
}
/**
* Get the position of the interact. Only available if this represents interact_at.
* @return the position of the interact.
* @throws IllegalArgumentException if called for attack or interact.
*/
public Vector getPosition() {
return BukkitConverters.getVectorConverter().getSpecific(getPositionAccessor().get(handle));
}
/**
* Sets the position of the interact.
* @param position the position.
* @throws IllegalArgumentException if called for attack or interact.
*/
public void setPosition(Vector position) {
getPositionAccessor().set(handle, BukkitConverters.getVectorConverter().getGeneric(position));
}
@Override
public WrappedEnumEntityUseAction deepClone() {
switch (action) {
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);
}
}
/**
* Get a field accessor for the hand in the interact and interact_at type.
* @return a field accessor for the hand field.
* @throws IllegalArgumentException if called for attack.
*/
private FieldAccessor getHandAccessor() {
if (handAccessor == null) {
handAccessor = MinecraftReflection.getHandEntityUseActionEnumFieldAccessor(handle);
}
return handAccessor;
}
/**
* Get a field accessor for the position in the interact_at type.
* @return a field accessor for the position field.
* @throws IllegalArgumentException if called for attack or interact.
*/
public FieldAccessor getPositionAccessor() {
if (positionAccessor == null) {
positionAccessor = MinecraftReflection.getVec3EntityUseActionEnumFieldAccessor(handle);
}
return positionAccessor;
}
}