mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2025-02-14 03:21:58 +01:00
parent
916434251d
commit
dd9eac3d6d
@ -82,6 +82,7 @@ public class BukkitCloner implements Cloner {
|
|||||||
fromWrapper(MinecraftReflection::getIChatBaseComponentClass, WrappedChatComponent::fromHandle);
|
fromWrapper(MinecraftReflection::getIChatBaseComponentClass, WrappedChatComponent::fromHandle);
|
||||||
fromManual(ComponentConverter::getBaseComponentArrayClass, source ->
|
fromManual(ComponentConverter::getBaseComponentArrayClass, source ->
|
||||||
ComponentConverter.clone((BaseComponent[]) source));
|
ComponentConverter.clone((BaseComponent[]) source));
|
||||||
|
fromWrapper(WrappedVillagerData::getNmsClass, WrappedVillagerData::fromHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function<Object, Object> findCloner(Class<?> type) {
|
private Function<Object, Object> findCloner(Class<?> type) {
|
||||||
|
@ -60,19 +60,24 @@ public class ImmutableDetector implements Cloner {
|
|||||||
static {
|
static {
|
||||||
add(MinecraftReflection::getGameProfileClass);
|
add(MinecraftReflection::getGameProfileClass);
|
||||||
add(MinecraftReflection::getDataWatcherSerializerClass);
|
add(MinecraftReflection::getDataWatcherSerializerClass);
|
||||||
add(() -> MinecraftReflection.getMinecraftClass("SoundEffect"));
|
|
||||||
add(MinecraftReflection::getBlockClass);
|
add(MinecraftReflection::getBlockClass);
|
||||||
add(MinecraftReflection::getItemClass);
|
add(MinecraftReflection::getItemClass);
|
||||||
|
add("SoundEffect");
|
||||||
|
|
||||||
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
|
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
|
||||||
add(() -> MinecraftReflection.getMinecraftClass("Particle"));
|
|
||||||
add(MinecraftReflection::getFluidTypeClass);
|
add(MinecraftReflection::getFluidTypeClass);
|
||||||
add(MinecraftReflection::getParticleTypeClass);
|
add(MinecraftReflection::getParticleTypeClass);
|
||||||
|
add("Particle");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MinecraftVersion.atOrAbove(MinecraftVersion.VILLAGE_UPDATE)) {
|
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) {
|
private static void add(Supplier<Class<?>> getClass) {
|
||||||
@ -83,6 +88,15 @@ public class ImmutableDetector implements Cloner {
|
|||||||
}
|
}
|
||||||
} catch (RuntimeException ignored) { }
|
} 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
|
@Override
|
||||||
public boolean canClone(Object source) {
|
public boolean canClone(Object source) {
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
package com.comphenix.protocol.wrappers;
|
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;
|
||||||
import com.comphenix.protocol.PacketType.Protocol;
|
import com.comphenix.protocol.PacketType.Protocol;
|
||||||
import com.comphenix.protocol.ProtocolLogger;
|
import com.comphenix.protocol.ProtocolLogger;
|
||||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.Validate;
|
||||||
import org.bukkit.GameMode;
|
import org.bukkit.GameMode;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a generic enum converter.
|
* Represents a generic enum converter.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
@ -693,4 +696,98 @@ public abstract class EnumWrappers {
|
|||||||
this.genericType = genericType;
|
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"),
|
"String"),
|
||||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F),
|
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F),
|
||||||
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.getChatComponentSerializer(true)),
|
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) {
|
} else if (type == PacketType.Play.Server.CHAT) {
|
||||||
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
|
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
|
||||||
|
Loading…
Reference in New Issue
Block a user