mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-12-28 03:57:33 +01:00
Added support for serializing/deserializing WrappedServerPing to JSON.
This commit is contained in:
parent
752807fa5f
commit
b59f55e2e5
@ -12,7 +12,7 @@
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="src/test/resources"/>
|
||||
<classpathentry including="**/*.java" kind="src" path="src/test/resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
|
@ -2,4 +2,5 @@ eclipse.preferences.version=1
|
||||
encoding//src/main/java=cp1252
|
||||
encoding//src/main/resources=cp1252
|
||||
encoding//src/test/java=cp1252
|
||||
encoding//src/test/resources=cp1252
|
||||
encoding/<project>=cp1252
|
||||
|
@ -4,7 +4,6 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import com.comphenix.protocol.reflect.ExactReflection;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.google.common.base.Joiner;
|
||||
@ -106,6 +105,50 @@ public final class Accessors {
|
||||
field.setAccessible(true);
|
||||
return new DefaultFieldAccessor(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor that will cache the content of the field.
|
||||
* <p>
|
||||
* Note that we don't check if the underlying field has changed after the value has been cached,
|
||||
* so it's best to use this on final fields.
|
||||
* @param inner - the accessor.
|
||||
* @return A cached field accessor.
|
||||
*/
|
||||
public static FieldAccessor getCached(final FieldAccessor inner) {
|
||||
return new FieldAccessor() {
|
||||
private final Object EMPTY = new Object();
|
||||
private volatile Object value = EMPTY;
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
inner.set(instance, value);
|
||||
update(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
Object cache = value;
|
||||
|
||||
if (cache != EMPTY)
|
||||
return cache;
|
||||
return update(inner.get(instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cached value.
|
||||
* @param value - the value to cache.
|
||||
* @return The cached value.
|
||||
*/
|
||||
private Object update(Object value) {
|
||||
return this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return inner.getField();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a field accessor where the write operation is synchronized on the current field value.
|
||||
|
@ -78,6 +78,7 @@ import com.google.common.base.Joiner;
|
||||
public class MinecraftReflection {
|
||||
public static final ReportType REPORT_CANNOT_FIND_MCPC_REMAPPER = new ReportType("Cannot find MCPC remapper.");
|
||||
public static final ReportType REPORT_CANNOT_LOAD_CPC_REMAPPER = new ReportType("Unable to load MCPC remapper.");
|
||||
public static final ReportType REPORT_NON_CRAFTBUKKIT_LIBRARY_PACKAGE = new ReportType("Cannot find standard Minecraft library location. Assuming MCPC.");
|
||||
|
||||
/**
|
||||
* Regular expression that matches a Minecraft object.
|
||||
@ -101,7 +102,12 @@ public class MinecraftReflection {
|
||||
* The package name of all the classes that belongs to the native code in Minecraft.
|
||||
*/
|
||||
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
|
||||
|
||||
|
||||
/**
|
||||
* The package with all the library classes.
|
||||
*/
|
||||
private static String MINECRAFT_LIBRARY_PACKAGE = "net.minecraft.util";
|
||||
|
||||
/**
|
||||
* Represents a regular expression that will match the version string in a package:
|
||||
* org.bukkit.craftbukkit.v1_6_R2 -> v1_6_R2
|
||||
@ -114,6 +120,7 @@ public class MinecraftReflection {
|
||||
// Package private for the purpose of unit testing
|
||||
static CachedPackage minecraftPackage;
|
||||
static CachedPackage craftbukkitPackage;
|
||||
static CachedPackage libraryPackage;
|
||||
|
||||
// org.bukkit.craftbukkit
|
||||
private static Constructor<?> craftNMSConstructor;
|
||||
@ -199,6 +206,9 @@ public class MinecraftReflection {
|
||||
// Libigot patch
|
||||
handleLibigot();
|
||||
|
||||
// Minecraft library package
|
||||
handleLibraryPackage();
|
||||
|
||||
// Next, do the same for CraftEntity.getHandle() in order to get the correct Minecraft package
|
||||
Class<?> craftEntity = getCraftEntityClass();
|
||||
Method getHandle = craftEntity.getMethod("getHandle");
|
||||
@ -244,7 +254,30 @@ public class MinecraftReflection {
|
||||
throw new IllegalStateException("Could not find Bukkit. Is it running?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Minecraft library package string.
|
||||
* @return The library package.
|
||||
*/
|
||||
private static String getMinecraftLibraryPackage() {
|
||||
getMinecraftPackage();
|
||||
return MINECRAFT_LIBRARY_PACKAGE;
|
||||
}
|
||||
|
||||
private static void handleLibraryPackage() {
|
||||
try {
|
||||
MINECRAFT_LIBRARY_PACKAGE = "net.minecraft.util";
|
||||
// Try loading Google GSON
|
||||
getClassSource().loadClass(CachedPackage.combine(MINECRAFT_LIBRARY_PACKAGE, "com.google.gson.Gson"));
|
||||
|
||||
} catch (Exception e) {
|
||||
// Assume it's MCPC
|
||||
MINECRAFT_LIBRARY_PACKAGE = "";
|
||||
ProtocolLibrary.getErrorReporter().reportWarning(MinecraftReflection.class,
|
||||
Report.newBuilder(REPORT_NON_CRAFTBUKKIT_LIBRARY_PACKAGE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the package version of the underlying CraftBukkit server.
|
||||
* @return The package version, or NULL if not applicable (before 1.4.6).
|
||||
@ -1552,6 +1585,20 @@ public class MinecraftReflection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the google.gson.Gson class used by Minecraft.
|
||||
* @return The GSON class.
|
||||
*/
|
||||
public static Class<?> getMinecraftGsonClass() {
|
||||
try {
|
||||
return getMinecraftLibraryClass("com.google.gson.Gson");
|
||||
} catch (RuntimeException e) {
|
||||
Class<?> match = FuzzyReflection.fromClass(PacketType.Status.Server.OUT_SERVER_INFO.getPacketClass()).
|
||||
getFieldByType(".*\\.google\\.gson\\.Gson").getType();
|
||||
return setMinecraftLibraryClass("com.google.gson.Gson", match);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given method retrieved by ASM is a constructor.
|
||||
* @param name - the name of the method.
|
||||
@ -1766,6 +1813,31 @@ public class MinecraftReflection {
|
||||
return minecraftPackage.getPackageClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class object of a specific Minecraft library class.
|
||||
* @param className - the specific library Minecraft class.
|
||||
* @return Class object.
|
||||
* @throws RuntimeException If we are unable to find the given class.
|
||||
*/
|
||||
public static Class<?> getMinecraftLibraryClass(String className) {
|
||||
if (libraryPackage == null)
|
||||
libraryPackage = new CachedPackage(getMinecraftLibraryPackage(), getClassSource());
|
||||
return libraryPackage.getPackageClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class object for the specific library class.
|
||||
* @param className - name of the Minecraft library class.
|
||||
* @param clazz - the new class object.
|
||||
* @return The provided clazz object.
|
||||
*/
|
||||
private static Class<?> setMinecraftLibraryClass(String className, Class<?> clazz) {
|
||||
if (libraryPackage == null)
|
||||
libraryPackage = new CachedPackage(getMinecraftLibraryPackage(), getClassSource());
|
||||
libraryPackage.setPackageClass(className, clazz);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class object for the specific Minecraft class.
|
||||
* @param className - name of the Minecraft class.
|
||||
|
@ -20,11 +20,13 @@ import net.minecraft.util.io.netty.buffer.Unpooled;
|
||||
import net.minecraft.util.io.netty.handler.codec.base64.Base64;
|
||||
import net.minecraft.util.io.netty.util.IllegalReferenceCountException;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Charsets;
|
||||
@ -59,6 +61,14 @@ public class WrappedServerPing extends AbstractWrapper {
|
||||
private static FieldAccessor PLAYERS_MAXIMUM = PLAYERS_INTS[0];
|
||||
private static FieldAccessor PLAYERS_ONLINE = PLAYERS_INTS[1];
|
||||
|
||||
// Server ping serialization
|
||||
private static Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
|
||||
private static MethodAccessor GSON_TO_JSON = Accessors.getMethodAccessor(GSON_CLASS, "toJson", Object.class);
|
||||
private static MethodAccessor GSON_FROM_JSON = Accessors.getMethodAccessor(GSON_CLASS, "fromJson", String.class, Class.class);
|
||||
private static FieldAccessor PING_GSON = Accessors.getCached(Accessors.getFieldAccessor(
|
||||
PacketType.Status.Server.OUT_SERVER_INFO.getPacketClass(), GSON_CLASS, true
|
||||
));
|
||||
|
||||
// Server data fields
|
||||
private static Class<?> VERSION_CLASS = MinecraftReflection.getServerPingServerDataClass();
|
||||
private static ConstructorAccessor VERSION_CONSTRUCTOR = Accessors.getConstructorAccessor(VERSION_CLASS, String.class, int.class);
|
||||
@ -117,6 +127,15 @@ public class WrappedServerPing extends AbstractWrapper {
|
||||
return new WrappedServerPing(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a wrapper server ping from an encoded JSON string.
|
||||
* @param json - the JSON string.
|
||||
* @return The wrapped server ping.
|
||||
*/
|
||||
public static WrappedServerPing fromJson(String json) {
|
||||
return fromHandle(GSON_FROM_JSON.invoke(PING_GSON.get(null), json, SERVER_PING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the message of the day.
|
||||
* @return The messge of the day.
|
||||
@ -329,6 +348,14 @@ public class WrappedServerPing extends AbstractWrapper {
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying JSON representation of this server ping.
|
||||
* @return The JSON representation.
|
||||
*/
|
||||
public String toJson() {
|
||||
return (String) GSON_TO_JSON.invoke(PING_GSON.get(null), handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a compressed favicon.
|
||||
* @author Kristian
|
||||
|
Loading…
Reference in New Issue
Block a user