diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index c3c5c60..8b05f72 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -34,7 +34,6 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import static com.cryptomorin.xseries.reflection.XReflection.*; -import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; /** * The {@link MapManager} manages all the maps. It also contains functions for wrapping. diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index 32396b9..534728c 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -297,7 +297,17 @@ public class MapWrapper extends AbstractMapWrapper { if (supports(20, 4)) { Object mapIdComponent = ReflectionUtil.getDeclaredField(getNMSClass("core.component", "DataComponents"), "B"); Object mapId1 = ReflectionUtil.callConstructor(getNMSClass("world.level.saveddata.maps", "MapId"), mapId); - ReflectionUtil.callMethod(nmsStack, "b", mapIdComponent, mapId1); + + // Use generic reflection because of generics + // T ItemStack#b(DataComponentType dataComponentType, T t) + try { + Method m = nmsStack.getClass().getMethod("b", getNMSClass("core.component", "DataComponentType"), Object.class); + m.setAccessible(true); + m.invoke(nmsStack, mapIdComponent, mapId1); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } } else if (supports(13)) { String nbtObjectName; if (supports(20)) { //1.20 diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index 5cf1957..ec3daf2 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -30,14 +30,18 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; +import sun.misc.Unsafe; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.api.events.CreativeInventoryMapUpdateEvent; import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.concurrent.TimeUnit; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getConnection; import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; import static com.cryptomorin.xseries.reflection.XReflection.*; @@ -51,7 +55,7 @@ public class PacketListener implements Listener { private static final Class playerCommonConnection; static { - if (supports(20) && supportsPatch(2)) { + if (supports(20, 2)) { // The packet send method has been abstracted from ServerGamePacketListenerImpl to ServerCommonPacketListenerImpl in 1.20.2 playerCommonConnection = getNMSClass("server.network", "ServerCommonPacketListenerImpl"); } else { @@ -78,11 +82,34 @@ public class PacketListener implements Listener { if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) { Object packetPlayOutMap = packetPlayOutMapClass.cast(packet); - int id = (int) getDeclaredField(packetPlayOutMap, "a"); - if (id < 0) { - int newId = -id; - setDeclaredField(packetPlayOutMap, "a", newId); + int id; + boolean inv = false; + if (supports(20, 4)) { //1.20.4 uses MapId class and record classes (final fields...) + Object mapId = getDeclaredField(packetPlayOutMap, "b"); + id = (int) getDeclaredField(mapId, "c"); + + if (id < 0) { + Object newMapid = callConstructor(mapId.getClass(), -id); + Object c = getDeclaredField(packetPlayOutMap, "c"); + Object d = getDeclaredField(packetPlayOutMap, "d"); + Object e = getDeclaredField(packetPlayOutMap, "e"); + Object f = getDeclaredField(packetPlayOutMap, "f"); + + packetPlayOutMap = callConstructor(packetPlayOutMapClass, newMapid, c, d, e, f); + packet = packetPlayOutMap; + + inv = true; + } } else { + id = (int) getDeclaredField(packetPlayOutMap, "a"); + + if (id < 0) { + setDeclaredField(packetPlayOutMap, "a", -id); + inv = true; + } + } + + if (!inv) { boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); MapCancelEvent event = new MapCancelEvent(player, id, async); if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true); @@ -102,13 +129,13 @@ public class PacketListener implements Listener { if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) { Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet); - int entityId = (int) getDeclaredField(packetPlayInEntity, "a"); + int entityId = (int) getDeclaredField(packetPlayInEntity, supports(20, 4) ? "b" : "a"); Enum actionEnum; Enum hand; Object pos; if (supports(17)) { - Object action = getDeclaredField(packetPlayInEntity, "b"); + Object action = getDeclaredField(packetPlayInEntity, supports(20, 4) ? "c" : "b"); actionEnum = (Enum) callDeclaredMethod(action, "a"); Class d = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$d"); Class e = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$e"); @@ -141,7 +168,12 @@ public class PacketListener implements Listener { } else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) { Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet); - int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 4) ? "b" : supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a + int slot; + if (supports(20, 4)) { //1.20.4+ uses short + slot = (short) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, "b"); + } else { //1.20.3 and lower uses int + slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a + } Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.5 = e, 1.20.2-1.20.4 = d, >= 1.18 = c, 1.17 = getItemStack ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack); @@ -167,9 +199,7 @@ public class PacketListener implements Listener { } private Channel getChannel(Player player) { - Object playerHandle = getHandle(player); - Object playerConnection = getDeclaredField(playerHandle, supports(20) ? "c" : supports(17) ? "b" : "playerConnection"); //1.20 = c, 1.17-1.19 = b, 1.16 = playerConnection - Object networkManager = getDeclaredField(playerCommonConnection, playerConnection, supports(20, 2) ? "c" : supports(19, 4) ? "h" : supports(19) ? "b" : supports(17) ? "a" : "networkManager"); //1.20.2 = ServerCommonPacketListenerImpl#c, 1.20(.1) & 1.19.4 = h, >= 1.19.3 = b, 1.18 - 1.17 = a, 1.16 = networkManager + Object networkManager = getDeclaredField(playerCommonConnection, getConnection(player), supports(21) ? "e" : supports(20, 2) ? "c" : supports(19, 4) ? "h" : supports(19) ? "b" : supports(17) ? "a" : "networkManager"); //1.20.2 = ServerCommonPacketListenerImpl#c, 1.20(.1) & 1.19.4 = h, >= 1.19.3 = b, 1.18 - 1.17 = a, 1.16 = networkManager return (Channel) getDeclaredField(networkManager, supports(20, 2) ? "n" : supports(18) ? "m" : supports(17) ? "k" : "channel"); //1.20.2 = n, 1.20(.1), 1.19 & 1.18 = m, 1.17 = k, 1.16 = channel } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java index 87f7e57..a723ca9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java @@ -35,6 +35,7 @@ public class ReflectionUtil { private static final Map> constructorCache = new HashMap<>(); private static final Map methodCache = new HashMap<>(); private static final Map fieldCache = new HashMap<>(); + private static final Class craftWorld = getCraftClass("CraftWorld"); /** * Helper class converted to {@link List} @@ -76,17 +77,8 @@ public class ReflectionUtil { } @Nullable - public static Object getHandle(@NotNull World world) { - Class worldServer = getNMSClass("server.level", "WorldServer"); - Class craftWorld = getCraftClass("CraftWorld"); - try { - Method m = craftWorld.getMethod("getHandle", worldServer); - m.setAccessible(true); - return m.invoke(null, world); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); - return null; - } + public static Object getHandle(@NotNull World world) {; + return callDeclaredMethod(craftWorld, world, "getHandle"); } @Nullable @@ -243,6 +235,26 @@ public class ReflectionUtil { } } + @Nullable + public static Object callDeclaredMethod(Class clazz, Object obj, String method, Object... params) { + try { + String cacheKey = "DeclaredMethod:" + clazz.getName() + ":" + method + ":" + Arrays.hashCode(params); + + if (methodCache.containsKey(cacheKey)) { + Method cachedMethod = methodCache.get(cacheKey); + return cachedMethod.invoke(obj, params); + } else { + Method m = clazz.getDeclaredMethod(method, toParamTypes(params)); + m.setAccessible(true); + methodCache.put(cacheKey, m); + return m.invoke(obj, params); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + public static boolean hasField(Object packet, String field) { try { String cacheKey = "HasField:" + packet.getClass().getName() + ":" + field;