Ursprung
916434251d
Commit
dd9eac3d6d
@ -82,6 +82,7 @@ public class BukkitCloner implements Cloner {
|
||||
fromWrapper(MinecraftReflection::getIChatBaseComponentClass, WrappedChatComponent::fromHandle);
|
||||
fromManual(ComponentConverter::getBaseComponentArrayClass, source ->
|
||||
ComponentConverter.clone((BaseComponent[]) source));
|
||||
fromWrapper(WrappedVillagerData::getNmsClass, WrappedVillagerData::fromHandle);
|
||||
}
|
||||
|
||||
private Function<Object, Object> findCloner(Class<?> type) {
|
||||
|
@ -60,19 +60,24 @@ public class ImmutableDetector implements Cloner {
|
||||
static {
|
||||
add(MinecraftReflection::getGameProfileClass);
|
||||
add(MinecraftReflection::getDataWatcherSerializerClass);
|
||||
add(() -> MinecraftReflection.getMinecraftClass("SoundEffect"));
|
||||
add(MinecraftReflection::getBlockClass);
|
||||
add(MinecraftReflection::getItemClass);
|
||||
add("SoundEffect");
|
||||
|
||||
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
|
||||
add(() -> MinecraftReflection.getMinecraftClass("Particle"));
|
||||
add(MinecraftReflection::getFluidTypeClass);
|
||||
add(MinecraftReflection::getParticleTypeClass);
|
||||
add("Particle");
|
||||
}
|
||||
|
||||
if (MinecraftVersion.atOrAbove(MinecraftVersion.VILLAGE_UPDATE)) {
|
||||
add(() -> MinecraftReflection.getMinecraftClass("EntityTypes"));
|
||||
add("EntityTypes");
|
||||
add("VillagerType");
|
||||
add("VillagerProfession");
|
||||
}
|
||||
|
||||
// TODO automatically detect the technically-not-an-enum enums that Mojang is so fond of
|
||||
// Would also probably go in tandem with having the FieldCloner use this
|
||||
}
|
||||
|
||||
private static void add(Supplier<Class<?>> getClass) {
|
||||
@ -84,6 +89,15 @@ public class ImmutableDetector implements Cloner {
|
||||
} catch (RuntimeException ignored) { }
|
||||
}
|
||||
|
||||
private static void add(String className) {
|
||||
try {
|
||||
Class<?> clazz = MinecraftReflection.getMinecraftClass(className);
|
||||
if (clazz != null) {
|
||||
immutableNMS.add(clazz);
|
||||
}
|
||||
} catch (RuntimeException ignored) { }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClone(Object source) {
|
||||
// Don't accept NULL
|
||||
|
@ -1,19 +1,22 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a generic enum converter.
|
||||
* @author Kristian
|
||||
@ -693,4 +696,98 @@ public abstract class EnumWrappers {
|
||||
this.genericType = genericType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for classes where it's an enum in everything but name
|
||||
* @param <T> Generic type
|
||||
*/
|
||||
public static class FauxEnumConverter<T extends Enum<T>> implements EquivalentConverter<T> {
|
||||
private final Class<T> specificClass;
|
||||
private final Class<?> genericClass;
|
||||
private final Map<Object, T> lookup;
|
||||
|
||||
public FauxEnumConverter(Class<T> specific, Class<?> generic) {
|
||||
Validate.notNull(specific,"specific class cannot be null");
|
||||
Validate.notNull(generic,"generic class cannot be null");
|
||||
|
||||
this.specificClass = specific;
|
||||
this.genericClass = generic;
|
||||
this.lookup = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
Validate.notNull(specific, "specific object cannot be null");
|
||||
|
||||
return Accessors
|
||||
.getFieldAccessor(genericClass, specific
|
||||
.name(), false)
|
||||
.get(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
Validate.notNull(generic, "generic object cannot be null");
|
||||
|
||||
return lookup.computeIfAbsent(generic, x -> {
|
||||
for (Field field : genericClass.getFields()) {
|
||||
try {
|
||||
if (!field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
|
||||
if (field.get(null) == generic) {
|
||||
return Enum.valueOf(specificClass, field.getName().toUpperCase());
|
||||
}
|
||||
} catch (ReflectiveOperationException ignored) { }
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Could not find ProtocolLib wrapper for " + generic);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return specificClass;
|
||||
}
|
||||
}
|
||||
|
||||
public static class IndexedEnumConverter<T extends Enum<T>> implements EquivalentConverter<T> {
|
||||
private Class<T> specificClass;
|
||||
private Class<?> genericClass;
|
||||
|
||||
public IndexedEnumConverter(Class<T> specificClass, Class<?> genericClass) {
|
||||
this.specificClass = specificClass;
|
||||
this.genericClass = genericClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneric(T specific) {
|
||||
int ordinal = specific.ordinal();
|
||||
for (Object elem : genericClass.getEnumConstants()) {
|
||||
if (((Enum<?>) elem).ordinal() == ordinal) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
int ordinal = ((Enum<?>) generic).ordinal();
|
||||
for (T elem : specificClass.getEnumConstants()) {
|
||||
if (elem.ordinal() == ordinal) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getSpecificType() {
|
||||
return specificClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
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.utility.MinecraftReflection;
|
||||
|
||||
public class WrappedVillagerData extends AbstractWrapper implements ClonableWrapper {
|
||||
private static final Class<?> NMS_CLASS = MinecraftReflection.getMinecraftClass("VillagerData");
|
||||
private static final Class<?> TYPE_CLASS = MinecraftReflection.getMinecraftClass("VillagerType");
|
||||
private static final Class<?> PROF_CLASS = MinecraftReflection.getMinecraftClass("VillagerProfession");
|
||||
|
||||
private static final EquivalentConverter<Type> TYPE_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Type.class, TYPE_CLASS);
|
||||
private static final EquivalentConverter<Profession> PROF_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Profession.class, PROF_CLASS);
|
||||
|
||||
public enum Type {
|
||||
DESERT, JUNGLE, PLAINS, SAVANNA, SNOW, SWAMP, TAIGA;
|
||||
}
|
||||
|
||||
public enum Profession {
|
||||
NONE, ARMORER, BUTCHER, CARTOGRAPHER, CLERIC, FARMER, FISHERMAN,
|
||||
FLETCHER, LEATHERWORKER, LIBRARIAN, MASON, NITWIT, SHEPHERD,
|
||||
TOOLSMITH, WEAPONSMITH;
|
||||
}
|
||||
|
||||
private StructureModifier<Object> modifier;
|
||||
|
||||
private WrappedVillagerData(Object handle) {
|
||||
super(NMS_CLASS);
|
||||
setHandle(handle);
|
||||
|
||||
modifier = new StructureModifier<>(NMS_CLASS).withTarget(handle);
|
||||
}
|
||||
|
||||
public static WrappedVillagerData fromHandle(Object handle) {
|
||||
return new WrappedVillagerData(handle);
|
||||
}
|
||||
|
||||
public static WrappedVillagerData fromValues(Type type, Profession profession, int level) {
|
||||
Object genericType = TYPE_CONVERTER.getGeneric(type);
|
||||
Object genericProf = PROF_CONVERTER.getGeneric(profession);
|
||||
|
||||
Object handle = Accessors.getConstructorAccessor(NMS_CLASS, TYPE_CLASS, PROF_CLASS, int.class)
|
||||
.invoke(genericType, genericProf, level);
|
||||
return fromHandle(handle);
|
||||
}
|
||||
|
||||
public static Class<?> getNmsClass() {
|
||||
return NMS_CLASS;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return modifier.<Integer>withType(int.class).read(0);
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return modifier.withType(TYPE_CLASS, TYPE_CONVERTER).read(0);
|
||||
}
|
||||
|
||||
public Profession getProfession() {
|
||||
return modifier.withType(PROF_CLASS, PROF_CONVERTER).read(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedVillagerData deepClone() {
|
||||
return WrappedVillagerData.fromValues(getType(), getProfession(), getLevel());
|
||||
}
|
||||
}
|
@ -535,7 +535,9 @@ public class PacketContainerTest {
|
||||
"String"),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.getChatComponentSerializer(true)),
|
||||
com.google.common.base.Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle()))
|
||||
com.google.common.base.Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle())),
|
||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(VillagerData.class)),
|
||||
new VillagerData(VillagerType.SNOW, VillagerProfession.ARMORER, 69))
|
||||
));
|
||||
} else if (type == PacketType.Play.Server.CHAT) {
|
||||
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren