From 8bd177daf54f82b56c0cca30fe97efcbee5921fc Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 2 Feb 2014 19:45:40 +0100 Subject: [PATCH] Update the integration test for Minecraft 1.7.2. --- .../protocol/SimpleCraftBukkitITCase.java | 21 +- .../protocol/SimpleMinecraftClient.java | 210 +++++++++++++----- .../integration/protocol/TestPingPacket.java | 17 +- .../protocol/events/PacketContainerTest.java | 3 +- .../protocol/injector/PluginVerifierTest.java | 2 + .../utility/StreamSerializerTest.java | 2 + .../wrappers/WrappedWatchableObjectTest.java | 2 + .../protocol/wrappers/nbt/NbtFactoryTest.java | 2 + 8 files changed, 191 insertions(+), 68 deletions(-) diff --git a/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleCraftBukkitITCase.java b/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleCraftBukkitITCase.java index 492eaece..a4ead957 100644 --- a/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleCraftBukkitITCase.java +++ b/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleCraftBukkitITCase.java @@ -11,6 +11,8 @@ import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.simple.SimpleLogger; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; @@ -20,6 +22,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import com.comphenix.protocol.ProtocolLibrary; @@ -29,6 +32,7 @@ import com.google.common.io.Files; // Damn final classes ... @RunWith(org.powermock.modules.junit4.PowerMockRunner.class) +@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" }) @PrepareForTest(PluginDescriptionFile.class) public class SimpleCraftBukkitITCase { // The fake plugin @@ -50,7 +54,14 @@ public class SimpleCraftBukkitITCase { @BeforeClass public static void setupCraftBukkit() throws Exception { setupPlugins(); - org.bukkit.craftbukkit.Main.main(new String[0]); + + try { + org.bukkit.craftbukkit.Main.main(new String[0]); + } finally { + System.out.println("Current class loader: " + Thread.currentThread().getContextClassLoader()); + System.out.println("Loader of SimpleLogger: " + SimpleLogger.class.getClassLoader()); + System.out.println("Loader of Logger: " + Logger.class.getClassLoader()); + } // We need to wait until the server object is ready while (Bukkit.getServer() == null) @@ -61,12 +72,13 @@ public class SimpleCraftBukkitITCase { // No need to look for updates FieldUtils.writeStaticField(ProtocolLibrary.class, "UPDATES_DISABLED", Boolean.TRUE, true); - + // Wait until the server and all the plugins have loaded Bukkit.getScheduler().callSyncMethod(FAKE_PLUGIN, new Callable() { @Override public Object call() throws Exception { initializePlugin(FAKE_PLUGIN); + ProtocolLibrary.getConfiguration().setDebug(true); return null; } }).get(TIMEOUT_MS, TimeUnit.MILLISECONDS); @@ -98,7 +110,9 @@ public class SimpleCraftBukkitITCase { int bestLength = Integer.MAX_VALUE; // Copy the ProtocolLib plugin to the server - Files.deleteDirectoryContents(pluginDirectory); + if (pluginDirectory.exists()) { + Files.deleteDirectoryContents(pluginDirectory); + } for (File file : new File("../").listFiles()) { String name = file.getName(); @@ -108,6 +122,7 @@ public class SimpleCraftBukkitITCase { bestFile = file; } } + pluginDirectory.mkdirs(); Files.copy(bestFile, new File(pluginDirectory, bestFile.getName())); } diff --git a/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java b/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java index e24feef1..6de6edab 100644 --- a/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java +++ b/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java @@ -1,5 +1,8 @@ package com.comphenix.integration.protocol; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; @@ -7,24 +10,24 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; -import java.nio.charset.Charset; -import com.comphenix.protocol.utility.MinecraftVersion; -import com.google.common.base.Charsets; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.PacketType.Protocol; +import com.comphenix.protocol.PacketType.Sender; +import com.comphenix.protocol.utility.StreamSerializer; +import com.google.common.io.ByteStreams; public class SimpleMinecraftClient { private static final int CONNECT_TIMEOUT = 2500; private static final int READ_TIMEOUT = 15000; - - // The version after which we must send a plugin message with the host name - private static final String PLUGIN_MESSAGE_VERSION = "1.6.0"; - + // Current Minecraft version - private final MinecraftVersion version; private final int protocolVersion; + + // Typical Minecraft serializer + private static StreamSerializer serializer = StreamSerializer.getDefault(); - public SimpleMinecraftClient(MinecraftVersion version, int protocolVersion) { - this.version = version; + public SimpleMinecraftClient(int protocolVersion) { this.protocolVersion = protocolVersion; } @@ -49,9 +52,6 @@ public class SimpleMinecraftClient { InputStream input = null; InputStreamReader reader = null; - // UTF-16! - Charset charset = Charsets.UTF_16BE; - try { socket = new Socket(); socket.connect(address, CONNECT_TIMEOUT); @@ -62,42 +62,20 @@ public class SimpleMinecraftClient { // Retrieve sockets output = socket.getOutputStream(); input = socket.getInputStream(); - reader = new InputStreamReader(input, charset); + + // The output writer + DataOutputStream data = new DataOutputStream(output); + + // Request a server information packet + writePacket(data, new HandshakePacket(protocolVersion, address.getHostName(), address.getPort(), 1)); + writePacket(data, new RequestPacket()); + data.flush(); - // Get the server to send a MOTD - output.write(new byte[] { (byte) 0xFE, (byte) 0x01 }); - - // For 1.6 - if (version.compareTo(new MinecraftVersion(PLUGIN_MESSAGE_VERSION)) >= 0) { - DataOutputStream data = new DataOutputStream(output); - String host = address.getHostName(); - - data.writeByte(0xFA); - writeString(data, "MC|PingHost"); - data.writeShort(3 + 2 * host.length() + 4); - - data.writeByte(protocolVersion); - writeString(data, host); - data.writeInt(address.getPort()); - data.flush(); - } - - int packetId = input.read(); - int length = reader.read(); - - if (packetId != 255) - throw new IOException("Invalid packet ID: " + packetId); - if (length <= 0) - throw new IOException("Invalid string length."); - - char[] chars = new char[length]; - - // Read all the characters - if (reader.read(chars, 0, length) != length) { - throw new IOException("Premature end of stream."); - } - - return new String(chars); + // Read a single packet, and close the connection + SimplePacket packet = readPacket(new DataInputStream(input), Protocol.STATUS); + + socket.close(); + return ((ResponsePacket) packet).getPingJson(); } finally { if (reader != null) @@ -111,10 +89,136 @@ public class SimpleMinecraftClient { } } - private void writeString(DataOutputStream output, String text) throws IOException { - if (text.length() > 32767) - throw new IOException("String too big: " + text.length()); - output.writeShort(text.length()); - output.writeChars(text); + private void writePacket(DataOutputStream output, SimplePacket packet) throws IOException { + ByteArrayOutputStream packetBuffer = new ByteArrayOutputStream(); + DataOutputStream packetOutput = new DataOutputStream(packetBuffer); + + // Prefix the packet with a length field + packet.write(packetOutput); + writeByteArray(output, packetBuffer.toByteArray()); + } + + private SimplePacket readPacket(DataInputStream input, Protocol protocol) throws IOException { + while (true) { + byte[] buffer = readByteArray(input); + + // Skip empty packets + if (buffer.length == 0) + continue; + + DataInputStream data = getDataInput(buffer); + PacketType type = PacketType.findCurrent(protocol, Sender.SERVER, serializer.deserializeVarInt(data)); + + if (type == PacketType.Status.Server.OUT_SERVER_INFO) { + ResponsePacket response = new ResponsePacket(); + response.read(type, data); + return response; + } else { + throw new IllegalArgumentException("Unsuppported and unexpected type: " + type); + } + } + } + + /** + * Wrap an input stream around a byte array. + * @param bytes - the array. + * @return The wrapped input stream. + */ + private DataInputStream getDataInput(byte[] bytes) { + return new DataInputStream(new ByteArrayInputStream(bytes)); + } + + /** + * Write a byte array to the output stream, prefixed by a length. + * @param output - the stream. + * @param data - the data to write. + */ + private static void writeByteArray(DataOutputStream output, byte[] data) throws IOException { + StreamSerializer.getDefault().serializeVarInt(output, data.length); + + if (data.length > 0) { + output.write(data); + } + } + + /** + * Read a byte array from an input stream, prefixed by length. + * @param input - the input stream. + * @return The read byte array. + */ + private static byte[] readByteArray(DataInputStream input) throws IOException { + int length = serializer.deserializeVarInt(input); + byte[] data = new byte[length]; + + ByteStreams.readFully(input, data); + return data; + } + + private static class RequestPacket extends SimplePacket { + public RequestPacket() { + super(PacketType.Status.Client.IN_START); + } + } + + private static class ResponsePacket extends SimplePacket { + private String ping; + + public ResponsePacket() { + super(PacketType.Status.Server.OUT_SERVER_INFO); + } + + @Override + public void read(PacketType type, DataInputStream input) throws IOException { + super.read(type, input); + ping = serializer.deserializeString(input, 32000); + } + + public String getPingJson() { + return ping; + } + } + + private static class HandshakePacket extends SimplePacket { + private int protocol; + private String host; + private int port; + private int nextState; + + public HandshakePacket(int protocol, String host, int port, int nextState) { + super(PacketType.Handshake.Client.SET_PROTOCOL); + this.protocol = protocol; + this.host = host; + this.port = port; + this.nextState = nextState; + } + + @Override + public void write(DataOutputStream output) throws IOException { + super.write(output); + serializer.serializeVarInt(output, protocol); + serializer.serializeString(output, host); + output.writeShort(port); + serializer.serializeVarInt(output, nextState); + } + } + + private static class SimplePacket { + protected final PacketType type; + protected final StreamSerializer serializer = StreamSerializer.getDefault(); + + public SimplePacket(PacketType type) { + this.type = type; + } + + public void write(DataOutputStream output) throws IOException { + serializer.serializeVarInt(output, type.getCurrentId()); + } + + public void read(PacketType type, DataInputStream input) throws IOException { + // Note - we don't read the packet id + if (this.type != type) { + throw new IllegalArgumentException("Unexpected type: " + type); + } + } } } \ No newline at end of file diff --git a/ProtocolLib/src/test/java/com/comphenix/integration/protocol/TestPingPacket.java b/ProtocolLib/src/test/java/com/comphenix/integration/protocol/TestPingPacket.java index 9f05fede..6d7db6c1 100644 --- a/ProtocolLib/src/test/java/com/comphenix/integration/protocol/TestPingPacket.java +++ b/ProtocolLib/src/test/java/com/comphenix/integration/protocol/TestPingPacket.java @@ -10,18 +10,14 @@ import java.util.concurrent.TimeUnit; import org.bukkit.plugin.Plugin; -import com.comphenix.protocol.Packets; +import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.events.ConnectionSide; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.injector.GamePhase; -import com.comphenix.protocol.utility.MinecraftVersion; public class TestPingPacket { // Current versions - private static final String CRAFTBUKKIT_VERSION = "1.6.2"; - private static final int PROTOCOL_VERSION = 74; + private static final int PROTOCOL_VERSION = 4; // Timeout private static final int TIMEOUT_PING_MS = 10000; @@ -52,7 +48,7 @@ public class TestPingPacket { // Make sure it's the same System.out.println("Server string: " + transmitted); - assertEquals(transmitted, source); + assertEquals(source, transmitted); } catch (ExecutionException e) { throw e.getCause(); } @@ -60,10 +56,10 @@ public class TestPingPacket { private Future testInterception(Plugin test) { ProtocolLibrary.getProtocolManager().addPacketListener( - new PacketAdapter(test, ConnectionSide.SERVER_SIDE, GamePhase.LOGIN, Packets.Server.KICK_DISCONNECT) { + new PacketAdapter(test, PacketType.Status.Server.OUT_SERVER_INFO) { @Override public void onPacketSending(PacketEvent event) { - source = event.getPacket().getStrings().read(0); + source = event.getPacket().getServerPings().read(0).toJson(); } }); @@ -71,8 +67,7 @@ public class TestPingPacket { return Executors.newSingleThreadExecutor().submit(new Callable() { @Override public String call() throws Exception { - SimpleMinecraftClient client = new SimpleMinecraftClient( - new MinecraftVersion(CRAFTBUKKIT_VERSION), PROTOCOL_VERSION); + SimpleMinecraftClient client = new SimpleMinecraftClient(PROTOCOL_VERSION); String information = client.queryLocalPing(); // Wait for the listener to catch up diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 00554cae..d55241be 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -41,6 +41,7 @@ import org.bukkit.potion.PotionEffectType; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import com.comphenix.protocol.BukkitInitialization; @@ -50,7 +51,6 @@ import com.comphenix.protocol.PacketType.Sender; import com.comphenix.protocol.injector.PacketConstructor; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.StructureModifier; -import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; @@ -65,6 +65,7 @@ import com.google.common.collect.Lists; // Ensure that the CraftItemFactory is mockable @RunWith(org.powermock.modules.junit4.PowerMockRunner.class) +@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" }) @PrepareForTest(CraftItemFactory.class) public class PacketContainerTest { // Helper converters diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/injector/PluginVerifierTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/injector/PluginVerifierTest.java index daa3cacd..26d8dda8 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/injector/PluginVerifierTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/injector/PluginVerifierTest.java @@ -18,6 +18,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import com.comphenix.protocol.injector.PluginVerifier.VerificationResult; import com.google.common.base.Objects; @@ -25,6 +26,7 @@ import com.google.common.collect.Lists; // Damn final classes @RunWith(org.powermock.modules.junit4.PowerMockRunner.class) +@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" }) @PrepareForTest(PluginDescriptionFile.class) public class PluginVerifierTest { @Test diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/utility/StreamSerializerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/utility/StreamSerializerTest.java index 3ce104da..c16ba518 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/utility/StreamSerializerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/utility/StreamSerializerTest.java @@ -16,6 +16,7 @@ import org.bukkit.inventory.ItemStack; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import com.comphenix.protocol.BukkitInitialization; @@ -23,6 +24,7 @@ import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; @RunWith(org.powermock.modules.junit4.PowerMockRunner.class) +@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" }) @PrepareForTest(CraftItemFactory.class) public class StreamSerializerTest { @BeforeClass diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java index ea1059fd..5714dd80 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/WrappedWatchableObjectTest.java @@ -8,11 +8,13 @@ import org.bukkit.inventory.ItemStack; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import com.comphenix.protocol.BukkitInitialization; @RunWith(org.powermock.modules.junit4.PowerMockRunner.class) +@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" }) @PrepareForTest(CraftItemFactory.class) public class WrappedWatchableObjectTest { @BeforeClass diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java index a4236afc..ea050296 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java @@ -32,6 +32,7 @@ import org.bukkit.inventory.ItemStack; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import com.comphenix.protocol.BukkitInitialization; @@ -39,6 +40,7 @@ import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer; @RunWith(org.powermock.modules.junit4.PowerMockRunner.class) +@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" }) @PrepareForTest(CraftItemFactory.class) public class NbtFactoryTest { @BeforeClass