Add support for the new JSON chat message format. FIXES Ticket-109
In addition, the PacketConstructor can now handle non-wrapped objects, along with Class types instead of instances in withPacket().
Dieser Commit ist enthalten in:
Ursprung
0b56df20d5
Commit
6054d559e1
@ -0,0 +1,43 @@
|
|||||||
|
package com.comphenix.protocol.error;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.error.Report.ReportBuilder;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an error reporter that rethrows every exception instead.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class RethrowErrorReporter implements ErrorReporter {
|
||||||
|
@Override
|
||||||
|
public void reportMinimal(Plugin sender, String methodName, Throwable error) {
|
||||||
|
throw new RuntimeException("Minimal error by " + sender + " in " + methodName, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportMinimal(Plugin sender, String methodName, Throwable error, Object... parameters) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Minimal error by " + sender + " in " + methodName + " with " + Joiner.on(",").join(parameters), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportWarning(Object sender, ReportBuilder reportBuilder) {
|
||||||
|
reportWarning(sender, reportBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportWarning(Object sender, Report report) {
|
||||||
|
throw new RuntimeException("Warning by " + sender + ": " + report);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportDetailed(Object sender, ReportBuilder reportBuilder) {
|
||||||
|
reportDetailed(sender, reportBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reportDetailed(Object sender, Report report) {
|
||||||
|
throw new RuntimeException("Detailed error " + sender + ": " + report, report.getException());
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.error.RethrowErrorReporter;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
@ -51,15 +52,19 @@ public class PacketConstructor {
|
|||||||
// Used to unwrap Bukkit objects
|
// Used to unwrap Bukkit objects
|
||||||
private List<Unwrapper> unwrappers;
|
private List<Unwrapper> unwrappers;
|
||||||
|
|
||||||
|
// Parameters that need to be unwrapped
|
||||||
|
private boolean[] unwrappable;
|
||||||
|
|
||||||
private PacketConstructor(Constructor<?> constructorMethod) {
|
private PacketConstructor(Constructor<?> constructorMethod) {
|
||||||
this.constructorMethod = constructorMethod;
|
this.constructorMethod = constructorMethod;
|
||||||
this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper());
|
this.unwrappers = Lists.newArrayList((Unwrapper) new BukkitUnwrapper(new RethrowErrorReporter() ));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PacketConstructor(int packetID, Constructor<?> constructorMethod, List<Unwrapper> unwrappers) {
|
private PacketConstructor(int packetID, Constructor<?> constructorMethod, List<Unwrapper> unwrappers, boolean[] unwrappable) {
|
||||||
this.packetID = packetID;
|
this.packetID = packetID;
|
||||||
this.constructorMethod = constructorMethod;
|
this.constructorMethod = constructorMethod;
|
||||||
this.unwrappers = unwrappers;
|
this.unwrappers = unwrappers;
|
||||||
|
this.unwrappable = unwrappable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableList<Unwrapper> getUnwrappers() {
|
public ImmutableList<Unwrapper> getUnwrappers() {
|
||||||
@ -80,31 +85,41 @@ public class PacketConstructor {
|
|||||||
* @return A constructor with a different set of unwrappers.
|
* @return A constructor with a different set of unwrappers.
|
||||||
*/
|
*/
|
||||||
public PacketConstructor withUnwrappers(List<Unwrapper> unwrappers) {
|
public PacketConstructor withUnwrappers(List<Unwrapper> unwrappers) {
|
||||||
return new PacketConstructor(packetID, constructorMethod, unwrappers);
|
return new PacketConstructor(packetID, constructorMethod, unwrappers, unwrappable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a packet constructor that creates packets using the given types.
|
* Create a packet constructor that creates packets using the given types.
|
||||||
|
* <p>
|
||||||
|
* Note that if you pass a Class<?> as a value, it will use its type directly.
|
||||||
* @param id - packet ID.
|
* @param id - packet ID.
|
||||||
* @param values - types to create.
|
* @param values - the values that will match each parameter in the desired constructor.
|
||||||
* @return A packet constructor with these types.
|
* @return A packet constructor with these types.
|
||||||
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
* @throws IllegalArgumentException If no packet constructor could be created with these types.
|
||||||
*/
|
*/
|
||||||
public PacketConstructor withPacket(int id, Object[] values) {
|
public PacketConstructor withPacket(int id, Object[] values) {
|
||||||
|
|
||||||
Class<?>[] types = new Class<?>[values.length];
|
Class<?>[] types = new Class<?>[values.length];
|
||||||
|
Throwable lastException = null;
|
||||||
|
boolean[] unwrappable = new boolean[values.length];
|
||||||
|
|
||||||
for (int i = 0; i < types.length; i++) {
|
for (int i = 0; i < types.length; i++) {
|
||||||
// Default type
|
// Default type
|
||||||
if (values[i] != null) {
|
if (values[i] != null) {
|
||||||
types[i] = values[i].getClass();
|
types[i] = (values[i] instanceof Class) ? (Class<?>)values[i] : values[i].getClass();
|
||||||
|
|
||||||
for (Unwrapper unwrapper : unwrappers) {
|
for (Unwrapper unwrapper : unwrappers) {
|
||||||
Object result = unwrapper.unwrapItem(values[i]);
|
Object result = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = unwrapper.unwrapItem(values[i]);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
lastException = e;
|
||||||
|
}
|
||||||
|
|
||||||
// Update type we're searching for
|
// Update type we're searching for
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
types[i] = result.getClass();
|
types[i] = result.getClass();
|
||||||
|
unwrappable[i] = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,11 +141,11 @@ public class PacketConstructor {
|
|||||||
|
|
||||||
if (isCompatible(types, params)) {
|
if (isCompatible(types, params)) {
|
||||||
// Right, we've found our type
|
// Right, we've found our type
|
||||||
return new PacketConstructor(id, constructor, unwrappers);
|
return new PacketConstructor(id, constructor, unwrappers, unwrappable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException("No suitable constructor could be found.");
|
throw new IllegalArgumentException("No suitable constructor could be found.", lastException);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,8 +158,9 @@ public class PacketConstructor {
|
|||||||
*/
|
*/
|
||||||
public PacketContainer createPacket(Object... values) throws FieldAccessException {
|
public PacketContainer createPacket(Object... values) throws FieldAccessException {
|
||||||
try {
|
try {
|
||||||
// Convert types
|
// Convert types that needs to be converted
|
||||||
for (int i = 0; i < values.length; i++) {
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
if (unwrappable[i]) {
|
||||||
for (Unwrapper unwrapper : unwrappers) {
|
for (Unwrapper unwrapper : unwrappers) {
|
||||||
Object converted = unwrapper.unwrapItem(values[i]);
|
Object converted = unwrapper.unwrapItem(values[i]);
|
||||||
|
|
||||||
@ -154,6 +170,7 @@ public class PacketConstructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Object nmsPacket = constructorMethod.newInstance(values);
|
Object nmsPacket = constructorMethod.newInstance(values);
|
||||||
return new PacketContainer(packetID, nmsPacket);
|
return new PacketContainer(packetID, nmsPacket);
|
||||||
|
@ -17,7 +17,11 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
@ -26,8 +30,13 @@ import org.bukkit.entity.Player;
|
|||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
import com.comphenix.protocol.injector.PacketConstructor;
|
import com.comphenix.protocol.injector.PacketConstructor;
|
||||||
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for sending chat messages.
|
* Utility methods for sending chat messages.
|
||||||
@ -35,11 +44,14 @@ import com.google.common.base.Strings;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class ChatExtensions {
|
public class ChatExtensions {
|
||||||
|
|
||||||
// Used to sent chat messages
|
// Used to sent chat messages
|
||||||
private PacketConstructor chatConstructor;
|
private PacketConstructor chatConstructor;
|
||||||
private ProtocolManager manager;
|
private ProtocolManager manager;
|
||||||
|
|
||||||
|
// Whether or not we have to use the post-1.6.1 chat format
|
||||||
|
private static Constructor<?> jsonConstructor = getJsonFormatConstructor();
|
||||||
|
private static Method messageFactory;
|
||||||
|
|
||||||
public ChatExtensions(ProtocolManager manager) {
|
public ChatExtensions(ProtocolManager manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
@ -68,10 +80,59 @@ public class ChatExtensions {
|
|||||||
* Send a message without invoking the packet listeners.
|
* Send a message without invoking the packet listeners.
|
||||||
* @param player - the player to send it to.
|
* @param player - the player to send it to.
|
||||||
* @param message - the message to send.
|
* @param message - the message to send.
|
||||||
* @return TRUE if the message was sent successfully, FALSE otherwise.
|
|
||||||
* @throws InvocationTargetException If we were unable to send the message.
|
* @throws InvocationTargetException If we were unable to send the message.
|
||||||
*/
|
*/
|
||||||
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
|
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
|
||||||
|
if (jsonConstructor != null) {
|
||||||
|
sendMessageAsJson(player, message);
|
||||||
|
} else {
|
||||||
|
sendMessageAsString(player, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message using the new JSON format in 1.6.1.
|
||||||
|
* @param player - the player to send it to.
|
||||||
|
* @param message - the message to send.
|
||||||
|
* @throws InvocationTargetException InvocationTargetException If we were unable to send the message.
|
||||||
|
*/
|
||||||
|
private void sendMessageAsJson(Player player, String message) throws InvocationTargetException {
|
||||||
|
Object messageObject = null;
|
||||||
|
|
||||||
|
if (chatConstructor == null) {
|
||||||
|
Class<?> messageClass = jsonConstructor.getParameterTypes()[0];
|
||||||
|
chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, messageClass);
|
||||||
|
|
||||||
|
// Try one of the string constructors
|
||||||
|
messageFactory = FuzzyReflection.fromClass(messageClass).getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
requireModifier(Modifier.STATIC).
|
||||||
|
parameterCount(1).
|
||||||
|
parameterExactType(String.class).
|
||||||
|
returnTypeMatches(FuzzyMatchers.matchParent()).
|
||||||
|
build());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageObject = messageFactory.invoke(null, message);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new InvocationTargetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
manager.sendServerPacket(player, chatConstructor.createPacket(messageObject), false);
|
||||||
|
} catch (FieldAccessException e) {
|
||||||
|
throw new InvocationTargetException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message as a pure string.
|
||||||
|
* @param player - the player.
|
||||||
|
* @param message - the message to send.
|
||||||
|
* @throws InvocationTargetException If anything went wrong.
|
||||||
|
*/
|
||||||
|
private void sendMessageAsString(Player player, String message) throws InvocationTargetException {
|
||||||
if (chatConstructor == null)
|
if (chatConstructor == null)
|
||||||
chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, message);
|
chatConstructor = manager.createPacketConstructor(Packets.Server.CHAT, message);
|
||||||
|
|
||||||
@ -143,4 +204,21 @@ public class ChatExtensions {
|
|||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a constructor for post-1.6.1 chat packets.
|
||||||
|
* @return A constructor for JSON-based packets.
|
||||||
|
*/
|
||||||
|
static Constructor<?> getJsonFormatConstructor() {
|
||||||
|
Class<?> chatPacket = PacketRegistry.getPacketClassFromID(3, true);
|
||||||
|
List<Constructor<?>> list = FuzzyReflection.fromClass(chatPacket).getConstructorList(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(1).
|
||||||
|
parameterMatches(MinecraftReflection.getMinecraftObjectMatcher()).
|
||||||
|
build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// First element or NULL
|
||||||
|
return Iterables.getFirst(list, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren