diff --git a/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java b/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java index 6415e1b4..60106d60 100644 --- a/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java +++ b/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java @@ -30,39 +30,12 @@ import java.util.UUID; public class NetUtil { - private static final int[] EXPONENTS_OF_TWO = new int[] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; + private static final int POSITION_X_SIZE = 38; + private static final int POSITION_Y_SIZE = 26; + private static final int POSITION_Z_SIZE = 38; - private static final int POSITION_X_SIZE = 1 + lastExponentOfTwo(nextPowerOfTwo(30000000)); - private static final int POSITION_Z_SIZE = POSITION_X_SIZE; - private static final int POSITION_Y_SIZE = 64 - POSITION_X_SIZE - POSITION_Z_SIZE; - private static final int POSITION_Y_SHIFT = POSITION_Z_SIZE; - private static final int POSITION_X_SHIFT = POSITION_Y_SHIFT + POSITION_Y_SIZE; - private static final long POSITION_X_MASK = (1L << POSITION_X_SIZE) - 1; - private static final long POSITION_Y_MASK = (1L << POSITION_Y_SIZE) - 1; - private static final long POSITION_Z_MASK = (1L << POSITION_Z_SIZE) - 1; - - private static int nextPowerOfTwo(int i) { - int minusOne = i - 1; - minusOne |= minusOne >> 1; - minusOne |= minusOne >> 2; - minusOne |= minusOne >> 4; - minusOne |= minusOne >> 8; - minusOne |= minusOne >> 16; - return minusOne + 1; - } - - private static boolean isPowerOfTwo(int i) { - return i != 0 && (i & i - 1) == 0; - } - - public static int nextExponentOfTwo(int i) { - int power = isPowerOfTwo(i) ? i : nextPowerOfTwo(i); - return EXPONENTS_OF_TWO[(int) (power * 125613361L >> 27) & 31]; - } - - public static int lastExponentOfTwo(int i) { - return nextExponentOfTwo(i) - (isPowerOfTwo(i) ? 0 : 1); - } + private static final int POSITION_Y_SHIFT = 0xFFF; + private static final int POSITION_WRITE_SHIFT = 0x3FFFFFF; public static CompoundTag readNBT(NetInput in) throws IOException { byte b = in.readByte(); @@ -83,14 +56,20 @@ public class NetUtil { public static Position readPosition(NetInput in) throws IOException { long val = in.readLong(); - int x = (int) (val << 64 - POSITION_X_SHIFT - POSITION_X_SIZE >> 64 - POSITION_X_SIZE); - int y = (int) (val << 64 - POSITION_Y_SHIFT - POSITION_Y_SIZE >> 64 - POSITION_Y_SIZE); - int z = (int) (val << 64 - POSITION_Z_SIZE >> 64 - POSITION_Z_SIZE); + + int x = (int) (val >> POSITION_X_SIZE); + int y = (int) ((val >> POSITION_Y_SIZE) & POSITION_Y_SHIFT); + int z = (int) ((val << POSITION_Z_SIZE) >> POSITION_Z_SIZE); + return new Position(x, y, z); } public static void writePosition(NetOutput out, Position pos) throws IOException { - out.writeLong((pos.getX() & POSITION_X_MASK) << POSITION_X_SHIFT | (pos.getY() & POSITION_Y_MASK) << POSITION_Y_SHIFT | (pos.getZ() & POSITION_Z_MASK)); + long x = pos.getX() & POSITION_WRITE_SHIFT; + long y = pos.getY() & POSITION_Y_SHIFT; + long z = pos.getZ() & POSITION_WRITE_SHIFT; + + out.writeLong(x << POSITION_X_SIZE | y << POSITION_Y_SIZE | z); } public static ItemStack readItem(NetInput in) throws IOException { diff --git a/src/test/java/org/spacehq/mc/protocol/ByteBufHelper.java b/src/test/java/org/spacehq/mc/protocol/ByteBufHelper.java new file mode 100644 index 00000000..092caf3a --- /dev/null +++ b/src/test/java/org/spacehq/mc/protocol/ByteBufHelper.java @@ -0,0 +1,60 @@ +package org.spacehq.mc.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.spacehq.mc.protocol.data.game.Position; +import org.spacehq.mc.protocol.data.game.values.world.block.BlockChangeRecord; +import org.spacehq.packetlib.packet.Packet; +import org.spacehq.packetlib.tcp.io.ByteBufNetInput; +import org.spacehq.packetlib.tcp.io.ByteBufNetOutput; + +import java.lang.reflect.Constructor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class ByteBufHelper { + + private static final ByteBuf buffer = Unpooled.buffer(); + + public static final ByteBufNetOutput out = new ByteBufNetOutput(buffer); + public static final ByteBufNetInput in = new ByteBufNetInput(buffer); + + @SuppressWarnings("unchecked") + public static T writeAndRead(Packet writable){ + if(buffer.isReadable()) { + buffer.release(); + } + + try { + writable.write(out); + + // Creating new fresh packet to reset fields. + Constructor constructor = writable.getClass().getDeclaredConstructor(); + constructor.setAccessible(true); + + Packet readable = (Packet) constructor.newInstance(); + readable.read(in); + + assertFalse("Buffer is not empty", buffer.isReadable()); + + return (T) readable; + } catch(Exception e) { + throw new IllegalStateException("Failed parse packet", e); + } finally { + buffer.release(); + } + } + + public static void assertPosition(Position position, int x, int y, int z) { + assertEquals("Received incorrect X position", x, position.getX()); + assertEquals("Received incorrect Y position", y, position.getY()); + assertEquals("Received incorrect Z position", z, position.getZ()); + } + + public static void assertBlock(BlockChangeRecord record, int block, int data) { + assertEquals("Received incorrect block id", block, record.getId()); + assertEquals("Received incorrect block data", data, record.getData()); + } + +} diff --git a/src/test/java/org/spacehq/mc/protocol/MinecraftProtocolTest.java b/src/test/java/org/spacehq/mc/protocol/MinecraftProtocolTest.java index 8d828391..2dd14179 100644 --- a/src/test/java/org/spacehq/mc/protocol/MinecraftProtocolTest.java +++ b/src/test/java/org/spacehq/mc/protocol/MinecraftProtocolTest.java @@ -1,7 +1,5 @@ package org.spacehq.mc.protocol; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -25,14 +23,13 @@ import org.spacehq.packetlib.event.session.PacketReceivedEvent; import org.spacehq.packetlib.event.session.SessionAdapter; import org.spacehq.packetlib.packet.Packet; import org.spacehq.packetlib.tcp.TcpSessionFactory; -import org.spacehq.packetlib.tcp.io.ByteBufNetInput; -import org.spacehq.packetlib.tcp.io.ByteBufNetOutput; import java.io.IOException; import java.util.concurrent.CountDownLatch; import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.*; +import static org.spacehq.mc.protocol.ByteBufHelper.*; import static org.spacehq.mc.protocol.MinecraftConstants.*; import static org.spacehq.mc.protocol.data.SubProtocol.STATUS; import static org.spacehq.mc.protocol.data.game.values.entity.player.GameMode.SURVIVAL; @@ -120,27 +117,11 @@ public class MinecraftProtocolTest { @Test public void testBlockBreak() throws IOException { - ByteBuf buffer = Unpooled.buffer(); + BlockChangeRecord record = new BlockChangeRecord(new Position(1, 61, -1), 3, 2); + ServerBlockChangePacket packet = writeAndRead(new ServerBlockChangePacket(record)); - ByteBufNetOutput out = new ByteBufNetOutput(buffer); - ByteBufNetInput in = new ByteBufNetInput(buffer); - - Position position = new Position(1, 61, -1); - BlockChangeRecord record = new BlockChangeRecord(position, 3, 2); - - new ServerBlockChangePacket(record).write(out); - ServerBlockChangePacket packet = new ServerBlockChangePacket(record); - packet.read(in); - - record = packet.getRecord(); - position = record.getPosition(); - - assertFalse("Buffer is not empty", buffer.isReadable()); - assertEquals("Received incorrect X position", 1, position.getX()); - assertEquals("Received incorrect Y position", 61, position.getY()); - assertEquals("Received incorrect Z position", -1, position.getZ()); - assertEquals("Received incorrect block id", 3, record.getId()); - assertEquals("Received incorrect block data", 2, record.getData()); + assertPosition(packet.getRecord().getPosition(), 1, 61, -1); + assertBlock(packet.getRecord(), 3, 2); } @After diff --git a/src/test/java/org/spacehq/mc/protocol/util/NetUtilTest.java b/src/test/java/org/spacehq/mc/protocol/util/NetUtilTest.java new file mode 100644 index 00000000..5e5df666 --- /dev/null +++ b/src/test/java/org/spacehq/mc/protocol/util/NetUtilTest.java @@ -0,0 +1,20 @@ +package org.spacehq.mc.protocol.util; + +import org.junit.Test; +import org.spacehq.mc.protocol.data.game.Position; + +import java.io.IOException; + +import static org.spacehq.mc.protocol.ByteBufHelper.*; +import static org.spacehq.mc.protocol.util.NetUtil.readPosition; +import static org.spacehq.mc.protocol.util.NetUtil.writePosition; + +public class NetUtilTest { + + @Test + public void testPosition() throws IOException { + writePosition(out, new Position(1, 61, -1)); + assertPosition(readPosition(in), 1, 61, -1); + } + +}