mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 03:25:29 +01:00
Improve cloning tests (#1822)
This commit is contained in:
parent
7fcfcdc365
commit
1beb95115f
@ -17,6 +17,11 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
@ -26,13 +31,11 @@ import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.ZeroBuffer;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
@ -52,33 +55,34 @@ public class StructureCache {
|
||||
private static boolean TRICK_TRIED = false;
|
||||
private static ConstructorAccessor TRICKED_DATA_SERIALIZER;
|
||||
|
||||
public static Object newPacket(Class<?> clazz) {
|
||||
Object result = DefaultInstances.DEFAULT.create(clazz);
|
||||
|
||||
if (result == null) {
|
||||
return PACKET_INSTANCE_CREATORS.computeIfAbsent(clazz, $ -> {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessorOrNull(clazz,
|
||||
public static Object newPacket(Class<?> packetClass) {
|
||||
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> {
|
||||
// prefer construction via PacketDataSerializer constructor on 1.17 and above
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
|
||||
clazz,
|
||||
MinecraftReflection.getPacketDataSerializerClass());
|
||||
if (accessor != null) {
|
||||
if (serializerAccessor != null) {
|
||||
return () -> {
|
||||
try {
|
||||
return accessor.invoke(MinecraftReflection.getPacketDataSerializer(new ZeroBuffer()));
|
||||
} catch (Exception exception) {
|
||||
// try trick nms around as they want a non-null compound in the map_chunk packet constructor
|
||||
Object trickyDataSerializer = getTrickDataSerializerOrNull();
|
||||
if (trickyDataSerializer != null) {
|
||||
return accessor.invoke(trickyDataSerializer);
|
||||
}
|
||||
// the tricks are over
|
||||
throw new IllegalArgumentException("Unable to create packet " + clazz, exception);
|
||||
// the tricked serializer should always be present... If not try using a normal buffer
|
||||
Object dataSerializer = getTrickDataSerializerOrNull();
|
||||
if (dataSerializer == null) {
|
||||
dataSerializer = MinecraftReflection.getPacketDataSerializer(new ZeroBuffer());
|
||||
}
|
||||
|
||||
return serializerAccessor.invoke(dataSerializer);
|
||||
};
|
||||
}
|
||||
throw new IllegalArgumentException("No matching constructor to create packet in class " + clazz);
|
||||
}).get();
|
||||
}
|
||||
|
||||
return result;
|
||||
// try via DefaultInstances as fallback
|
||||
return () -> {
|
||||
Object packetInstance = DefaultInstances.DEFAULT.create(clazz);
|
||||
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + clazz);
|
||||
return packetInstance;
|
||||
};
|
||||
});
|
||||
return packetConstructor.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,17 +117,15 @@ public class StructureCache {
|
||||
public static StructureModifier<Object> getStructure(final PacketType packetType) {
|
||||
Preconditions.checkNotNull(packetType, "type cannot be null");
|
||||
|
||||
StructureModifier<Object> modifier = STRUCTURE_MODIFIER_CACHE.computeIfAbsent(packetType, type -> {
|
||||
return STRUCTURE_MODIFIER_CACHE.computeIfAbsent(packetType, type -> {
|
||||
Class<?> packetClass = PacketRegistry.getPacketClassFromType(type);
|
||||
return new StructureModifier<>(packetClass, MinecraftReflection.getPacketClass(), true);
|
||||
});
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet data serializer sub-class if needed to allow the fixed read of a NbtTagCompound because of a null
|
||||
* check in the MapChunk packet constructor.
|
||||
* Creates a packet data serializer sub-class if needed to allow the fixed read of a NbtTagCompound because of a
|
||||
* null check in the MapChunk packet constructor.
|
||||
*
|
||||
* @return an accessor to a constructor which creates a data serializer.
|
||||
*/
|
||||
|
@ -269,13 +269,8 @@ public class StructureModifier<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// don't try to convert null, just return null
|
||||
// get the field value and convert it if needed
|
||||
Object fieldValue = accessor.get(this.target);
|
||||
if (fieldValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// check if we need to convert the field value and do so if needed
|
||||
return this.needConversion() ? this.converter.getSpecific(fieldValue) : (T) fieldValue;
|
||||
}
|
||||
|
||||
@ -332,14 +327,10 @@ public class StructureModifier<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
// just write the value if the specific given one is null or no conversion is needed
|
||||
if (value == null || !this.needConversion()) {
|
||||
accessor.set(this.target, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
// convert and write
|
||||
accessor.set(this.target, this.converter.getGeneric(value));
|
||||
Object fieldValue = this.needConversion() ? this.converter.getGeneric(value) : value;
|
||||
accessor.set(this.target, fieldValue);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,9 @@ public class ImmutableDetector implements Cloner {
|
||||
add("world.entity.npc.VillagerProfession", "VillagerProfession");
|
||||
}
|
||||
|
||||
add("world.entity.animal.CatVariant");
|
||||
add("world.entity.animal.FrogVariant");
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -18,14 +18,15 @@
|
||||
package com.comphenix.protocol.reflect.instances;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.base.Defaults;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides constructors for primtive types, wrappers, arrays and strings.
|
||||
* Provides constructors for primitive java types and wrappers.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PrimitiveGenerator implements InstanceProvider {
|
||||
@ -33,24 +34,32 @@ public class PrimitiveGenerator implements InstanceProvider {
|
||||
/**
|
||||
* Default value for Strings.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String STRING_DEFAULT = "";
|
||||
|
||||
/**
|
||||
* Shared instance of this generator.
|
||||
*/
|
||||
public static PrimitiveGenerator INSTANCE = new PrimitiveGenerator(STRING_DEFAULT);
|
||||
public static PrimitiveGenerator INSTANCE = new PrimitiveGenerator();
|
||||
|
||||
// Our default string value
|
||||
private String stringDefault;
|
||||
private final String stringDefault;
|
||||
|
||||
public PrimitiveGenerator() {
|
||||
this.stringDefault = "";
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public PrimitiveGenerator(String stringDefault) {
|
||||
this.stringDefault = stringDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the string default.
|
||||
*
|
||||
* @return Default instance of a string.
|
||||
*/
|
||||
@Deprecated
|
||||
public String getStringDefault() {
|
||||
return stringDefault;
|
||||
}
|
||||
@ -68,10 +77,13 @@ public class PrimitiveGenerator implements InstanceProvider {
|
||||
return Array.newInstance(arrayType, 0);
|
||||
} else if (type.isEnum()) {
|
||||
Object[] values = type.getEnumConstants();
|
||||
if (values != null && values.length > 0)
|
||||
if (values != null && values.length > 0) {
|
||||
return values[0];
|
||||
}
|
||||
} else if (type.equals(String.class)) {
|
||||
return stringDefault;
|
||||
return this.stringDefault;
|
||||
} else if (type.equals(Optional.class)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Cannot handle this type
|
||||
|
@ -366,7 +366,7 @@ public class ZeroBuffer extends ByteBuf {
|
||||
|
||||
@Override
|
||||
public short readUnsignedByte() {
|
||||
return 0;
|
||||
return 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,6 +35,7 @@ import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
@ -67,6 +68,7 @@ import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import net.minecraft.core.IRegistry;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutGameStateChange;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
|
||||
import net.minecraft.resources.MinecraftKey;
|
||||
@ -74,9 +76,8 @@ import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectList;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeBase;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.world.entity.npc.VillagerData;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import net.minecraft.world.entity.npc.VillagerType;
|
||||
import net.minecraft.world.entity.animal.CatVariant;
|
||||
import net.minecraft.world.entity.animal.FrogVariant;
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
@ -794,7 +795,7 @@ public class PacketContainerTest {
|
||||
assertArrayEquals(signature, read.getSignature());
|
||||
}*/
|
||||
|
||||
private void assertPacketsEqual(PacketContainer constructed, PacketContainer cloned) {
|
||||
private void assertPacketsEqualAndSerializable(PacketContainer constructed, PacketContainer cloned) {
|
||||
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
|
||||
assertEquals(firstMod.size(), secondMod.size());
|
||||
|
||||
@ -810,6 +811,9 @@ public class PacketContainerTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object buffer = MinecraftReflection.createPacketDataSerializer(0);
|
||||
MinecraftMethods.getPacketWriteByteBufMethod().invoke(cloned.getHandle(), buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -833,32 +837,59 @@ public class PacketContainerTest {
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(Byte.class)),
|
||||
(byte) 1),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(Integer.class)),
|
||||
1),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(Float.class)),
|
||||
1F),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(String.class)),
|
||||
"String"),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(Float.class)),
|
||||
1.0F),
|
||||
new WrappedDataWatcherObject(0, Registry.get(Boolean.class)),
|
||||
true),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.getChatComponentSerializer(true)),
|
||||
Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle())),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(VillagerData.class)),
|
||||
new VillagerData(VillagerType.b, VillagerProfession.c, 69))
|
||||
new WrappedDataWatcherObject(0, Registry.getItemStackSerializer(false)),
|
||||
BukkitConverters.getItemStackConverter().getGeneric(new ItemStack(Material.WOODEN_AXE))),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(CatVariant.class)),
|
||||
CatVariant.a),
|
||||
new WrappedWatchableObject(
|
||||
new WrappedDataWatcherObject(0, Registry.get(FrogVariant.class)),
|
||||
FrogVariant.c)
|
||||
));
|
||||
} else if (type == PacketType.Play.Server.CHAT) {
|
||||
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
|
||||
} else if (type == PacketType.Play.Server.REMOVE_ENTITY_EFFECT || type == PacketType.Play.Server.ENTITY_EFFECT) {
|
||||
constructed.getEffectTypes().write(0, PotionEffectType.GLOWING);
|
||||
} else if (type == PacketType.Play.Server.GAME_STATE_CHANGE) {
|
||||
constructed.getStructures().write(
|
||||
0,
|
||||
InternalStructure.getConverter().getSpecific(PacketPlayOutGameStateChange.a));
|
||||
} else if (type == PacketType.Play.Client.USE_ITEM || type == PacketType.Play.Client.BLOCK_PLACE) {
|
||||
constructed.getLongs().write(0, 0L); // timestamp of the packet, not sent over the network
|
||||
}
|
||||
|
||||
// gives some indication which cloning process fails as the checks itself are happening outside this method
|
||||
System.out.println("Cloning " + type);
|
||||
|
||||
// Clone the packet both ways
|
||||
// Clone the packet all three ways
|
||||
PacketContainer shallowCloned = constructed.shallowClone();
|
||||
this.assertPacketsEqual(constructed, shallowCloned);
|
||||
this.assertPacketsEqualAndSerializable(constructed, shallowCloned);
|
||||
|
||||
PacketContainer deepCloned = constructed.deepClone();
|
||||
this.assertPacketsEqual(constructed, deepCloned);
|
||||
this.assertPacketsEqualAndSerializable(constructed, deepCloned);
|
||||
|
||||
PacketContainer serializedCloned = SerializableCloner.clone(constructed);
|
||||
if (type == PacketType.Play.Client.USE_ITEM || type == PacketType.Play.Client.BLOCK_PLACE) {
|
||||
// shit fix - but what are we supposed to do :/
|
||||
serializedCloned.getLongs().write(0, 0L);
|
||||
}
|
||||
this.assertPacketsEqualAndSerializable(constructed, serializedCloned);
|
||||
} catch (Exception ex) {
|
||||
Assertions.fail("Unable to clone " + type, ex);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user