Add modifiers for world keys and moving block position

Fixes #906
Fixes #927
This commit is contained in:
Dan Mulloy 2020-08-04 19:03:59 -04:00
parent 8c51b175c4
commit 0512215007
No known key found for this signature in database
GPG Key ID: 2B62F7DACFF133E8
5 changed files with 236 additions and 25 deletions

View File

@ -29,10 +29,7 @@ import javax.annotation.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.*;
import com.comphenix.protocol.reflect.cloning.*;
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
@ -148,6 +145,7 @@ public class PacketContainer implements Serializable {
// TODO this is kinda hacky, come up with a better solution
if (type == PacketType.Play.Server.CHAT) {
System.out.println("writing UUID to chat packet");
getUUIDs().writeSafely(0, new UUID(0L, 0L));
}
}
@ -927,15 +925,18 @@ public class PacketContainer implements Serializable {
* @return A modifier for dimension IDs
*/
public StructureModifier<Integer> getDimensions() {
// this isn't technically correct (and is, therefore, an inferior type of correct)
// but the resource keys are parameterized and we might have to modify structure modifier to support it
// or at least come up with a way to reflectively obtain ResourceKey<DimensionManager>
// TODO a more complete solution
return structureModifier.withType(
NEW_DIMENSIONS ? MinecraftReflection.getMinecraftClass("ResourceKey") : MinecraftReflection.getMinecraftClass("DimensionManager"),
BukkitConverters.getDimensionIDConverter()
);
if (NEW_DIMENSIONS) {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("ResourceKey"),
BukkitConverters.getDimensionIDConverter(),
MinecraftReflection.getMinecraftClass("DimensionManager")
);
} else {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("DimensionManager"),
BukkitConverters.getDimensionIDConverter()
);
}
}
/**
@ -949,6 +950,29 @@ public class PacketContainer implements Serializable {
));
}
/**
* Retrieve a read/write structure for MovingObjectPositionBlock in 1.16+
* @return The Structure Modifier
*/
public StructureModifier<MovingObjectPositionBlock> getMovingBlockPositions() {
return structureModifier.withType(
MovingObjectPositionBlock.getNmsClass(),
MovingObjectPositionBlock.getConverter()
);
}
/**
* Retrieve a read/write structure for World ResourceKeys in 1.16+
* @return The Structure Modifier
*/
public StructureModifier<World> getWorldKeys() {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("ResourceKey"),
BukkitConverters.getWorldKeyConverter(),
MinecraftReflection.getNmsWorldClass()
);
}
/**
* Retrieve a read/write structure for the Map class.
* @param keyConverter Converter for map keys

View File

@ -19,6 +19,8 @@ package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
@ -458,7 +460,16 @@ public class StructureModifier<TField> {
return this;
}
private static Type[] getParamTypes(Field field) {
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
return ((ParameterizedType) genericType).getActualTypeArguments();
}
return new Class<?>[0];
}
/**
* Retrieves a structure modifier that only reads and writes fields of a given type.
* @param <T> Type
@ -467,7 +478,7 @@ public class StructureModifier<TField> {
* @return A structure modifier for fields of this type.
*/
@SuppressWarnings("unchecked")
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter) {
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter, Class... paramTypes) {
if (fieldType == null) {
// It's not supported in this version, so return an empty modifier
return new StructureModifier<T>() {
@ -489,11 +500,13 @@ public class StructureModifier<TField> {
for (Field field : data) {
if (fieldType.isAssignableFrom(field.getType())) {
filtered.add(field);
// Don't use the original index
if (defaultFields.containsKey(field))
defaults.put(field, index);
if (paramTypes.length == 0 || Arrays.equals(getParamTypes(field), paramTypes)) {
filtered.add(field);
// Don't use the original index
if (defaultFields.containsKey(field))
defaults.put(field, index);
}
}
// Keep track of the field index

View File

@ -52,10 +52,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@ -1171,6 +1168,59 @@ public class BukkitConverters {
private static MethodAccessor dimensionFromId = null;
private static MethodAccessor idFromDimension = null;
private static FieldAccessor worldKeyField = null;
private static MethodAccessor getServer = null;
private static MethodAccessor getWorldServer = null;
private static MethodAccessor getWorld = null;
public static EquivalentConverter<World> getWorldKeyConverter() {
return ignoreNull(new EquivalentConverter<World>() {
@Override
public Object getGeneric(World specific) {
Object nmsWorld = getWorldConverter().getGeneric(specific);
if (worldKeyField == null) {
Class<?> worldClass = MinecraftReflection.getNmsWorldClass();
Class<?> resourceKeyClass = MinecraftReflection.getMinecraftClass("ResourceKey");
FuzzyReflection fuzzy = FuzzyReflection.fromClass(nmsWorld.getClass(), true);
worldKeyField = Accessors.getFieldAccessor(fuzzy.getParameterizedField(resourceKeyClass, worldClass));
}
return worldKeyField.get(nmsWorld);
}
@Override
public World getSpecific(Object generic) {
if (getServer == null) {
getServer = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getServer");
}
Object server = getServer.invoke(Bukkit.getServer());
if (getWorldServer == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(server.getClass(), false);
getWorldServer = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterExactArray(generic.getClass())
.returnTypeExact(MinecraftReflection.getWorldServerClass())
.build()));
}
Object worldServer = getWorldServer.invoke(server, generic);
if (getWorld == null) {
getWorld = Accessors.getMethodAccessor(worldServer.getClass(), "getWorld");
}
return (World) getWorld.invoke(worldServer);
}
@Override
public Class<World> getSpecificType() {
return World.class;
}
});
}
public static EquivalentConverter<Integer> getDimensionIDConverter() {
return ignoreNull(new EquivalentConverter<Integer>() {
@Override

View File

@ -0,0 +1,102 @@
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.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import org.bukkit.util.Vector;
public class MovingObjectPositionBlock implements Cloneable {
private static final Class<?> NMS_CLASS = MinecraftReflection.getNullableNMS("MovingObjectPositionBlock");
private BlockPosition position;
private Vector posVector;
private Direction direction;
private boolean insideBlock;
public MovingObjectPositionBlock() { }
public MovingObjectPositionBlock(BlockPosition position, Vector posVector, Direction direction, boolean insideBlock) {
this.position = position;
this.posVector = posVector;
this.direction = direction;
this.insideBlock = insideBlock;
}
public static Class<?> getNmsClass() {
return NMS_CLASS;
}
public BlockPosition getBlockPosition() {
return position;
}
public void setBlockPosition(BlockPosition position) {
this.position = position;
}
public Vector getPosVector() {
return posVector;
}
public void setPosVector(Vector vector) {
this.posVector = vector;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public boolean isInsideBlock() {
return insideBlock;
}
public void setInsideBlock(boolean insideBlock) {
this.insideBlock = insideBlock;
}
private static ConstructorAccessor constructor;
public static EquivalentConverter<MovingObjectPositionBlock> getConverter() {
return Converters.ignoreNull(new EquivalentConverter<MovingObjectPositionBlock>() {
@Override
public Object getGeneric(MovingObjectPositionBlock specific) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(NMS_CLASS,
MinecraftReflection.getVec3DClass(),
EnumWrappers.getDirectionClass(),
MinecraftReflection.getBlockPositionClass(),
boolean.class);
}
Object nmsVector = BukkitConverters.getVectorConverter().getGeneric(specific.posVector);
Object nmsDirection = EnumWrappers.getDirectionConverter().getGeneric(specific.direction);
Object nmsBlockPos = BlockPosition.getConverter().getGeneric(specific.position);
return constructor.invoke(nmsVector, nmsDirection, nmsBlockPos, specific.insideBlock);
}
@Override
public MovingObjectPositionBlock getSpecific(Object generic) {
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass()).withTarget(generic);
Direction direction = modifier.withType(EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter()).read(0);
BlockPosition blockPos = modifier.withType(MinecraftReflection.getBlockPositionClass(), BlockPosition.getConverter()).read(0);
Vector posVector = modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
boolean insideBlock = (boolean) modifier.withType(boolean.class).read(1);
return new MovingObjectPositionBlock(blockPos, posVector, direction, insideBlock);
}
@Override
public Class<MovingObjectPositionBlock> getSpecificType() {
return MovingObjectPositionBlock.class;
}
});
}
}

View File

@ -30,6 +30,7 @@ import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
@ -52,6 +53,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -501,7 +503,7 @@ public class PacketContainerTest {
}
@Test
public void testDimensionManager() {
public void testDimensions() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.RESPAWN);
container.getDimensions().write(0, 1);
assertEquals((Object) 1, container.getDimensions().read(0));
@ -521,6 +523,26 @@ public class PacketContainerTest {
assertEquals(data, written);
}
@Test
public void testMovingBlockPos() {
PacketContainer container = new PacketContainer(PacketType.Play.Client.USE_ITEM);
Vector vector = new Vector(0, 1, 2);
BlockPosition position = new BlockPosition(3, 4, 5);
EnumWrappers.Direction direction = EnumWrappers.Direction.DOWN;
MovingObjectPositionBlock movingPos = new MovingObjectPositionBlock(position, vector, direction, true);
container.getMovingBlockPositions().write(0, movingPos);
MovingObjectPositionBlock back = container.getMovingBlockPositions().read(0);
assertEquals(back.getPosVector(), vector);
assertEquals(back.getBlockPosition(), position);
assertEquals(back.getDirection(), direction);
assertTrue(back.isInsideBlock());
}
/**
* Actions from the outbound Boss packet. Used for testing generic enums.
* @author dmulloy2