mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 11:36:51 +01:00
Add an optional cloner, fix sound cloning, clean up the data watcher
This commit is contained in:
parent
7077037bb7
commit
5115bcaf98
@ -61,6 +61,7 @@ import com.comphenix.protocol.reflect.cloning.Cloner;
|
|||||||
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
|
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
|
||||||
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
import com.comphenix.protocol.reflect.cloning.FieldCloner;
|
||||||
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
|
||||||
|
import com.comphenix.protocol.reflect.cloning.OptionalCloner;
|
||||||
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
|
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||||
@ -125,6 +126,7 @@ public class PacketContainer implements Serializable {
|
|||||||
instanceProvider(DefaultInstances.DEFAULT).
|
instanceProvider(DefaultInstances.DEFAULT).
|
||||||
andThen(BukkitCloner.class).
|
andThen(BukkitCloner.class).
|
||||||
andThen(ImmutableDetector.class).
|
andThen(ImmutableDetector.class).
|
||||||
|
andThen(OptionalCloner.class).
|
||||||
andThen(CollectionCloner.class).
|
andThen(CollectionCloner.class).
|
||||||
andThen(getSpecializedDeepClonerFactory()).
|
andThen(getSpecializedDeepClonerFactory()).
|
||||||
build();
|
build();
|
||||||
|
@ -72,6 +72,7 @@ public class ImmutableDetector implements Cloner {
|
|||||||
// All primitive types
|
// All primitive types
|
||||||
if (Primitives.isWrapperType(type) || String.class.equals(type))
|
if (Primitives.isWrapperType(type) || String.class.equals(type))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// May not be true, but if so, that kind of code is broken anyways
|
// May not be true, but if so, that kind of code is broken anyways
|
||||||
if (isEnumWorkaround(type))
|
if (isEnumWorkaround(type))
|
||||||
return true;
|
return true;
|
||||||
@ -86,6 +87,15 @@ public class ImmutableDetector implements Cloner {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for known immutable classes in 1.9
|
||||||
|
if (MinecraftReflection.dataWatcherItemExists()) {
|
||||||
|
if (type.equals(MinecraftReflection.getDataWatcherSerializerClass())
|
||||||
|
|| type.equals(MinecraftReflection.getMinecraftClass("SoundEffect"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Probably not
|
// Probably not
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* (c) 2016 dmulloy2
|
||||||
|
*/
|
||||||
|
package com.comphenix.protocol.reflect.cloning;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cloner that can clone Optional objects
|
||||||
|
* @author dmulloy2
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class OptionalCloner implements Cloner {
|
||||||
|
protected Cloner wrapped;
|
||||||
|
|
||||||
|
public OptionalCloner(Cloner wrapped) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canClone(Object source) {
|
||||||
|
return source instanceof Optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object clone(Object source) {
|
||||||
|
Optional<?> optional = (Optional<?>) source;
|
||||||
|
if (!optional.isPresent()) {
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the inner value
|
||||||
|
return Optional.of(wrapped.clone(optional.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cloner getWrapped() {
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang.Validate;
|
import org.apache.commons.lang.Validate;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
@ -34,7 +34,6 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
|
|
||||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
@ -43,6 +42,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|||||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
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.wrappers.collection.ConvertedMap;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,12 +54,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
|
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
|
||||||
|
|
||||||
private static MethodAccessor GETTER = null;
|
private static MethodAccessor GETTER = null;
|
||||||
public static MethodAccessor SETTER = null;
|
private static MethodAccessor SETTER = null;
|
||||||
public static MethodAccessor REGISTER = null;
|
private static MethodAccessor REGISTER = 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 Field ENTITY_DATA_FIELD = null;
|
|
||||||
|
|
||||||
private static ConstructorAccessor constructor = null;
|
private static ConstructorAccessor constructor = null;
|
||||||
private static ConstructorAccessor lightningConstructor = null;
|
private static ConstructorAccessor lightningConstructor = null;
|
||||||
@ -117,7 +117,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
// ---- Collection Methods
|
// ---- Collection Methods
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Map<Integer, ?> getMap() {
|
private Map<Integer, Object> getMap() {
|
||||||
|
if (MAP_FIELD == null) {
|
||||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||||
List<Field> candidates = fuzzy.getFieldListByType(Map.class);
|
List<Field> candidates = fuzzy.getFieldListByType(Map.class);
|
||||||
|
|
||||||
@ -129,12 +130,13 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
MAP_FIELD = Accessors.getFieldAccessor(candidate);
|
MAP_FIELD = Accessors.getFieldAccessor(candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MAP_FIELD == null) {
|
|
||||||
throw new FieldAccessException("Could not find index -> object map.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Map<Integer, ?>) MAP_FIELD.get(handle);
|
if (MAP_FIELD == null) {
|
||||||
|
throw new FieldAccessException("Could not find index <-> Item map.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Map<Integer, Object>) MAP_FIELD.get(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,13 +144,25 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
* @return The contents
|
* @return The contents
|
||||||
*/
|
*/
|
||||||
public Map<Integer, WrappedWatchableObject> asMap() {
|
public Map<Integer, WrappedWatchableObject> asMap() {
|
||||||
Map<Integer, WrappedWatchableObject> ret = new HashMap<>();
|
return new ConvertedMap<Integer, Object, WrappedWatchableObject>(getMap()) {
|
||||||
|
@Override
|
||||||
for (Entry<Integer, ?> entry : getMap().entrySet()) {
|
protected WrappedWatchableObject toOuter(Object inner) {
|
||||||
ret.put(entry.getKey(), new WrappedWatchableObject(entry.getValue()));
|
return inner != null ? new WrappedWatchableObject(inner) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
@Override
|
||||||
|
protected Object toInner(WrappedWatchableObject outer) {
|
||||||
|
return outer != null ? outer.getHandle() : null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing the registered indexes.
|
||||||
|
* @return The set
|
||||||
|
*/
|
||||||
|
public Set<Integer> getIndexes() {
|
||||||
|
return getMap().keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,9 +187,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the watchable object at a given index.
|
* Gets the item at a given index.
|
||||||
*
|
*
|
||||||
* @param index Index
|
* @param index Index to get
|
||||||
* @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) {
|
||||||
@ -187,10 +201,21 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the item at a given index.
|
||||||
|
*
|
||||||
|
* @param index Index to remove
|
||||||
|
* @return The previous value, or null if none existed
|
||||||
|
*/
|
||||||
|
public WrappedWatchableObject remove(int index) {
|
||||||
|
Object removed = getMap().remove(index);
|
||||||
|
return removed != null ? new WrappedWatchableObject(removed) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not this DataWatcher has an object at a given index.
|
* Whether or not this DataWatcher has an object at a given index.
|
||||||
*
|
*
|
||||||
* @param index Index
|
* @param index Index to check for
|
||||||
* @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) {
|
||||||
@ -311,8 +336,11 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
*
|
*
|
||||||
* @param index Index
|
* @param index Index
|
||||||
* @param value New value
|
* @param value New value
|
||||||
|
* @deprecated Usage of this method is discouraged due to changes in 1.9.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setObject(int index, Object value) {
|
public void setObject(int index, Object value) {
|
||||||
|
Validate.isTrue(hasIndex(index), "You cannot register objects without the watcher object!");
|
||||||
setObject(new WrappedDataWatcherObject(index, null), value);
|
setObject(new WrappedDataWatcherObject(index, null), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +378,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Add support for setting the dirty state
|
// ---- Utility Methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clone the content of the current DataWatcher.
|
* Clone the content of the current DataWatcher.
|
||||||
@ -372,69 +400,39 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
*
|
*
|
||||||
* @param entity - the entity to read from.
|
* @param entity - the entity to read from.
|
||||||
* @return Associated data watcher.
|
* @return Associated data watcher.
|
||||||
* @throws FieldAccessException Reflection failed.
|
|
||||||
*/
|
*/
|
||||||
public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException {
|
public static WrappedDataWatcher getEntityWatcher(Entity entity) {
|
||||||
if (ENTITY_DATA_FIELD == null)
|
if (ENTITY_DATA_FIELD == null) {
|
||||||
ENTITY_DATA_FIELD = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true).getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass());
|
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));
|
||||||
try {
|
return handle != null ? new WrappedDataWatcher(handle) : null;
|
||||||
Object nsmWatcher = FieldUtils.readField(ENTITY_DATA_FIELD, unwrapper.unwrapItem(entity), true);
|
|
||||||
|
|
||||||
if (nsmWatcher != null)
|
|
||||||
return new WrappedDataWatcher(nsmWatcher);
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new FieldAccessException("Cannot access DataWatcher field.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the entity associated with this data watcher.
|
* Retrieve the entity associated with this data watcher.
|
||||||
* <p>
|
|
||||||
* <b>Warning:</b> This is only supported on 1.7.2 and above.
|
|
||||||
*
|
|
||||||
* @return The entity, or NULL.
|
* @return The entity, or NULL.
|
||||||
*/
|
*/
|
||||||
public Entity getEntity() {
|
public Entity getEntity() {
|
||||||
if (!MinecraftReflection.isUsingNetty())
|
|
||||||
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
|
||||||
|
|
||||||
if (ENTITY_FIELD == null) {
|
if (ENTITY_FIELD == null) {
|
||||||
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
return (Entity) MinecraftReflection.getBukkitEntity(ENTITY_FIELD.get(handle));
|
return (Entity) MinecraftReflection.getBukkitEntity(ENTITY_FIELD.get(handle));
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Unable to retrieve entity.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the entity associated with this data watcher.
|
* Set the entity associated with this data watcher.
|
||||||
* <p>
|
|
||||||
* <b>Warning:</b> This is only supported on 1.7.2 and above.
|
|
||||||
*
|
|
||||||
* @param entity - the new entity.
|
* @param entity - the new entity.
|
||||||
*/
|
*/
|
||||||
public void setEntity(Entity entity) {
|
public void setEntity(Entity entity) {
|
||||||
if (!MinecraftReflection.isUsingNetty())
|
|
||||||
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
|
||||||
|
|
||||||
if (ENTITY_FIELD == null) {
|
if (ENTITY_FIELD == null) {
|
||||||
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Unable to set entity.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -489,6 +487,13 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
return getWatchableObjects().hashCode();
|
return getWatchableObjects().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "WrappedDataWatcher[handle=" + handle + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 1.9 classes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a DataWatcherObject in 1.9.
|
* Represents a DataWatcherObject in 1.9.
|
||||||
*
|
*
|
||||||
@ -638,7 +643,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
* @return The serializer, or null if none exists
|
* @return The serializer, or null if none exists
|
||||||
*/
|
*/
|
||||||
public static Serializer get(Class<?> clazz) {
|
public static Serializer get(Class<?> clazz) {
|
||||||
|
Validate.notNull("Class cannot be null!");
|
||||||
initialize();
|
initialize();
|
||||||
|
|
||||||
return REGISTRY.get(clazz);
|
return REGISTRY.get(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,6 +655,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
* @return The serializer, or null if none exists
|
* @return The serializer, or null if none exists
|
||||||
*/
|
*/
|
||||||
public static Serializer fromHandle(Object handle) {
|
public static Serializer fromHandle(Object handle) {
|
||||||
|
Validate.notNull("Handle cannot be null!");
|
||||||
initialize();
|
initialize();
|
||||||
|
|
||||||
for (Serializer serializer : REGISTRY.values()) {
|
for (Serializer serializer : REGISTRY.values()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user