diff --git a/src/dev/w1zzrd/spigot/wizcompat/packet/EntityCreator.java b/src/dev/w1zzrd/spigot/wizcompat/packet/EntityCreator.java index e68009d..d103c2b 100644 --- a/src/dev/w1zzrd/spigot/wizcompat/packet/EntityCreator.java +++ b/src/dev/w1zzrd/spigot/wizcompat/packet/EntityCreator.java @@ -9,38 +9,10 @@ import static dev.w1zzrd.spigot.wizcompat.packet.Reflect.*; public final class EntityCreator { private EntityCreator() { throw new UnsupportedOperationException("Functional class"); } - private static Package getNativeMonsterPackage(final Player from) { - // Given player wll be an instance of CraftPlayer - final Package bukkitEntityPackage = from.getClass().getPackage(); - final Class craftShulker = loadClass(bukkitEntityPackage, "CraftShulker"); - - assert craftShulker != null; - - // CraftShulker constructor accepts minecraft EntityShulker instance as second argument - final Class nativeEntityShulker = craftShulker.getDeclaredConstructors()[0].getParameterTypes()[1]; - - // EntityShulker is classified squarely as a monster, so it should be grouped with all other hostiles - return nativeEntityShulker.getPackage(); - } - - private static Package getNativePacketPackage(final Player from) { - final Method sendPacket = findDeclaredMethod( - reflectGetField(reflectGetField(from, "entity"), "playerConnection", "networkManager").getClass(), - new String[]{ "sendPacket" }, - new Object[]{ null } - ); - - return sendPacket.getParameterTypes()[0].getPackage(); - } - private static Object getMinecraftServerFromWorld(final Object worldServer) { return reflectGetField(worldServer, "server", "D"); } - private static Object getWorldServerFromPlayer(final Player from) { - return reflectGetField(from.getWorld(), "world"); - } - private static Object getMonsterEntityType(final Class entityClass) { final Class type_EntityTypes = entityClass.getDeclaredConstructors()[0].getParameterTypes()[0]; @@ -51,7 +23,7 @@ public final class EntityCreator { final Package versionPackage = getNativeMonsterPackage(target); final Class type_Entity = loadClass(versionPackage, entityClassName); - final Object nativeWorld = getWorldServerFromPlayer(target); + final Object nativeWorld = Players.getWorldServerFromPlayer(target); assert type_Entity != null; final Object entityType = getMonsterEntityType(type_Entity); @@ -66,13 +38,9 @@ public final class EntityCreator { return createFakeMonster(target, "EntityShulker"); } - public static void sendPacket(final Player target, final Object packet) { - reflectInvoke(reflectGetField(reflectGetField(target, "entity"), "playerConnection", "networkManager"), new String[]{ "sendPacket" }, packet); - } - public static void sendEntitySpawnPacket(final Player target, final Object entity) { final Package versionPackage = getNativePacketPackage(target); - sendPacket(target, reflectConstruct(loadClass(versionPackage, "PacketPlayOutSpawnEntityLiving", "game.PacketPlayOutSpawnEntityLiving"), entity)); + Packets.sendPacket(target, reflectConstruct(loadClass(versionPackage, "PacketPlayOutSpawnEntityLiving", "game.PacketPlayOutSpawnEntityLiving"), entity)); } public static void sendEntityMetadataPacket(final Player target, final Object entity) { @@ -94,7 +62,7 @@ public final class EntityCreator { ); } - sendPacket( + Packets.sendPacket( target, constr1 ); @@ -102,7 +70,7 @@ public final class EntityCreator { public static void sendEntityDespawnPacket(final Player target, final int entityID) { final Package versionPackage = getNativePacketPackage(target); - sendPacket(target, reflectConstruct(loadClass(versionPackage, "PacketPlayOutEntityDestroy", "game.PacketPlayOutEntityDestroy"), entityID)); + Packets.sendPacket(target, reflectConstruct(loadClass(versionPackage, "PacketPlayOutEntityDestroy", "game.PacketPlayOutEntityDestroy"), entityID)); } public static int getEntityID(final Object entity) { diff --git a/src/dev/w1zzrd/spigot/wizcompat/packet/Packets.java b/src/dev/w1zzrd/spigot/wizcompat/packet/Packets.java new file mode 100644 index 0000000..8b4747f --- /dev/null +++ b/src/dev/w1zzrd/spigot/wizcompat/packet/Packets.java @@ -0,0 +1,13 @@ +package dev.w1zzrd.spigot.wizcompat.packet; + +import org.bukkit.entity.Player; + +import static dev.w1zzrd.spigot.wizcompat.packet.Players.getEntityFromPlayer; +import static dev.w1zzrd.spigot.wizcompat.packet.Reflect.reflectGetField; +import static dev.w1zzrd.spigot.wizcompat.packet.Reflect.reflectInvoke; + +public class Packets { + public static void sendPacket(final Player target, final Object packet) { + reflectInvoke(reflectGetField(getEntityFromPlayer(target), "playerConnection", "networkManager"), new String[]{ "sendPacket" }, packet); + } +} diff --git a/src/dev/w1zzrd/spigot/wizcompat/packet/Players.java b/src/dev/w1zzrd/spigot/wizcompat/packet/Players.java new file mode 100644 index 0000000..c674d56 --- /dev/null +++ b/src/dev/w1zzrd/spigot/wizcompat/packet/Players.java @@ -0,0 +1,78 @@ +package dev.w1zzrd.spigot.wizcompat.packet; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +import static dev.w1zzrd.spigot.wizcompat.packet.Reflect.*; + +public final class Players { + private Players() { throw new UnsupportedOperationException("Functional class"); } + + public static void sendPlayerGameModePacket(final Player target, final GameMode gameMode) { + final Object entity = getEntityFromPlayer(target); + + final Package versionPackage = getNativePacketPackage(target); + final Class type_PacketPlayOutPlayerInfo = loadClass(versionPackage, "PacketPlayOutPlayerInfo", "game.PacketPlayOutPlayerInfo"); + final Class type_EnumPlayerInfoAction = loadClass(versionPackage, "PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); + final Class type_PlayerInfoData = loadClass(versionPackage, "PacketPlayOutPlayerInfo$PlayerInfoData", "game.PacketPlayOutPlayerInfo$PlayerInfoData"); + + assert type_PacketPlayOutPlayerInfo != null; + assert type_EnumPlayerInfoAction != null; + assert type_PlayerInfoData != null; + + final Class type_GameMode = type_PlayerInfoData.getDeclaredConstructors()[0].getParameterTypes()[2]; + + final Object profile = reflectInvoke(entity, new String[]{ "getProfile" }); + final Integer ping = reflectGetField(entity, int.class, "ping", "e"); + + Object nativeGameMode = null; + for (final Enum e : (Enum[])reflectInvokeStatic(type_GameMode, new String[]{ "values" })) + if (e.name().equalsIgnoreCase(gameMode.name())) { + nativeGameMode = e; + break; + } + + // Use deprecated implementation as a last resort + if (nativeGameMode == null) + nativeGameMode = reflectInvokeStatic(type_GameMode, new String[]{ "getById" }, gameMode.getValue()); + + Object listName = reflectInvoke(entity, new String[]{ "getPlayerListName" }); + if (listName == null) + listName = reflectGetField(entity, "listName"); + + // Syntactic sugar can go suck a fat one + // I didn't realize the constructor took an array type, because I completely missed the "..." + final Object entityArray = Array.newInstance(entity.getClass(), 1); + Array.set(entityArray, 0, entity); + + final Object packet = reflectConstruct(type_PacketPlayOutPlayerInfo, reflectGetStaticField(type_EnumPlayerInfoAction, "UPDATE_GAME_MODE", "b"), entityArray); + + assert packet != null; + + final List playerInfo = new ArrayList<>(); + playerInfo.add(reflectConstruct(type_PlayerInfoData, profile, ping, nativeGameMode, listName)); + + reflectSetField(packet, List.class, playerInfo); + + Packets.sendPacket(target, packet); + } + + static Object getWorldServerFromPlayer(final Player from) { + return reflectGetField(from.getWorld(), "world"); + } + + public static Object getEntityFromPlayer(final Player player) { + try { + return reflectInvoke(player, new String[]{ "getHandle" }); + } catch(Throwable t) { + t.printStackTrace(); + } + + return reflectGetField(player, "entity"); + } + +} diff --git a/src/dev/w1zzrd/spigot/wizcompat/packet/Reflect.java b/src/dev/w1zzrd/spigot/wizcompat/packet/Reflect.java index 621785c..9e61a60 100644 --- a/src/dev/w1zzrd/spigot/wizcompat/packet/Reflect.java +++ b/src/dev/w1zzrd/spigot/wizcompat/packet/Reflect.java @@ -1,5 +1,7 @@ package dev.w1zzrd.spigot.wizcompat.packet; +import org.bukkit.entity.Player; + import java.lang.reflect.*; public final class Reflect { @@ -17,10 +19,13 @@ public final class Reflect { do { for (final Method check : current.getDeclaredMethods()) - if (contains(methodNames, check.getName()) && argsMatch(check.getParameterTypes(), args)) + if ((methodNames.length == 0 || contains(methodNames, check.getName())) && argsMatch(check.getParameterTypes(), args)) return check; current = current.getSuperclass(); + + if (current == null) + return null; } while (true); } @@ -29,10 +34,13 @@ public final class Reflect { do { for (final Field check : current.getDeclaredFields()) - if (contains(fieldNames, check.getName()) && (expectedType == null || check.getType().equals(expectedType))) + if ((fieldNames.length == 0 || contains(fieldNames, check.getName())) && (expectedType == null || check.getType().equals(expectedType))) return check; current = current.getSuperclass(); + + if (current == null) + return null; } while (true); } @@ -56,6 +64,19 @@ public final class Reflect { return null; } + public static Object reflectInvokeStatic(final Class target, final String[] methodNames, final Object... args) { + final Method targetMethod = findDeclaredMethod(target, methodNames, args); + targetMethod.setAccessible(true); + + try { + return targetMethod.invoke(null, args); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + + return null; + } + public static void reflectSetStaticField(final Class target, final Object value, final String... fieldNames) { reflectSetStaticField(target, null, value, fieldNames); } @@ -71,7 +92,7 @@ public final class Reflect { } public static T reflectGetStaticField(final Class target, final String... fieldNames) { - return (T) reflectGetStaticField(target, null, fieldNames); + return reflectGetStaticField(target, null, fieldNames); } public static T reflectGetGenericStaticField(final Class target, final Class fieldType, final Class genericType) { @@ -194,6 +215,30 @@ public final class Reflect { (primitive == double.class && objectType == Double.class); } + static Package getNativeMonsterPackage(final Player from) { + // Given player wll be an instance of CraftPlayer + final Package bukkitEntityPackage = from.getClass().getPackage(); + final Class craftShulker = loadClass(bukkitEntityPackage, "CraftShulker"); + + assert craftShulker != null; + + // CraftShulker constructor accepts minecraft EntityShulker instance as second argument + final Class nativeEntityShulker = craftShulker.getDeclaredConstructors()[0].getParameterTypes()[1]; + + // EntityShulker is classified squarely as a monster, so it should be grouped with all other hostiles + return nativeEntityShulker.getPackage(); + } + + static Package getNativePacketPackage(final Player from) { + final Method sendPacket = findDeclaredMethod( + reflectGetField(reflectGetField(from, "entity"), "playerConnection", "networkManager").getClass(), + new String[]{ "sendPacket" }, + new Object[]{ null } + ); + + return sendPacket.getParameterTypes()[0].getPackage(); + } + private interface DeclarationGetter { R[] getDeclared(final Class t); }