Fix data watchers and particles

This commit is contained in:
Dan Mulloy 2024-06-05 22:36:24 -05:00
parent 052e7a3dc0
commit f0468e04d9
No known key found for this signature in database
GPG Key ID: 3C5AD5D866D1539A
6 changed files with 195 additions and 49 deletions

View File

@ -26,6 +26,7 @@ import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain; import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/** /**
* Represents the type of a packet in a specific protocol. * Represents the type of a packet in a specific protocol.
* <p> * <p>
@ -477,8 +478,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x35, "SignUpdate", "UpdateSign", "CPacketUpdateSign"); public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x35, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x36, "Swing", "ArmAnimation", "CPacketAnimation"); public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x36, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x37, "TeleportToEntity", "Spectate", "CPacketSpectate"); public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x37, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn", "UseItem", "CPacketPlayerTryUseItemOnBlock"); public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x39, "BlockPlace", "CPacketPlayerTryUseItem"); public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x39, "UseItem", "BlockPlace", "CPacketPlayerTryUseItem");
/** /**
* @deprecated Removed in 1.17 * @deprecated Removed in 1.17

View File

@ -535,15 +535,15 @@ public class ProtocolLib extends JavaPlugin {
// that reloading the server might break ProtocolLib / plugins depending on it // that reloading the server might break ProtocolLib / plugins depending on it
if (Util.isCurrentlyReloading()) { if (Util.isCurrentlyReloading()) {
logger.severe("╔══════════════════════════════════════════════════════════════════╗"); highlyVisibleError(
logger.severe("║ WARNING ║"); " WARNING ",
logger.severe("║ RELOADING THE SERVER WHILE PROTOCOL LIB IS ENABLED MIGHT ║"); " RELOADING THE SERVER WHILE PROTOCOLLIB IS ENABLED MIGHT ",
logger.severe("║ LEAD TO UNEXPECTED ERRORS! ║"); " LEAD TO UNEXPECTED ERRORS! ",
logger.severe("║ ║"); " ",
logger.severe("║ Consider to cleanly restart your server if you encounter ║"); " Consider cleanly restarting your server if you encounter ",
logger.severe("║ any issues related to Protocol Lib before opening an issue ║"); " any issues related to ProtocolLib before opening an issue ",
logger.severe("║ on GitHub! ║"); " on GitHub! "
logger.severe("╚══════════════════════════════════════════════════════════════════╝"); );
} }
// Clean up // Clean up

View File

@ -19,6 +19,7 @@ package com.comphenix.protocol.utility;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -26,6 +27,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -115,6 +117,8 @@ public final class MinecraftReflection {
private static MethodAccessor asCraftMirror = null; private static MethodAccessor asCraftMirror = null;
private static MethodAccessor isEmpty = null; private static MethodAccessor isEmpty = null;
private static boolean isMojangMapped = false;
private MinecraftReflection() { private MinecraftReflection() {
// No need to make this constructable. // No need to make this constructable.
} }
@ -204,6 +208,9 @@ public final class MinecraftReflection {
} }
} }
// for now, we're going to say that it's Mojang mapped if the nms world was renamed to ServerLevel
isMojangMapped = getNmsWorldClass().getName().contains("ServerLevel");
return MINECRAFT_FULL_PACKAGE; return MINECRAFT_FULL_PACKAGE;
} catch (NoSuchMethodException exception) { } catch (NoSuchMethodException exception) {
throw new IllegalStateException("Cannot find getHandle() in CraftEntity", exception); throw new IllegalStateException("Cannot find getHandle() in CraftEntity", exception);
@ -1109,19 +1116,37 @@ public final class MinecraftReflection {
} }
} }
static Class<?> getOrInferMinecraftClass(String className, Supplier<Class<?>> supplier) {
return getOptionalNMS(className).orElseGet(() -> {
Class<?> clazz = supplier.get();
return setMinecraftClass(className, clazz);
});
}
/** /**
* Retrieves the entity use action class in 1.17. * Retrieves the entity use action class in 1.17.
* *
* @return The EntityUseAction class * @return The EntityUseAction class
*/ */
public static Class<?> getEnumEntityUseActionClass() { public static Class<?> getEnumEntityUseActionClass() {
Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass(); return getOrInferMinecraftClass("ServerboundInteractPacket.Action", () -> {
FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true); Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
try { FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
} catch (IllegalArgumentException ignored) { Field field = fuzzyReflection.getField(FuzzyFieldContract.newBuilder()
return fuzzyReflection.getFieldByType("^.*(Action)").getType(); .banModifier(Modifier.STATIC)
} .typeDerivedOf(Object.class)
.build());
if (field != null) {
return field.getType();
}
try {
return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
} catch (IllegalArgumentException ignored) {
return fuzzyReflection.getFieldByType("^.*(Action)").getType();
}
});
} }
/** /**
@ -1763,4 +1788,8 @@ public final class MinecraftReflection {
public static Optional<Class<?>> getRegistryFriendlyByteBufClass() { public static Optional<Class<?>> getRegistryFriendlyByteBufClass() {
return getOptionalNMS("network.RegistryFriendlyByteBuf"); return getOptionalNMS("network.RegistryFriendlyByteBuf");
} }
public static boolean isMojangMapped() {
return isMojangMapped;
}
} }

View File

@ -7,12 +7,15 @@ import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.ExactReflection; import com.comphenix.protocol.reflect.ExactReflection;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.MinecraftVersion;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -549,8 +552,14 @@ public abstract class EnumWrappers {
// In 1.17 the hand and use action class is no longer a field in the packet // In 1.17 the hand and use action class is no longer a field in the packet
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand", "world.InteractionHand"); HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand", "world.InteractionHand");
// class is named 'b' in the packet but class order differs in spigot and paper so we can only use the first method's return type (safest way)
ENTITY_USE_ACTION_CLASS = MinecraftReflection.getEnumEntityUseActionClass().getMethods()[0].getReturnType(); FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeMatches(FuzzyMatchers.except(Void.class))
.build());
ENTITY_USE_ACTION_CLASS = getType.getReturnType();
} else { } else {
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1); HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0); ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);

View File

@ -18,8 +18,11 @@ package com.comphenix.protocol.wrappers;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
@ -30,6 +33,7 @@ import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.collection.ConvertedMap; import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableBiMap;
@ -45,6 +49,11 @@ import org.bukkit.inventory.ItemStack;
*/ */
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject>, ClonableWrapper { public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject>, ClonableWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass(); private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
private static final boolean ARRAY_BACKED = MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove();
private static Class<?> SYNCHED_DATA_HOLDER_CLASS = ARRAY_BACKED
? MinecraftReflection.getMinecraftClass("network.syncher.SyncedDataHolder")
: MinecraftReflection.getEntityClass();
private static MethodAccessor GETTER = null; private static MethodAccessor GETTER = null;
private static MethodAccessor SETTER = null; private static MethodAccessor SETTER = null;
@ -53,6 +62,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
private static FieldAccessor ENTITY_DATA_FIELD = null; private static FieldAccessor ENTITY_DATA_FIELD = null;
private static FieldAccessor ENTITY_FIELD = null; private static FieldAccessor ENTITY_FIELD = null;
private static FieldAccessor MAP_FIELD = null; private static FieldAccessor MAP_FIELD = null;
private static FieldAccessor ARRAY_FIELD = null;
private static ConstructorAccessor constructor = null; private static ConstructorAccessor constructor = null;
private static ConstructorAccessor eggConstructor = null; private static ConstructorAccessor eggConstructor = null;
@ -78,8 +88,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* resulting DataWatcher will not have any keys or values and new ones will * resulting DataWatcher will not have any keys or values and new ones will
* have to be added using watcher objects. * have to be added using watcher objects.
*/ */
@Deprecated
public WrappedDataWatcher() { public WrappedDataWatcher() {
this(newHandle(fakeEntity())); this(new ArrayList<>());
} }
/** /**
@ -89,8 +100,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* *
* @param entity The entity * @param entity The entity
*/ */
@Deprecated
public WrappedDataWatcher(Entity entity) { public WrappedDataWatcher(Entity entity) {
this(newHandle(BukkitUnwrapper.getInstance().unwrapItem(entity))); this(getHandleFromEntity(entity));
} }
/** /**
@ -99,26 +111,51 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* *
* @param objects The list of objects * @param objects The list of objects
*/ */
@Deprecated
public WrappedDataWatcher(List<WrappedWatchableObject> objects) { public WrappedDataWatcher(List<WrappedWatchableObject> objects) {
this(); this(newHandle(fakeEntity(), objects));
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
}
} else {
for (WrappedWatchableObject object : objects) {
setObject(object.getIndex(), object);
}
}
} }
private static Object newHandle(Object entity) { private static final EquivalentConverter<List<WrappedWatchableObject>> ITEMS_CONVERTER = new EquivalentConverter<List<WrappedWatchableObject>>() {
if (constructor == null) { @Override
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass()); public Object getGeneric(List<WrappedWatchableObject> specific) {
Object[] generic = (Object[]) Array.newInstance(MinecraftReflection.getDataWatcherItemClass(), specific.size());
for (int i = 0; i < specific.size(); i++) {
generic[i] = specific.get(i).getHandle();
}
return generic;
} }
return constructor.invoke(entity); @Override
public List<WrappedWatchableObject> getSpecific(Object generic) {
Object[] genericArray = (Object[]) generic;
List<WrappedWatchableObject> specific = new ArrayList<>(genericArray.length);
for (Object genericObj : genericArray) {
specific.add(new WrappedWatchableObject(genericObj));
}
return specific;
}
@Override
@SuppressWarnings("unchecked")
public Class<List<WrappedWatchableObject>> getSpecificType() {
Class<?> dummy = List.class;
return (Class<List<WrappedWatchableObject>>) dummy;
}
};
public static WrappedDataWatcher create(Entity entity, List<WrappedWatchableObject> objects) {
return new WrappedDataWatcher(newHandle(entity, objects));
}
private static Object newHandle(Object entity, List<WrappedWatchableObject> objects) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE, SYNCHED_DATA_HOLDER_CLASS,
MinecraftReflection.getArrayClass(MinecraftReflection.getDataWatcherItemClass()));
}
Object[] genericItems = (Object[]) ITEMS_CONVERTER.getGeneric(objects);
return constructor.invoke(entity, genericItems);
} }
private static Object fakeEntity() { private static Object fakeEntity() {
@ -142,6 +179,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
// ---- Collection Methods // ---- Collection Methods
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Deprecated
private Map<Integer, Object> getMap() { private Map<Integer, Object> getMap() {
if (MAP_FIELD == null) { if (MAP_FIELD == null) {
try { try {
@ -152,19 +190,49 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
.typeDerivedOf(Map.class) .typeDerivedOf(Map.class)
.build())); .build()));
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
throw new FieldAccessException("Failed to find watchable object map"); throw new FieldAccessException("Failed to find watchable object map", ex);
} }
} }
return (Map<Integer, Object>) MAP_FIELD.get(handle); return (Map<Integer, Object>) MAP_FIELD.get(handle);
} }
private Object[] getBackingArray() {
if (ARRAY_FIELD == null) {
try {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
ARRAY_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
.newBuilder()
.banModifier(Modifier.STATIC)
.typeDerivedOf(Object[].class)
.build()));
} catch (IllegalArgumentException ex) {
throw new FieldAccessException("Failed to find watchable object array", ex);
}
}
Object[] backing = (Object[]) ARRAY_FIELD.get(handle);
return backing;
}
/** /**
* Gets the contents of this DataWatcher as a map. * Gets the contents of this DataWatcher as a map.
* @return The contents * @return The contents
*/ */
@Deprecated
public Map<Integer, WrappedWatchableObject> asMap() { public Map<Integer, WrappedWatchableObject> asMap() {
return new ConvertedMap<Integer, Object, WrappedWatchableObject>(getMap()) { Map<Integer, Object> backingMap;
if (ARRAY_BACKED) {
Object[] backingArray = getBackingArray();
backingMap = new HashMap<>(backingArray.length);
for (int i = 0; i < backingArray.length; i++) {
backingMap.put(i, backingArray[i]);
}
} else {
backingMap = getMap();
}
return new ConvertedMap<Integer, Object, WrappedWatchableObject>(backingMap) {
@Override @Override
protected WrappedWatchableObject toOuter(Object inner) { protected WrappedWatchableObject toOuter(Object inner) {
return inner != null ? new WrappedWatchableObject(inner) : null; return inner != null ? new WrappedWatchableObject(inner) : null;
@ -181,7 +249,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* Gets a set containing the registered indexes. * Gets a set containing the registered indexes.
* @return The set * @return The set
*/ */
@Deprecated
public Set<Integer> getIndexes() { public Set<Integer> getIndexes() {
if (ARRAY_BACKED) {
return IntStream.range(0, size()).boxed().collect(Collectors.toSet());
}
return getMap().keySet(); return getMap().keySet();
} }
@ -189,7 +262,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* Gets a list of the contents of this DataWatcher. * Gets a list of the contents of this DataWatcher.
* @return The contents * @return The contents
*/ */
@SuppressWarnings("unchecked")
public List<WrappedWatchableObject> getWatchableObjects() { public List<WrappedWatchableObject> getWatchableObjects() {
if (ARRAY_BACKED) {
return (List<WrappedWatchableObject>)ITEMS_CONVERTER.getSpecific(getBackingArray());
}
return new ArrayList<>(asMap().values()); return new ArrayList<>(asMap().values());
} }
@ -203,6 +281,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The size * @return The size
*/ */
public int size() { public int size() {
if (ARRAY_BACKED) {
return getBackingArray().length;
}
return getMap().size(); return getMap().size();
} }
@ -213,7 +295,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The watchable object, or null if none exists * @return The watchable object, or null if none exists
*/ */
public WrappedWatchableObject getWatchableObject(int index) { public WrappedWatchableObject getWatchableObject(int index) {
Object handle = getMap().get(index); Object handle = ARRAY_BACKED
? getBackingArray()[index]
: getMap().get(index);
if (handle != null) { if (handle != null) {
return new WrappedWatchableObject(handle); return new WrappedWatchableObject(handle);
} else { } else {
@ -247,6 +332,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return True if it does, false if not * @return True if it does, false if not
*/ */
public boolean hasIndex(int index) { public boolean hasIndex(int index) {
if (ARRAY_BACKED) {
return index < size();
}
return getMap().containsKey(index); return getMap().containsKey(index);
} }
@ -254,14 +343,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* Returns a set containing all the registered indexes * Returns a set containing all the registered indexes
* @return The set * @return The set
*/ */
@Deprecated
public Set<Integer> indexSet() { public Set<Integer> indexSet() {
return getMap().keySet(); return getIndexes();
} }
/** /**
* Clears the contents of this DataWatcher. The watcher will be empty after * Clears the contents of this DataWatcher. The watcher will be empty after
* this operation is called. * this operation is called.
*/ */
@Deprecated
public void clear() { public void clear() {
getMap().clear(); getMap().clear();
} }
@ -523,6 +614,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return A cloned data watcher. * @return A cloned data watcher.
*/ */
public WrappedDataWatcher deepClone() { public WrappedDataWatcher deepClone() {
if (ARRAY_BACKED) {
return new WrappedDataWatcher(newHandle(getEntityHandle(), getWatchableObjects()));
}
WrappedDataWatcher clone = new WrappedDataWatcher(getEntity()); WrappedDataWatcher clone = new WrappedDataWatcher(getEntity());
if (MinecraftReflection.watcherObjectExists()) { if (MinecraftReflection.watcherObjectExists()) {
@ -545,22 +640,23 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return Associated data watcher. * @return Associated data watcher.
*/ */
public static WrappedDataWatcher getEntityWatcher(Entity entity) { public static WrappedDataWatcher getEntityWatcher(Entity entity) {
Object handle = getHandleFromEntity(entity);
return handle != null ? new WrappedDataWatcher(handle) : null;
}
private static Object getHandleFromEntity(Entity entity) {
if (ENTITY_DATA_FIELD == null) { if (ENTITY_DATA_FIELD == null) {
ENTITY_DATA_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true); ENTITY_DATA_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
} }
BukkitUnwrapper unwrapper = new BukkitUnwrapper(); BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object handle = ENTITY_DATA_FIELD.get(unwrapper.unwrapItem(entity)); Object handle = ENTITY_DATA_FIELD.get(unwrapper.unwrapItem(entity));
return handle != null ? new WrappedDataWatcher(handle) : null; return handle;
} }
/** private Object getEntityHandle() {
* Retrieve the entity associated with this data watcher.
* @return The entity, or NULL.
*/
public Entity getEntity() {
if (ENTITY_FIELD == null) { if (ENTITY_FIELD == null) {
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true); ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, SYNCHED_DATA_HOLDER_CLASS, true);
} }
Object entity = ENTITY_FIELD.get(handle); Object entity = ENTITY_FIELD.get(handle);
@ -568,6 +664,15 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
throw new NullPointerException(handle + "." + ENTITY_FIELD); throw new NullPointerException(handle + "." + ENTITY_FIELD);
} }
return entity;
}
/**
* Retrieve the entity associated with this data watcher.
* @return The entity, or NULL.
*/
public Entity getEntity() {
Object entity = getEntityHandle();
return (Entity) MinecraftReflection.getBukkitEntity(entity); return (Entity) MinecraftReflection.getBukkitEntity(entity);
} }

View File

@ -36,7 +36,9 @@ public class WrappedParticle<T> {
.newBuilder() .newBuilder()
.requireModifier(Modifier.STATIC) .requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class) .returnTypeExact(Particle.class)
.parameterExactArray(MinecraftReflection.getParticleClass()) .parameterExactArray(MinecraftReflection.isMojangMapped()
? MinecraftReflection.getParticleTypeClass()
: MinecraftReflection.getParticleClass())
.build(); .build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));