Print a hex dump in the case of very large arrays.
Dieser Commit ist enthalten in:
Ursprung
d8e8a88076
Commit
2244f986bb
@ -49,6 +49,7 @@ import com.comphenix.protocol.reflect.EquivalentConverter;
|
|||||||
import com.comphenix.protocol.reflect.PrettyPrinter;
|
import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||||
import com.comphenix.protocol.reflect.PrettyPrinter.ObjectPrinter;
|
import com.comphenix.protocol.reflect.PrettyPrinter.ObjectPrinter;
|
||||||
import com.comphenix.protocol.utility.ChatExtensions;
|
import com.comphenix.protocol.utility.ChatExtensions;
|
||||||
|
import com.comphenix.protocol.utility.HexDumper;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||||
import com.google.common.collect.MapMaker;
|
import com.google.common.collect.MapMaker;
|
||||||
@ -76,6 +77,11 @@ class CommandPacket extends CommandBase {
|
|||||||
*/
|
*/
|
||||||
public static final int PAGE_LINE_COUNT = 9;
|
public static final int PAGE_LINE_COUNT = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of bytes before we do a hex dump.
|
||||||
|
*/
|
||||||
|
private static final int HEX_DUMP_THRESHOLD = 256;
|
||||||
|
|
||||||
private Plugin plugin;
|
private Plugin plugin;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
private ProtocolManager manager;
|
private ProtocolManager manager;
|
||||||
@ -465,9 +471,19 @@ class CommandPacket extends CommandBase {
|
|||||||
return PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass(), PrettyPrinter.RECURSE_DEPTH, new ObjectPrinter() {
|
return PrettyPrinter.printObject(packet, clazz, MinecraftReflection.getPacketClass(), PrettyPrinter.RECURSE_DEPTH, new ObjectPrinter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean print(StringBuilder output, Object value) {
|
public boolean print(StringBuilder output, Object value) {
|
||||||
if (value != null) {
|
// Special case
|
||||||
EquivalentConverter<Object> converter = findConverter(value.getClass());
|
if (value instanceof byte[]) {
|
||||||
|
byte[] data = (byte[]) value;
|
||||||
|
|
||||||
|
if (data.length > HEX_DUMP_THRESHOLD) {
|
||||||
|
output.append("[");
|
||||||
|
HexDumper.defaultDumper().appendTo(output, data);
|
||||||
|
output.append("]");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (value != null) {
|
||||||
|
EquivalentConverter<Object> converter = findConverter(value.getClass());
|
||||||
|
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
output.append(converter.getSpecific(value));
|
output.append(converter.getSpecific(value));
|
||||||
return true;
|
return true;
|
||||||
|
234
ProtocolLib/src/main/java/com/comphenix/protocol/utility/HexDumper.java
Normale Datei
234
ProtocolLib/src/main/java/com/comphenix/protocol/utility/HexDumper.java
Normale Datei
@ -0,0 +1,234 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a class for printing hexadecimal dumps.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class HexDumper {
|
||||||
|
private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
private int positionLength = 6;
|
||||||
|
private char[] positionSuffix = ": ".toCharArray();
|
||||||
|
private char[] delimiter = " ".toCharArray();
|
||||||
|
private int groupLength = 2;
|
||||||
|
private int groupCount = 24;
|
||||||
|
private char[] lineDelimiter = "\n".toCharArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a hex dumper tuned for lines of 80 characters:
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <th>Property</th>
|
||||||
|
* <th>Value</th>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Position Length</td>
|
||||||
|
* <td>6</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Position Suffix</td>
|
||||||
|
* <td>": "</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Delimiter</td>
|
||||||
|
* <td>" "</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Group Length</td>
|
||||||
|
* <td>2</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Group Count</td>
|
||||||
|
* <td>24</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Line Delimiter</td>
|
||||||
|
* <td>"\n"</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* @return The default dumper.
|
||||||
|
*/
|
||||||
|
public static HexDumper defaultDumper() {
|
||||||
|
return new HexDumper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the delimiter between each new line.
|
||||||
|
* @param lineDelimiter - the line delimiter.
|
||||||
|
* @return This instance, for chaining.
|
||||||
|
*/
|
||||||
|
public HexDumper lineDelimiter(String lineDelimiter) {
|
||||||
|
this.lineDelimiter = Preconditions.checkNotNull(lineDelimiter, "lineDelimiter cannot be NULL").toCharArray();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the number of hex characters in the position.
|
||||||
|
* @param positionLength - number of characters, from 0 to 8.
|
||||||
|
* @return This instance, for chaining.
|
||||||
|
*/
|
||||||
|
public HexDumper positionLength(int positionLength) {
|
||||||
|
if (positionLength < 0)
|
||||||
|
throw new IllegalArgumentException("positionLength cannot be less than zero.");
|
||||||
|
if (positionLength > 8)
|
||||||
|
throw new IllegalArgumentException("positionLength cannot be greater than eight.");
|
||||||
|
this.positionLength = positionLength;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a suffix to write after each position.
|
||||||
|
* @param positionSuffix - non-null string to write after the positions.
|
||||||
|
* @return This instance, for chaining.
|
||||||
|
*/
|
||||||
|
public HexDumper positionSuffix(String positionSuffix) {
|
||||||
|
this.positionSuffix = Preconditions.checkNotNull(positionSuffix, "positionSuffix cannot be NULL").toCharArray();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the delimiter to write in between each group of hexadecimal characters.
|
||||||
|
* @param delimiter - non-null string to write between each group.
|
||||||
|
* @return This instance, for chaining.
|
||||||
|
*/
|
||||||
|
public HexDumper delimiter(String delimiter) {
|
||||||
|
this.delimiter = Preconditions.checkNotNull(delimiter, "delimiter cannot be NULL").toCharArray();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the length of each group in hexadecimal characters.
|
||||||
|
* @param groupLength - the length of each group.
|
||||||
|
* @return This instance, for chaining.
|
||||||
|
*/
|
||||||
|
public HexDumper groupLength(int groupLength) {
|
||||||
|
if (groupLength < 1)
|
||||||
|
throw new IllegalArgumentException("groupLength cannot be less than one.");
|
||||||
|
this.groupLength = groupLength;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the number of groups in each line. This is limited by the supply of bytes in the byte array.
|
||||||
|
* <p>
|
||||||
|
* Use {@link Integer#MAX_VALUE} to effectively disable lines.
|
||||||
|
* @param groupLength - the length of each group.
|
||||||
|
* @return This instance, for chaining.
|
||||||
|
*/
|
||||||
|
public HexDumper groupCount(int groupCount) {
|
||||||
|
if (groupCount < 1)
|
||||||
|
throw new IllegalArgumentException("groupCount cannot be less than one.");
|
||||||
|
this.groupCount = groupCount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the hex dump of the given data to the string builder, using the current formatting settings.
|
||||||
|
* @param appendable - appendable source.
|
||||||
|
* @param data - the data to dump.
|
||||||
|
* @param start - the starting index of the data.
|
||||||
|
* @param length - the number of bytes to dump.
|
||||||
|
* @throws IOException Any underlying IO exception.
|
||||||
|
*/
|
||||||
|
public void appendTo(Appendable appendable, byte[] data) throws IOException {
|
||||||
|
appendTo(appendable, data, 0, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the hex dump of the given data to the string builder, using the current formatting settings.
|
||||||
|
* @param appendable - appendable source.
|
||||||
|
* @param data - the data to dump.
|
||||||
|
* @param start - the starting index of the data.
|
||||||
|
* @param length - the number of bytes to dump.
|
||||||
|
* @throws IOException Any underlying IO exception.
|
||||||
|
*/
|
||||||
|
public void appendTo(Appendable appendable, byte[] data, int start, int length) throws IOException {
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
appendTo(output, data, start, length);
|
||||||
|
appendable.append(output.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the hex dump of the given data to the string builder, using the current formatting settings.
|
||||||
|
* @param builder - the builder.
|
||||||
|
* @param data - the data to dump.
|
||||||
|
* @param start - the starting index of the data.
|
||||||
|
* @param length - the number of bytes to dump.
|
||||||
|
*/
|
||||||
|
public void appendTo(StringBuilder builder, byte[] data) {
|
||||||
|
appendTo(builder, data, 0, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the hex dump of the given data to the string builder, using the current formatting settings.
|
||||||
|
* @param builder - the builder.
|
||||||
|
* @param data - the data to dump.
|
||||||
|
* @param start - the starting index of the data.
|
||||||
|
* @param length - the number of bytes to dump.
|
||||||
|
*/
|
||||||
|
public void appendTo(StringBuilder builder, byte[] data, int start, int length) {
|
||||||
|
// Positions
|
||||||
|
int dataIndex = start;
|
||||||
|
int dataEnd = start + length;
|
||||||
|
int groupCounter = 0;
|
||||||
|
int currentGroupLength = 0;
|
||||||
|
|
||||||
|
// Current niblet in the byte
|
||||||
|
int value = 0;
|
||||||
|
boolean highNiblet = true;
|
||||||
|
|
||||||
|
while (dataIndex < dataEnd || !highNiblet) {
|
||||||
|
// Prefix
|
||||||
|
if (groupCounter == 0 && currentGroupLength == 0) {
|
||||||
|
// Print the current dataIndex (print in reverse)
|
||||||
|
for (int i = positionLength - 1; i >= 0; i--) {
|
||||||
|
builder.append(HEX_DIGITS[(dataIndex >>> (4 * i)) & 0xF]);
|
||||||
|
}
|
||||||
|
builder.append(positionSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print niblet
|
||||||
|
if (highNiblet) {
|
||||||
|
value = data[dataIndex++] & 0xFF;
|
||||||
|
builder.append(HEX_DIGITS[value >>> 4]);
|
||||||
|
} else {
|
||||||
|
builder.append(HEX_DIGITS[value & 0x0F]);
|
||||||
|
}
|
||||||
|
highNiblet = !highNiblet;
|
||||||
|
currentGroupLength++;
|
||||||
|
|
||||||
|
// See if we're dealing with the last element
|
||||||
|
if (currentGroupLength >= groupLength) {
|
||||||
|
currentGroupLength = 0;
|
||||||
|
|
||||||
|
// See if we've reached the last element in the line
|
||||||
|
if (++groupCounter >= groupCount) {
|
||||||
|
builder.append(lineDelimiter);
|
||||||
|
groupCounter = 0;
|
||||||
|
} else {
|
||||||
|
// Write delimiter
|
||||||
|
builder.append(delimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the length of each line.
|
||||||
|
* @param byteCount - the maximum number of bytes
|
||||||
|
* @return The lenght of the final line.
|
||||||
|
*/
|
||||||
|
public int getLineLength(int byteCount) {
|
||||||
|
int constant = positionLength + positionSuffix.length + lineDelimiter.length;
|
||||||
|
int groups = Math.min((2 * byteCount) / groupLength, groupCount);
|
||||||
|
|
||||||
|
// Total expected length of each line
|
||||||
|
return constant + delimiter.length * (groups - 1) + groupLength * groups;
|
||||||
|
}
|
||||||
|
}
|
@ -627,6 +627,18 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the World (NMS) class.
|
||||||
|
* @return The world class.
|
||||||
|
*/
|
||||||
|
public static Class<?> getNmsWorldClass() {
|
||||||
|
try {
|
||||||
|
return getMinecraftClass("World");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return setMinecraftClass("World", getWorldServerClass().getSuperclass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fallback on the return value of a named method in order to get a NMS class.
|
* Fallback on the return value of a named method in order to get a NMS class.
|
||||||
* @param nmsClass - the expected name of the Minecraft class.
|
* @param nmsClass - the expected name of the Minecraft class.
|
||||||
@ -1675,6 +1687,14 @@ public class MinecraftReflection {
|
|||||||
return getCraftBukkitClass("entity.CraftPlayer");
|
return getCraftBukkitClass("entity.CraftPlayer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the CraftWorld class.
|
||||||
|
* @return The CraftWorld class.
|
||||||
|
*/
|
||||||
|
public static Class<?> getCraftWorldClass() {
|
||||||
|
return getCraftBukkitClass("CraftWorld");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the CraftEntity class.
|
* Retrieve the CraftEntity class.
|
||||||
* @return CraftEntity class.
|
* @return CraftEntity class.
|
||||||
|
@ -28,6 +28,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.WorldType;
|
import org.bukkit.WorldType;
|
||||||
@ -39,6 +40,7 @@ import org.bukkit.potion.PotionEffectType;
|
|||||||
import com.comphenix.protocol.PacketType;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||||
import com.comphenix.protocol.injector.PacketConstructor;
|
import com.comphenix.protocol.injector.PacketConstructor;
|
||||||
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
|
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
|
||||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
@ -46,6 +48,7 @@ import com.comphenix.protocol.reflect.FieldAccessException;
|
|||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||||
@ -86,6 +89,9 @@ public class BukkitConverters {
|
|||||||
private static volatile Constructor<?> mobEffectConstructor;
|
private static volatile Constructor<?> mobEffectConstructor;
|
||||||
private static volatile StructureModifier<Object> mobEffectModifier;
|
private static volatile StructureModifier<Object> mobEffectModifier;
|
||||||
|
|
||||||
|
// Used for fetching the CraftWorld associated with a WorldServer
|
||||||
|
private static FieldAccessor craftWorldField;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
MinecraftReflection.getWorldTypeClass();
|
MinecraftReflection.getWorldTypeClass();
|
||||||
@ -98,6 +104,15 @@ public class BukkitConverters {
|
|||||||
hasAttributeSnapshot = true;
|
hasAttributeSnapshot = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch CraftWorld field
|
||||||
|
try {
|
||||||
|
craftWorldField = Accessors.getFieldAccessor(
|
||||||
|
MinecraftReflection.getNmsWorldClass(),
|
||||||
|
MinecraftReflection.getCraftWorldClass(), true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -711,6 +726,29 @@ public class BukkitConverters {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the converter used to convert between a NMS World and a Bukkit world.
|
||||||
|
* @return The potion effect converter.
|
||||||
|
*/
|
||||||
|
public static EquivalentConverter<World> getWorldConverter() {
|
||||||
|
return new IgnoreNullConverter<World>() {
|
||||||
|
@Override
|
||||||
|
protected Object getGenericValue(Class<?> genericType, World specific) {
|
||||||
|
return BukkitUnwrapper.getInstance().unwrapItem(specific);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected World getSpecificValue(Object generic) {
|
||||||
|
return (World) craftWorldField.get(generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<World> getSpecificType() {
|
||||||
|
return World.class;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the converter used to convert between a PotionEffect and the equivalent NMS Mobeffect.
|
* Retrieve the converter used to convert between a PotionEffect and the equivalent NMS Mobeffect.
|
||||||
* @return The potion effect converter.
|
* @return The potion effect converter.
|
||||||
@ -827,8 +865,9 @@ public class BukkitConverters {
|
|||||||
put(NbtBase.class, (EquivalentConverter) getNbtConverter()).
|
put(NbtBase.class, (EquivalentConverter) getNbtConverter()).
|
||||||
put(NbtCompound.class, (EquivalentConverter) getNbtConverter()).
|
put(NbtCompound.class, (EquivalentConverter) getNbtConverter()).
|
||||||
put(WrappedWatchableObject.class, (EquivalentConverter) getWatchableObjectConverter()).
|
put(WrappedWatchableObject.class, (EquivalentConverter) getWatchableObjectConverter()).
|
||||||
put(PotionEffect.class, (EquivalentConverter) getPotionEffectConverter());
|
put(PotionEffect.class, (EquivalentConverter) getPotionEffectConverter()).
|
||||||
|
put(World.class, (EquivalentConverter) getWorldConverter());
|
||||||
|
|
||||||
// Types added in 1.7.2
|
// Types added in 1.7.2
|
||||||
if (MinecraftReflection.isUsingNetty()) {
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
builder.put(Material.class, (EquivalentConverter) getBlockConverter());
|
builder.put(Material.class, (EquivalentConverter) getBlockConverter());
|
||||||
@ -866,7 +905,8 @@ public class BukkitConverters {
|
|||||||
put(MinecraftReflection.getNBTBaseClass(), (EquivalentConverter) getNbtConverter()).
|
put(MinecraftReflection.getNBTBaseClass(), (EquivalentConverter) getNbtConverter()).
|
||||||
put(MinecraftReflection.getNBTCompoundClass(), (EquivalentConverter) getNbtConverter()).
|
put(MinecraftReflection.getNBTCompoundClass(), (EquivalentConverter) getNbtConverter()).
|
||||||
put(MinecraftReflection.getWatchableObjectClass(), (EquivalentConverter) getWatchableObjectConverter()).
|
put(MinecraftReflection.getWatchableObjectClass(), (EquivalentConverter) getWatchableObjectConverter()).
|
||||||
put(MinecraftReflection.getMobEffectClass(), (EquivalentConverter) getPotionEffectConverter());
|
put(MinecraftReflection.getMobEffectClass(), (EquivalentConverter) getPotionEffectConverter()).
|
||||||
|
put(MinecraftReflection.getNmsWorldClass(), (EquivalentConverter) getWorldConverter());
|
||||||
|
|
||||||
if (hasWorldType)
|
if (hasWorldType)
|
||||||
builder.put(MinecraftReflection.getWorldTypeClass(), (EquivalentConverter) getWorldTypeConverter());
|
builder.put(MinecraftReflection.getWorldTypeClass(), (EquivalentConverter) getWorldTypeConverter());
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren