Add support for using multiple lines in WrappedServerPing.

This commit is contained in:
Kristian S. Stangeland 2014-01-09 20:01:37 +01:00
parent 75fe2c6db9
commit a902c1f186
6 changed files with 110 additions and 6 deletions

View File

@ -4,25 +4,50 @@ import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor; import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor; import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.sf.cglib.asm.ClassReader; import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.MethodVisitor; import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type; import net.sf.cglib.asm.Type;
public class ClassAnalyser { public class ClassAnalyser {
/** /**
* Represents a method in ASM. * Represents a method in ASM.
* <p>
* Keep in mind that this may also invoke a constructor.
* @author Kristian * @author Kristian
*/ */
public static class AsmMethod { public static class AsmMethod {
public enum AsmOpcodes {
INVOKE_VIRTUAL,
INVOKE_SPECIAL,
INVOKE_STATIC,
INVOKE_INTERFACE,
INVOKE_DYNAMIC;
public static AsmOpcodes fromIntOpcode(int opcode) {
switch (opcode) {
case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
}
}
}
private final AsmOpcodes opcode;
private final String ownerClass; private final String ownerClass;
private final String methodName; private final String methodName;
private final String signature; private final String signature;
public AsmMethod(String ownerClass, String methodName, String signature) { public AsmMethod(AsmOpcodes opcode, String ownerClass, String methodName, String signature) {
this.opcode = opcode;
this.ownerClass = ownerClass; this.ownerClass = ownerClass;
this.methodName = methodName; this.methodName = methodName;
this.signature = signature; this.signature = signature;
@ -32,6 +57,14 @@ public class ClassAnalyser {
return ownerClass; return ownerClass;
} }
/**
* Retrieve the opcode used to invoke this method or constructor.
* @return The opcode.
*/
public AsmOpcodes getOpcode() {
return opcode;
}
/** /**
* Retrieve the associated owner class. * Retrieve the associated owner class.
* @return The owner class. * @return The owner class.
@ -92,7 +125,7 @@ public class ClassAnalyser {
return new EmptyMethodVisitor() { return new EmptyMethodVisitor() {
@Override @Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) { public void visitMethodInsn(int opcode, String owner, String name, String desc) {
output.add(new AsmMethod(owner, name, desc)); output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
} }
}; };
} }

View File

@ -670,6 +670,33 @@ public class MinecraftReflection {
} }
} }
/**
* Retrieve the NMS chat component text class.
* @return The chat component class.
*/
public static Class<?> getChatComponentTextClass() {
try {
return getMinecraftClass("ChatComponentText");
} catch (RuntimeException e) {
try {
Method getScoreboardDisplayName = FuzzyReflection.fromClass(getEntityClass()).
getMethodByParameters("getScoreboardDisplayName", getIChatBaseComponentClass(), new Class<?>[0]);
Class<?> baseClass = getIChatBaseComponentClass();
for (AsmMethod method : ClassAnalyser.getDefault().getMethodCalls(getScoreboardDisplayName)) {
Class<?> owner = method.getOwnerClass();
if (isMinecraftClass(owner) && baseClass.isAssignableFrom(owner)) {
return setMinecraftClass("ChatComponentText", owner);
}
}
} catch (Exception e1) {
throw new IllegalStateException("Cannot find ChatComponentText class.", e);
}
}
throw new IllegalStateException("Cannot find ChatComponentText class.");
}
/** /**
* Attempt to find the ChatSerializer class. * Attempt to find the ChatSerializer class.
* @return The serializer class. * @return The serializer class.

View File

@ -4,8 +4,10 @@ import org.bukkit.ChatColor;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Preconditions;
/** /**
* Represents a chat component added in Minecraft 1.7.2 * Represents a chat component added in Minecraft 1.7.2
@ -17,6 +19,7 @@ public class WrappedChatComponent extends AbstractWrapper {
private static MethodAccessor SERIALIZE_COMPONENT = null; private static MethodAccessor SERIALIZE_COMPONENT = null;
private static MethodAccessor DESERIALIZE_COMPONENT = null; private static MethodAccessor DESERIALIZE_COMPONENT = null;
private static MethodAccessor CONSTRUCT_COMPONENT = null; private static MethodAccessor CONSTRUCT_COMPONENT = null;
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
static { static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER); FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER);
@ -30,6 +33,10 @@ public class WrappedChatComponent extends AbstractWrapper {
// Get a component from a standard Minecraft message // Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor( CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(
MinecraftReflection.getCraftChatMessage(), "fromString", String.class); MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
// And the component text constructor
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(
MinecraftReflection.getChatComponentTextClass(), String.class);
} }
private transient String cache; private transient String cache;
@ -58,6 +65,16 @@ public class WrappedChatComponent extends AbstractWrapper {
return new WrappedChatComponent(DESERIALIZE_COMPONENT.invoke(null, json), json); return new WrappedChatComponent(DESERIALIZE_COMPONENT.invoke(null, json), json);
} }
/**
* Construct a wrapper around a new text chat component with the given text.
* @param text - the text of the text chat component.
* @return The wrapper around the new chat component.
*/
public static WrappedChatComponent fromText(String text) {
Preconditions.checkNotNull(text, "text cannot be NULL.");
return fromHandle(CONSTRUCT_TEXT_COMPONENT.invoke(text));
}
/** /**
* Construct an array of chat components from a standard Minecraft message. * Construct an array of chat components from a standard Minecraft message.
* <p> * <p>

View File

@ -135,12 +135,10 @@ public class WrappedServerPing extends AbstractWrapper {
/** /**
* Set the message of the day. * Set the message of the day.
* <p>
* <b>Warning:</b> Only the first line will be transmitted.
* @param description - the message. * @param description - the message.
*/ */
public void setMotD(String message) { public void setMotD(String message) {
setMotD(WrappedChatComponent.fromChatMessage(message)[0]); setMotD(WrappedChatComponent.fromText(message));
} }
/** /**

View File

@ -2,6 +2,7 @@ package com.comphenix.protocol.utility;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import net.minecraft.server.v1_7_R1.ChatComponentText;
import net.minecraft.server.v1_7_R1.ChatSerializer; import net.minecraft.server.v1_7_R1.ChatSerializer;
import net.minecraft.server.v1_7_R1.ChunkCoordIntPair; import net.minecraft.server.v1_7_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_7_R1.IChatBaseComponent; import net.minecraft.server.v1_7_R1.IChatBaseComponent;
@ -44,6 +45,11 @@ public class MinecraftReflectionTest {
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass()); assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
} }
@Test
public void testChatComponentText() {
assertEquals(ChatComponentText.class, MinecraftReflection.getChatComponentTextClass());
}
@Test @Test
public void testChatSerializer() { public void testChatSerializer() {
assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass()); assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass());

View File

@ -0,0 +1,23 @@
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
public class WrappedChatComponentTest {
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
BukkitInitialization.initializePackage();
}
@Test
public void testText() {
WrappedChatComponent test = WrappedChatComponent.fromText("Hello.");
String json = test.getJson();
assertNotNull(json);
}
}