diff --git a/.idea/misc.xml b/.idea/misc.xml index 5cacde7..285bc57 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -30,7 +30,7 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index fe21f7f..005712b 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,10 @@ org.bstats tech.sbdevelopment.mapreflectionapi.libs.bstats + + com.cryptomorin.xseries + tech.sbdevelopment.mapreflectionapi.libs.xseries + @@ -177,13 +181,17 @@ 1.18.34 provided - org.bstats bstats-bukkit 3.0.2 compile + + com.github.cryptomorin + XSeries + 11.2.0 + diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java b/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java index 3963b7b..f3ea19a 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java +++ b/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java @@ -27,7 +27,7 @@ import java.awt.*; import java.io.InputStream; import java.util.Arrays; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.supports; +import static com.cryptomorin.xseries.reflection.XReflection.supports; /** * Additional functionality on top of Bukkit's MapPalette diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java index bc41d31..0d76789 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java @@ -34,7 +34,7 @@ import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager; import java.util.logging.Level; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.supports; +import static com.cryptomorin.xseries.reflection.XReflection.supports; public class MapReflectionAPI extends JavaPlugin { private static MapReflectionAPI instance; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index 60fbc15..c3c5c60 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -33,7 +33,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +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. @@ -185,7 +186,7 @@ public class MapManager { * @return The found {@link ItemFrame}, or null */ public ItemFrame getItemFrameById(World world, int entityId) { - Object worldHandle = getHandle(world); + Object worldHandle = ReflectionUtil.getHandle(world); Object nmsEntity = ReflectionUtil.callMethod(worldHandle, supports(18) ? "a" : "getEntity", entityId); if (nmsEntity == null) return null; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java index 0caefd0..9e46c98 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java @@ -27,7 +27,8 @@ import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import java.util.ArrayList; import java.util.List; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket; /** * The {@link MapSender} sends the Map packets to players. @@ -111,6 +112,8 @@ public class MapSender { return; } + int id = -id0; + Object packet; if (supports(20, 4)) { //1.20.5+ Object updateData = ReflectionUtil.callConstructor(worldMapData, @@ -139,26 +142,16 @@ public class MapSender { content.array //Data ); - if (supports(21)) { //1.21+ - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - ReflectionUtil.callConstructor(mapId, -id0), //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Show icons - new ReflectionUtil.CollectionParam<>(), //Icons - updateData - ); - } else { - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - -id0, //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Show icons - new ReflectionUtil.CollectionParam<>(), //Icons - updateData - ); - } + packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, + id, //ID + (byte) 0, //Scale, 0 = 1 block per pixel + false, //Show icons + new ReflectionUtil.CollectionParam<>(), //Icons + updateData + ); } else if (supports(14)) { //1.16-1.14 packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - -id0, //ID + id, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //Tracking position false, //Locked @@ -171,7 +164,7 @@ public class MapSender { ); } else { //1.13- packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - -id0, //ID + id, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //??? new ReflectionUtil.CollectionParam<>(), //Icons diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index def2c14..32396b9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -41,7 +41,9 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket; /** * A {@link MapWrapper} wraps one image. @@ -207,7 +209,7 @@ public class MapWrapper extends AbstractMapWrapper { ); } - sendPacketSync(player, packet); + sendPacket(player, packet); } @Override @@ -371,7 +373,7 @@ public class MapWrapper extends AbstractMapWrapper { ReflectionUtil.setDeclaredField(packet, "b", list); } - sendPacketSync(player, packet); + sendPacket(player, packet); } }; } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index e7914fb..5cf1957 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -38,8 +38,9 @@ import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import java.util.concurrent.TimeUnit; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; public class PacketListener implements Listener { private static final Class packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap"); diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java index ad81288..87f7e57 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java @@ -18,6 +18,7 @@ package tech.sbdevelopment.mapreflectionapi.utils; +import org.bukkit.World; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; +import static com.cryptomorin.xseries.reflection.XReflection.getCraftClass; +import static com.cryptomorin.xseries.reflection.XReflection.getNMSClass; + public class ReflectionUtil { private static final Map> constructorCache = new HashMap<>(); private static final Map methodCache = new HashMap<>(); @@ -71,6 +75,20 @@ public class ReflectionUtil { .toArray(Class[]::new); } + @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; + } + } + @Nullable public static Class getClass(@NotNull String name) { try { diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java deleted file mode 100644 index 912a2c4..0000000 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2023 Crypto Morin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE - * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package tech.sbdevelopment.mapreflectionapi.utils; - -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.Arrays; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * ReflectionUtils - Reflection handler for NMS and CraftBukkit.
- * Caches the packet related methods and is asynchronous. - *

- * This class does not handle null checks as most of the requests are from the - * other utility classes that already handle null checks. - *

- * Clientbound Packets are considered fake - * updates to the client without changing the actual data. Since all the data is handled - * by the server. - *

- * A useful resource used to compare mappings is Mini's Mapping Viewer - * - * @author Crypto Morin - * @version 7.1.0.0.1 - */ -public final class ReflectionUtils { - /** - * We use reflection mainly to avoid writing a new class for version barrier. - * The version barrier is for NMS that uses the Minecraft version as the main package name. - *

- * E.g. EntityPlayer in 1.15 is in the class {@code net.minecraft.server.v1_15_R1} - * but in 1.14 it's in {@code net.minecraft.server.v1_14_R1} - * In order to maintain cross-version compatibility we cannot import these classes. - *

- * Performance is not a concern for these specific statically initialized values. - *

- * Versions Legacy - */ - public static final String NMS_VERSION; - - static { // This needs to be right below VERSION because of initialization order. - // This package loop is used to avoid implementation-dependant strings like Bukkit.getVersion() or Bukkit.getBukkitVersion() - // which allows easier testing as well. - String found = null; - for (Package pack : Package.getPackages()) { - String name = pack.getName(); - - // .v because there are other packages. - if (name.startsWith("org.bukkit.craftbukkit.v")) { - found = pack.getName().split("\\.")[3]; - - // Just a final guard to make sure it finds this important class. - // As a protection for forge+bukkit implementation that tend to mix versions. - // The real CraftPlayer should exist in the package. - // Note: Doesn't seem to function properly. Will need to separate the version - // handler for NMS and CraftBukkit for softwares like catmc. - try { - Class.forName("org.bukkit.craftbukkit." + found + ".entity.CraftPlayer"); - break; - } catch (ClassNotFoundException e) { - found = null; - } - } - } - if (found == null) - throw new IllegalArgumentException("Failed to parse server version. Could not find any package starting with name: 'org.bukkit.craftbukkit.v'"); - NMS_VERSION = found; - } - - /** - * The raw minor version number. - * E.g. {@code v1_17_R1} to {@code 17} - * - * @see #supports(int) - * @since 4.0.0 - */ - public static final int MINOR_NUMBER; - /** - * The raw patch version number. Refers to the major.minor.patch version scheme. - * E.g. - *

    - *
  • {@code v1.20.4} to {@code 4}
  • - *
  • {@code v1.18.2} to {@code 2}
  • - *
  • {@code v1.19.1} to {@code 1}
  • - *
- *

- * I'd not recommend developers to support individual patches at all. You should always support the latest patch. - * For example, between v1.14.0, v1.14.1, v1.14.2, v1.14.3 and v1.14.4 you should only support v1.14.4 - *

- * This can be used to warn server owners when your plugin will break on older patches. - * - * @see #supportsPatch(int) - * @since 7.0.0 - */ - public static final int PATCH_NUMBER; - - static { - String[] split = NMS_VERSION.substring(1).split("_"); - if (split.length < 1) { - throw new IllegalStateException("Version number division error: " + Arrays.toString(split) + ' ' + getVersionInformation()); - } - - String minorVer = split[1]; - try { - MINOR_NUMBER = Integer.parseInt(minorVer); - if (MINOR_NUMBER < 0) - throw new IllegalStateException("Negative minor number? " + minorVer + ' ' + getVersionInformation()); - } catch (Throwable ex) { - throw new RuntimeException("Failed to parse minor number: " + minorVer + ' ' + getVersionInformation(), ex); - } - - // Bukkit.getBukkitVersion() = "1.12.2-R0.1-SNAPSHOT" - Matcher bukkitVer = Pattern.compile("^\\d+\\.\\d+\\.(\\d+)").matcher(Bukkit.getBukkitVersion()); - if (bukkitVer.find()) { // matches() won't work, we just want to match the start using "^" - try { - // group(0) gives the whole matched string, we just want the captured group. - PATCH_NUMBER = Integer.parseInt(bukkitVer.group(1)); - } catch (Throwable ex) { - throw new RuntimeException("Failed to parse minor number: " + bukkitVer + ' ' + getVersionInformation(), ex); - } - } else { - // 1.8-R0.1-SNAPSHOT - PATCH_NUMBER = 0; - } - } - - /** - * Gets the full version information of the server. Useful for including in errors. - * - * @since 7.0.0 - */ - public static String getVersionInformation() { - return "(NMS: " + NMS_VERSION + " | " + - "Minecraft: " + Bukkit.getVersion() + " | " + - "Bukkit: " + Bukkit.getBukkitVersion() + ')'; - } - - /** - * Gets the latest known patch number of the given minor version. - * For example: 1.14 -> 4, 1.17 -> 10 - * The latest version is expected to get newer patches, so make sure to account for unexpected results. - * - * @param minorVersion the minor version to get the patch number of. - * @return the patch number of the given minor version if recognized, otherwise null. - * @since 7.0.0 - */ - public static Integer getLatestPatchNumberOf(int minorVersion) { - if (minorVersion <= 0) throw new IllegalArgumentException("Minor version must be positive: " + minorVersion); - - // https://minecraft.wiki/w/Java_Edition_version_history - // There are many ways to do this, but this is more visually appealing. - int[] patches = { - /* 1 */ 1, - /* 2 */ 5, - /* 3 */ 2, - /* 4 */ 7, - /* 5 */ 2, - /* 6 */ 4, - /* 7 */ 10, - /* 8 */ 8, // I don't think they released a server version for 1.8.9 - /* 9 */ 4, - - /* 10 */ 2,// ,_ _ _, - /* 11 */ 2,// \o-o/ - /* 12 */ 2,// ,(.-.), - /* 13 */ 2,// _/ |) (| \_ - /* 14 */ 4,// /\=-=/\ - /* 15 */ 2,// ,| \=/ |, - /* 16 */ 5,// _/ \ | / \_ - /* 17 */ 1,// \_!_/ - /* 18 */ 2, - /* 19 */ 4, - /* 20 */ 4, - }; - - if (minorVersion > patches.length) return null; - return patches[minorVersion - 1]; - } - - /** - * Mojang remapped their NMS in 1.17: Spigot Thread - */ - public static final String - CRAFTBUKKIT_PACKAGE = "org.bukkit.craftbukkit." + NMS_VERSION + '.', - NMS_PACKAGE = v(17, "net.minecraft.").orElse("net.minecraft.server." + NMS_VERSION + '.'); - /** - * A nullable public accessible field only available in {@code EntityPlayer}. - * This can be null if the player is offline. - */ - private static final MethodHandle PLAYER_CONNECTION; - /** - * Responsible for getting the NMS handler {@code EntityPlayer} object for the player. - * {@code CraftPlayer} is simply a wrapper for {@code EntityPlayer}. - * Used mainly for handling packet related operations. - *

- * This is also where the famous player {@code ping} field comes from! - */ - private static final MethodHandle GET_HANDLE; - /** - * Responsible for getting the NMS handler {@code WorldServer} object for the world. - * {@code CraftWorld} is simply a wrapper for {@code WorldServer}. - */ - private static final MethodHandle GET_HANDLE_WORLD; - /** - * Sends a packet to the player's client through a {@code NetworkManager} which - * is where {@code ProtocolLib} controls packets by injecting channels! - */ - private static final MethodHandle SEND_PACKET; - - static { - Class entityPlayer = getNMSClass("server.level", "EntityPlayer"); - Class worldServer = getNMSClass("server.level", "WorldServer"); - Class craftPlayer = getCraftClass("entity.CraftPlayer"); - Class craftWorld = getCraftClass("CraftWorld"); - Class playerConnection = getNMSClass("server.network", "PlayerConnection"); - Class playerCommonConnection; - if (supports(20) && supportsPatch(2)) { - // The packet send method has been abstracted from ServerGamePacketListenerImpl to ServerCommonPacketListenerImpl in 1.20.2 - playerCommonConnection = getNMSClass("server.network", "ServerCommonPacketListenerImpl"); - } else { - playerCommonConnection = playerConnection; - } - - MethodHandles.Lookup lookup = MethodHandles.lookup(); - MethodHandle sendPacket = null, getHandle = null, getHandleWorld = null, connection = null; - - try { - connection = lookup.findGetter(entityPlayer, - v(20, "c").v(17, "b").orElse("playerConnection"), playerConnection); - getHandle = lookup.findVirtual(craftPlayer, "getHandle", MethodType.methodType(entityPlayer)); - getHandleWorld = lookup.findVirtual(craftWorld, "getHandle", MethodType.methodType(worldServer)); - sendPacket = lookup.findVirtual(playerCommonConnection, - v(20, 2, "b").v(18, "a").orElse("sendPacket"), - MethodType.methodType(void.class, getNMSClass("network.protocol", "Packet"))); - } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) { - ex.printStackTrace(); - } - - PLAYER_CONNECTION = connection; - SEND_PACKET = sendPacket; - GET_HANDLE = getHandle; - GET_HANDLE_WORLD = getHandleWorld; - } - - private ReflectionUtils() { - } - - /** - * Gives the {@code handle} object if the server version is equal or greater than the given version. - * This method is purely for readability and should be always used with {@link VersionHandler#orElse(Object)}. - * - * @see #v(int, int, Object) - * @see VersionHandler#orElse(Object) - * @since 5.0.0 - */ - public static VersionHandler v(int version, T handle) { - return new VersionHandler<>(version, handle); - } - - /** - * Overload for {@link #v(int, T)} that supports patch versions - * - * @since 9.5.0 - */ - public static VersionHandler v(int version, int patch, T handle) { - return new VersionHandler<>(version, patch, handle); - } - - public static CallableVersionHandler v(int version, Callable handle) { - return new CallableVersionHandler<>(version, handle); - } - - /** - * Checks whether the server version is equal or greater than the given version. - * - * @param minorNumber the version to compare the server version with. - * @return true if the version is equal or newer, otherwise false. - * @see #MINOR_NUMBER - * @since 4.0.0 - */ - public static boolean supports(int minorNumber) { - return MINOR_NUMBER >= minorNumber; - } - - /** - * Checks whether the server version is equal or greater than the given version. - * - * @param minorNumber the minor version to compare the server version with. - * @param patchNumber the patch number to compare the server version with. - * @return true if the version is equal or newer, otherwise false. - * @see #MINOR_NUMBER - * @see #PATCH_NUMBER - * @since 7.1.0 - */ - public static boolean supports(int minorNumber, int patchNumber) { - return (MINOR_NUMBER == minorNumber && supportsPatch(patchNumber)) || MINOR_NUMBER > minorNumber; - } - - /** - * Checks whether the server version is equal or greater than the given version. - * - * @param patchNumber the version to compare the server version with. - * @return true if the version is equal or newer, otherwise false. - * @see #PATCH_NUMBER - * @since 7.0.0 - */ - public static boolean supportsPatch(int patchNumber) { - return PATCH_NUMBER >= patchNumber; - } - - /** - * Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility. - * - * @param packageName the 1.17+ package name of this class. - * @param name the name of the class. - * @return the NMS class or null if not found. - * @since 4.0.0 - */ - @Nullable - public static Class getNMSClass(@Nullable String packageName, @Nonnull String name) { - if (packageName != null && supports(17)) name = packageName + '.' + name; - - try { - return Class.forName(NMS_PACKAGE + name); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * Get a NMS {@link #NMS_PACKAGE} class. - * - * @param name the name of the class. - * @return the NMS class or null if not found. - * @since 1.0.0 - */ - @Nullable - public static Class getNMSClass(@Nonnull String name) { - return getNMSClass(null, name); - } - - /** - * Sends a packet to the player asynchronously if they're online. - * Packets are thread-safe. - * - * @param player the player to send the packet to. - * @param packets the packets to send. - * @return the async thread handling the packet. - * @see #sendPacketSync(Player, Object...) - * @since 1.0.0 - */ - @Nonnull - public static CompletableFuture sendPacket(@Nonnull Player player, @Nonnull Object... packets) { - return CompletableFuture.runAsync(() -> sendPacketSync(player, packets)) - .exceptionally(ex -> { - ex.printStackTrace(); - return null; - }); - } - - /** - * Sends a packet to the player synchronously if they're online. - * - * @param player the player to send the packet to. - * @param packets the packets to send. - * @see #sendPacket(Player, Object...) - * @since 2.0.0 - */ - public static void sendPacketSync(@Nonnull Player player, @Nonnull Object... packets) { - try { - Object handle = GET_HANDLE.invoke(player); - Object connection = PLAYER_CONNECTION.invoke(handle); - - // Checking if the connection is not null is enough. There is no need to check if the player is online. - if (connection != null) { - for (Object packet : packets) SEND_PACKET.invoke(connection, packet); - } - } catch (Throwable throwable) { - throwable.printStackTrace(); - } - } - - @Nullable - public static Object getHandle(@Nonnull Player player) { - Objects.requireNonNull(player, "Cannot get handle of null player"); - try { - return GET_HANDLE.invoke(player); - } catch (Throwable throwable) { - throwable.printStackTrace(); - return null; - } - } - - @Nullable - public static Object getHandle(@Nonnull World world) { - Objects.requireNonNull(world, "Cannot get handle of null world"); - try { - return GET_HANDLE_WORLD.invoke(world); - } catch (Throwable throwable) { - throwable.printStackTrace(); - return null; - } - } - - @Nullable - public static Object getConnection(@Nonnull Player player) { - Objects.requireNonNull(player, "Cannot get connection of null player"); - try { - Object handle = GET_HANDLE.invoke(player); - return PLAYER_CONNECTION.invoke(handle); - } catch (Throwable throwable) { - throwable.printStackTrace(); - return null; - } - } - - /** - * Get a CraftBukkit (org.bukkit.craftbukkit) class. - * - * @param name the name of the class to load. - * @return the CraftBukkit class or null if not found. - * @since 1.0.0 - */ - @Nullable - public static Class getCraftClass(@Nonnull String name) { - try { - return Class.forName(CRAFTBUKKIT_PACKAGE + name); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * @deprecated Use {@link #toArrayClass(Class)} instead. - */ - @Deprecated - public static Class getArrayClass(String clazz, boolean nms) { - clazz = "[L" + (nms ? NMS_PACKAGE : CRAFTBUKKIT_PACKAGE) + clazz + ';'; - try { - return Class.forName(clazz); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * Gives an array version of a class. For example if you wanted {@code EntityPlayer[]} you'd use: - *

{@code
-     *     Class EntityPlayer = ReflectionUtils.getNMSClass("...", "EntityPlayer");
-     *     Class EntityPlayerArray = ReflectionUtils.toArrayClass(EntityPlayer);
-     * }
- * - * @param clazz the class to get the array version of. You could use for multi-dimensions arrays too. - */ - public static Class toArrayClass(Class clazz) { - try { - return Class.forName("[L" + clazz.getName() + ';'); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - public static final class VersionHandler { - private int version, patch; - private T handle; - - private VersionHandler(int version, T handle) { - this(version, 0, handle); - } - - private VersionHandler(int version, int patch, T handle) { - if (supports(version) && supportsPatch(patch)) { - this.version = version; - this.patch = patch; - this.handle = handle; - } - } - - public VersionHandler v(int version, T handle) { - return v(version, 0, handle); - } - - public VersionHandler v(int version, int patch, T handle) { - if (version == this.version && patch == this.patch) - throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version + '.' + patch); - if (version > this.version && supports(version) && patch >= this.patch && supportsPatch(patch)) { - this.version = version; - this.patch = patch; - this.handle = handle; - } - return this; - } - - /** - * If none of the previous version checks matched, it'll return this object. - */ - public T orElse(T handle) { - return this.version == 0 ? handle : this.handle; - } - } - - public static final class CallableVersionHandler { - private int version; - private Callable handle; - - private CallableVersionHandler(int version, Callable handle) { - if (supports(version)) { - this.version = version; - this.handle = handle; - } - } - - public CallableVersionHandler v(int version, Callable handle) { - if (version == this.version) - throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version); - if (version > this.version && supports(version)) { - this.version = version; - this.handle = handle; - } - return this; - } - - public T orElse(Callable handle) { - try { - return (this.version == 0 ? handle : this.handle).call(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - } -} \ No newline at end of file