From 38cf8f48886a0f31c91d833544942cc50b986ec3 Mon Sep 17 00:00:00 2001 From: Dan Mulloy Date: Sat, 29 Nov 2014 13:35:34 -0500 Subject: [PATCH] Add wrapped block position, fix some errors --- .../protocol/utility/MinecraftReflection.java | 11 +- .../protocol/wrappers/BlockPosition.java | 230 ++++++++++++++++++ .../wrappers/WrappedWatchableObject.java | 27 +- 3 files changed, 255 insertions(+), 13 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BlockPosition.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 42076c61..416d9fd7 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -423,7 +423,8 @@ public class MinecraftReflection { */ @Deprecated public static boolean isChunkPosition(Object obj) { - return obj != null && getChunkPositionClass().isAssignableFrom(obj.getClass()); + Class chunkPosition = getChunkPositionClass(); + return obj != null && chunkPosition != null && chunkPosition.isAssignableFrom(obj.getClass()); } /** @@ -447,10 +448,13 @@ public class MinecraftReflection { /** * Determine if a given object is a ChunkCoordinate. * @param obj - the object to test. + * @deprecated ChunkCoordinate(s) does not exist. * @return TRUE if it can, FALSE otherwise. */ + @Deprecated public static boolean isChunkCoordinates(Object obj) { - return obj != null && getChunkCoordinatesClass().isAssignableFrom(obj.getClass()); + Class chunkCoordinates = getChunkCoordinatesClass(); + return obj != null && chunkCoordinates != null && chunkCoordinates.isAssignableFrom(obj.getClass()); } /** @@ -1188,7 +1192,7 @@ public class MinecraftReflection { try { return getMinecraftClass("ChunkPosition"); } catch (RuntimeException e) { - return getBlockPositionClass(); + return null; // Class normalChunkGenerator = getCraftBukkitClass("generator.NormalChunkGenerator"); // // // ChunkPosition a(net.minecraft.server.World world, String string, int i, int i1, int i2) { @@ -1233,6 +1237,7 @@ public class MinecraftReflection { * Retrieve the ChunkCoordinates class. * @return The ChunkPosition class. */ + @Deprecated public static Class getChunkCoordinatesClass() { try { return getMinecraftClass("ChunkCoordinates"); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BlockPosition.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BlockPosition.java new file mode 100644 index 00000000..5f5e27a3 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BlockPosition.java @@ -0,0 +1,230 @@ +/** + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +package com.comphenix.protocol.wrappers; + +import java.lang.reflect.Constructor; + +import org.bukkit.util.Vector; + +import com.comphenix.protocol.reflect.EquivalentConverter; +import com.comphenix.protocol.reflect.FieldAccessException; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; +import com.google.common.base.Objects; + +/** + * Copies a immutable net.minecraft.server.BlockPosition, which represents a integer 3D vector. + * + * @author dmulloy2 + */ +public class BlockPosition { + + /** + * Represents the null (0, 0, 0) origin. + */ + public static BlockPosition ORIGIN = new BlockPosition(0, 0, 0); + + private static Constructor blockPositionConstructor; + + // Use protected members, like Bukkit + protected final int x; + protected final int y; + protected final int z; + + // Used to access a BlockPosition, in case it's names are changed + private static StructureModifier intModifier; + + /** + * Construct an immutable 3D vector. + */ + public BlockPosition(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Construct an immutable integer 3D vector from a mutable Bukkit vector. + * @param vector - the mutable real Bukkit vector to copy. + */ + public BlockPosition(Vector vector) { + if (vector == null) + throw new IllegalArgumentException("Vector cannot be NULL."); + this.x = vector.getBlockX(); + this.y = vector.getBlockY(); + this.z = vector.getBlockZ(); + } + + /** + * Convert this instance to an equivalent real 3D vector. + * @return Real 3D vector. + */ + public Vector toVector() { + return new Vector(x, y, z); + } + + /** + * Retrieve the x-coordinate. + * @return X coordinate. + */ + public int getX() { + return x; + } + + /** + * Retrieve the y-coordinate. + * @return Y coordinate. + */ + public int getY() { + return y; + } + + /** + * Retrieve the z-coordinate. + * @return Z coordinate. + */ + public int getZ() { + return z; + } + + /** + * Adds the current position and a given position together, producing a result position. + * @param other - the other position. + * @return The new result position. + */ + public BlockPosition add(BlockPosition other) { + if (other == null) + throw new IllegalArgumentException("other cannot be NULL"); + return new BlockPosition(x + other.x, y + other.y, z + other.z); + } + + /** + * Adds the current position and a given position together, producing a result position. + * @param other - the other position. + * @return The new result position. + */ + public BlockPosition subtract(BlockPosition other) { + if (other == null) + throw new IllegalArgumentException("other cannot be NULL"); + return new BlockPosition(x - other.x, y - other.y, z - other.z); + } + + /** + * Multiply each dimension in the current position by the given factor. + * @param factor - multiplier. + * @return The new result. + */ + public BlockPosition multiply(int factor) { + return new BlockPosition(x * factor, y * factor, z * factor); + } + + /** + * Divide each dimension in the current position by the given divisor. + * @param divisor - the divisor. + * @return The new result. + */ + public BlockPosition divide(int divisor) { + if (divisor == 0) + throw new IllegalArgumentException("Cannot divide by null."); + return new BlockPosition(x / divisor, y / divisor, z / divisor); + } + + /** + * Used to convert between NMS ChunkPosition and the wrapper instance. + * @return A new converter. + */ + public static EquivalentConverter getConverter() { + return new EquivalentConverter() { + @Override + public Object getGeneric(Class genericType, BlockPosition specific) { + if (blockPositionConstructor == null) { + try { + blockPositionConstructor = MinecraftReflection.getBlockPositionClass(). + getConstructor(int.class, int.class, int.class); + } catch (Exception e) { + throw new RuntimeException("Cannot find block position constructor.", e); + } + } + + // Construct the underlying BlockPosition + try { + Object result = blockPositionConstructor.newInstance(specific.x, specific.y, specific.z); + return result; + } catch (Exception e) { + throw new RuntimeException("Cannot construct BlockPosition.", e); + } + } + + @Override + public BlockPosition getSpecific(Object generic) { + if (MinecraftReflection.isBlockPosition(generic)) { + // Use a structure modifier + intModifier = new StructureModifier(generic.getClass(), null, false).withType(int.class); + + // Damn it all + if (intModifier.size() < 3) { + throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields."); + } + + if (intModifier.size() >= 3) { + try { + StructureModifier instance = intModifier.withTarget(generic); + BlockPosition result = new BlockPosition(instance.read(0), instance.read(1), instance.read(2)); + return result; + } catch (FieldAccessException e) { + // This is an exeptional work-around, so we don't want to burden the caller with the messy details + throw new RuntimeException("Field access error.", e); + } + } + } + + // Otherwise, return NULL + return null; + } + + // Thanks Java Generics! + @Override + public Class getSpecificType() { + return BlockPosition.class; + } + }; + } + + @Override + public boolean equals(Object obj) { + // Fast checks + if (this == obj) return true; + if (obj == null) return false; + + // Only compare objects of similar type + if (obj instanceof BlockPosition) { + BlockPosition other = (BlockPosition) obj; + return x == other.x && y == other.y && z == other.z; + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(x, y, z); + } + + @Override + public String toString() { + return "BlockPosition [x=" + x + ", y=" + y + ", z=" + z + "]"; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java index 49b5ee4f..cc99fa72 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -353,6 +353,8 @@ public class WrappedWatchableObject extends AbstractWrapper { static Class getWrappedType(Class unwrapped) { if (unwrapped.equals(MinecraftReflection.getChunkPositionClass())) return ChunkPosition.class; + else if (unwrapped.equals(MinecraftReflection.getBlockPositionClass())) + return BlockPosition.class; else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass())) return WrappedChunkCoordinate.class; else if (unwrapped.equals(MinecraftReflection.getItemStackClass())) @@ -367,17 +369,17 @@ public class WrappedWatchableObject extends AbstractWrapper { * @return The raw NMS object. */ static Object getUnwrapped(Object wrapped) { - // Convert special cases + // Convert special cases if (wrapped instanceof ChunkPosition) - return ChunkPosition.getConverter().getGeneric( - MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped); + return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped); + else if (wrapped instanceof BlockPosition) + return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped); else if (wrapped instanceof WrappedChunkCoordinate) - return ((WrappedChunkCoordinate) wrapped).getHandle(); - else if (wrapped instanceof ItemStack) - return BukkitConverters.getItemStackConverter().getGeneric( - MinecraftReflection.getItemStackClass(), (ItemStack) wrapped); - else - return wrapped; + return ((WrappedChunkCoordinate) wrapped).getHandle(); + else if (wrapped instanceof ItemStack) + return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped); + else + return wrapped; } /** @@ -388,6 +390,8 @@ public class WrappedWatchableObject extends AbstractWrapper { static Class getUnwrappedType(Class wrapped) { if (wrapped.equals(ChunkPosition.class)) return MinecraftReflection.getChunkPositionClass(); + else if (wrapped.equals(BlockPosition.class)) + return MinecraftReflection.getBlockPositionClass(); else if (wrapped.equals(WrappedChunkCoordinate.class)) return MinecraftReflection.getChunkCoordinatesClass(); else if (ItemStack.class.isAssignableFrom(wrapped)) @@ -417,7 +421,10 @@ public class WrappedWatchableObject extends AbstractWrapper { Object value = getNMSValue(); // Only a limited set of references types are supported - if (MinecraftReflection.isChunkPosition(value)) { + if (MinecraftReflection.isBlockPosition(value)) { + EquivalentConverter converter = BlockPosition.getConverter(); + return converter.getGeneric(MinecraftReflection.getBlockPositionClass(), converter.getSpecific(value)); + } else if (MinecraftReflection.isChunkPosition(value)) { EquivalentConverter converter = ChunkPosition.getConverter(); return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value)); } else if (MinecraftReflection.isItemStack(value)) {