mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 11:36:51 +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"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</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 kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
<attributes>
|
<attributes>
|
||||||
|
@ -2,4 +2,5 @@ eclipse.preferences.version=1
|
|||||||
encoding//src/main/java=cp1252
|
encoding//src/main/java=cp1252
|
||||||
encoding//src/main/resources=cp1252
|
encoding//src/main/resources=cp1252
|
||||||
encoding//src/test/java=cp1252
|
encoding//src/test/java=cp1252
|
||||||
|
encoding//src/test/resources=cp1252
|
||||||
encoding/<project>=cp1252
|
encoding/<project>=cp1252
|
||||||
|
@ -4,7 +4,6 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.ExactReflection;
|
import com.comphenix.protocol.reflect.ExactReflection;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
@ -107,6 +106,50 @@ public final class Accessors {
|
|||||||
return new DefaultFieldAccessor(field);
|
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.
|
* Retrieve a field accessor where the write operation is synchronized on the current field value.
|
||||||
* @param accessor - the accessor.
|
* @param accessor - the accessor.
|
||||||
|
@ -78,6 +78,7 @@ import com.google.common.base.Joiner;
|
|||||||
public class MinecraftReflection {
|
public class MinecraftReflection {
|
||||||
public static final ReportType REPORT_CANNOT_FIND_MCPC_REMAPPER = new ReportType("Cannot find MCPC remapper.");
|
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_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.
|
* Regular expression that matches a Minecraft object.
|
||||||
@ -102,6 +103,11 @@ public class MinecraftReflection {
|
|||||||
*/
|
*/
|
||||||
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
|
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:
|
* Represents a regular expression that will match the version string in a package:
|
||||||
* org.bukkit.craftbukkit.v1_6_R2 -> v1_6_R2
|
* org.bukkit.craftbukkit.v1_6_R2 -> v1_6_R2
|
||||||
@ -114,6 +120,7 @@ public class MinecraftReflection {
|
|||||||
// Package private for the purpose of unit testing
|
// Package private for the purpose of unit testing
|
||||||
static CachedPackage minecraftPackage;
|
static CachedPackage minecraftPackage;
|
||||||
static CachedPackage craftbukkitPackage;
|
static CachedPackage craftbukkitPackage;
|
||||||
|
static CachedPackage libraryPackage;
|
||||||
|
|
||||||
// org.bukkit.craftbukkit
|
// org.bukkit.craftbukkit
|
||||||
private static Constructor<?> craftNMSConstructor;
|
private static Constructor<?> craftNMSConstructor;
|
||||||
@ -199,6 +206,9 @@ public class MinecraftReflection {
|
|||||||
// Libigot patch
|
// Libigot patch
|
||||||
handleLibigot();
|
handleLibigot();
|
||||||
|
|
||||||
|
// Minecraft library package
|
||||||
|
handleLibraryPackage();
|
||||||
|
|
||||||
// Next, do the same for CraftEntity.getHandle() in order to get the correct Minecraft package
|
// Next, do the same for CraftEntity.getHandle() in order to get the correct Minecraft package
|
||||||
Class<?> craftEntity = getCraftEntityClass();
|
Class<?> craftEntity = getCraftEntityClass();
|
||||||
Method getHandle = craftEntity.getMethod("getHandle");
|
Method getHandle = craftEntity.getMethod("getHandle");
|
||||||
@ -245,6 +255,29 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Retrieve the package version of the underlying CraftBukkit server.
|
||||||
* @return The package version, or NULL if not applicable (before 1.4.6).
|
* @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.
|
* Determine if a given method retrieved by ASM is a constructor.
|
||||||
* @param name - the name of the method.
|
* @param name - the name of the method.
|
||||||
@ -1766,6 +1813,31 @@ public class MinecraftReflection {
|
|||||||
return minecraftPackage.getPackageClass(className);
|
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.
|
* Set the class object for the specific Minecraft class.
|
||||||
* @param className - name of the 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.handler.codec.base64.Base64;
|
||||||
import net.minecraft.util.io.netty.util.IllegalReferenceCountException;
|
import net.minecraft.util.io.netty.util.IllegalReferenceCountException;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
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.ConstructorAccessor;
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
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.MinecraftReflection;
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
import com.google.common.base.Charsets;
|
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_MAXIMUM = PLAYERS_INTS[0];
|
||||||
private static FieldAccessor PLAYERS_ONLINE = PLAYERS_INTS[1];
|
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
|
// Server data fields
|
||||||
private static Class<?> VERSION_CLASS = MinecraftReflection.getServerPingServerDataClass();
|
private static Class<?> VERSION_CLASS = MinecraftReflection.getServerPingServerDataClass();
|
||||||
private static ConstructorAccessor VERSION_CONSTRUCTOR = Accessors.getConstructorAccessor(VERSION_CLASS, String.class, int.class);
|
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);
|
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.
|
* Retrieve the message of the day.
|
||||||
* @return The messge of the day.
|
* @return The messge of the day.
|
||||||
@ -329,6 +348,14 @@ public class WrappedServerPing extends AbstractWrapper {
|
|||||||
return copy;
|
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.
|
* Represents a compressed favicon.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
|
Loading…
Reference in New Issue
Block a user