♻️ Merged reflection utility classes
This commit is contained in:
parent
e110f0afcc
commit
13573d4add
5 changed files with 300 additions and 417 deletions
|
@ -30,7 +30,7 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.MapManager;
|
import tech.sbdevelopment.mapreflectionapi.api.MapManager;
|
||||||
import tech.sbdevelopment.mapreflectionapi.listeners.MapListener;
|
import tech.sbdevelopment.mapreflectionapi.listeners.MapListener;
|
||||||
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
|
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils;
|
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ public class MapReflectionAPI extends JavaPlugin {
|
||||||
getLogger().info("MapReflectionAPI v" + getDescription().getVersion() + "");
|
getLogger().info("MapReflectionAPI v" + getDescription().getVersion() + "");
|
||||||
getLogger().info("Made by © Copyright SBDevelopment 2022");
|
getLogger().info("Made by © Copyright SBDevelopment 2022");
|
||||||
|
|
||||||
if (!ReflectionUtils.supports(12)) {
|
if (!ReflectionUtil.supports(12)) {
|
||||||
getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.19!");
|
getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.19!");
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -73,8 +72,8 @@ public class MapSender {
|
||||||
}, 0, 2);
|
}, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Class<?> packetPlayOutMapClass = ReflectionUtils.getNMSClass("network.protocol.game", "PacketPlayOutMap");
|
private static final Class<?> packetPlayOutMapClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutMap");
|
||||||
private static final Class<?> worldMapData = ReflectionUtils.supports(17) ? ReflectionUtils.getNMSClass("world.level.saveddata.maps", "WorldMap") : null;
|
private static final Class<?> worldMapData = ReflectionUtil.supports(17) ? ReflectionUtil.getNMSClass("world.level.saveddata.maps", "WorldMap") : null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a map to a player
|
* Send a map to a player
|
||||||
|
@ -101,7 +100,7 @@ public class MapSender {
|
||||||
|
|
||||||
final int id = -id0;
|
final int id = -id0;
|
||||||
Object packet;
|
Object packet;
|
||||||
if (ReflectionUtils.supports(17)) { //1.17+
|
if (ReflectionUtil.supports(17)) { //1.17+
|
||||||
Object updateData = ReflectionUtil.callConstructor(worldMapData,
|
Object updateData = ReflectionUtil.callConstructor(worldMapData,
|
||||||
content.minX, //X pos
|
content.minX, //X pos
|
||||||
content.minY, //Y pos
|
content.minY, //Y pos
|
||||||
|
@ -117,7 +116,7 @@ public class MapSender {
|
||||||
new ArrayList<>(), //Icons
|
new ArrayList<>(), //Icons
|
||||||
updateData
|
updateData
|
||||||
);
|
);
|
||||||
} else if (ReflectionUtils.supports(14)) { //1.16-1.14
|
} else if (ReflectionUtil.supports(14)) { //1.16-1.14
|
||||||
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
|
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
|
||||||
id, //ID
|
id, //ID
|
||||||
(byte) 0, //Scale
|
(byte) 0, //Scale
|
||||||
|
@ -144,7 +143,7 @@ public class MapSender {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtils.sendPacket(player, packet);
|
ReflectionUtil.sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
static final class QueuedMap {
|
static final class QueuedMap {
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|
|
@ -32,7 +32,6 @@ import org.bukkit.metadata.FixedMetadataValue;
|
||||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -48,14 +47,14 @@ public class MapWrapper {
|
||||||
this.content = image;
|
this.content = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Class<?> craftStackClass = ReflectionUtils.getCraftClass("CraftItemStack");
|
private static final Class<?> craftStackClass = ReflectionUtil.getCraftClass("CraftItemStack");
|
||||||
private static final Class<?> setSlotPacketClass = ReflectionUtils.getNMSClass("network.protocol.game", "PacketPlayOutSetSlot");
|
private static final Class<?> setSlotPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutSetSlot");
|
||||||
private static final Class<?> tagCompoundClass = ReflectionUtils.getCraftClass("NBTTagCompound");
|
private static final Class<?> tagCompoundClass = ReflectionUtil.getCraftClass("NBTTagCompound");
|
||||||
private static final Class<?> entityClass = ReflectionUtils.getNMSClass("world.entity", "Entity");
|
private static final Class<?> entityClass = ReflectionUtil.getNMSClass("world.entity", "Entity");
|
||||||
private static final Class<?> dataWatcherClass = ReflectionUtils.getNMSClass("network.syncher", "DataWatcher");
|
private static final Class<?> dataWatcherClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher");
|
||||||
private static final Class<?> entityMetadataPacketClass = ReflectionUtils.getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata");
|
private static final Class<?> entityMetadataPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata");
|
||||||
private static final Class<?> entityItemFrameClass = ReflectionUtils.getNMSClass("world.entity.decoration", "EntityItemFrame");
|
private static final Class<?> entityItemFrameClass = ReflectionUtil.getNMSClass("world.entity.decoration", "EntityItemFrame");
|
||||||
private static final Class<?> dataWatcherItemClass = ReflectionUtils.getNMSClass("network.syncher", "DataWatcher$Item");
|
private static final Class<?> dataWatcherItemClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$Item");
|
||||||
|
|
||||||
protected MapController controller = new MapController() {
|
protected MapController controller = new MapController() {
|
||||||
private final Map<UUID, Integer> viewers = new HashMap<>();
|
private final Map<UUID, Integer> viewers = new HashMap<>();
|
||||||
|
@ -142,17 +141,17 @@ public class MapWrapper {
|
||||||
slot = 8 - (slot - 36);
|
slot = 8 - (slot - 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object playerHandle = ReflectionUtils.getHandle(player);
|
Object playerHandle = ReflectionUtil.getHandle(player);
|
||||||
Object inventoryMenu = ReflectionUtil.getField(playerHandle, ReflectionUtils.supports(19) ? "bT" : ReflectionUtils.supports(17) ? "bU" : "defaultContainer");
|
Object inventoryMenu = ReflectionUtil.getField(playerHandle, ReflectionUtil.supports(19) ? "bT" : ReflectionUtil.supports(17) ? "bU" : "defaultContainer");
|
||||||
int windowId = (int) ReflectionUtil.getField(inventoryMenu, ReflectionUtils.supports(17) ? "j" : "windowId");
|
int windowId = (int) ReflectionUtil.getField(inventoryMenu, ReflectionUtil.supports(17) ? "j" : "windowId");
|
||||||
|
|
||||||
ItemStack stack = new ItemStack(ReflectionUtils.supports(13) ? Material.FILLED_MAP : Material.MAP, 1);
|
ItemStack stack = new ItemStack(ReflectionUtil.supports(13) ? Material.FILLED_MAP : Material.MAP, 1);
|
||||||
|
|
||||||
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
|
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
|
||||||
|
|
||||||
Object packet;
|
Object packet;
|
||||||
if (ReflectionUtils.supports(17)) { //1.17+
|
if (ReflectionUtil.supports(17)) { //1.17+
|
||||||
int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, ReflectionUtils.supports(18) ? "j" : "getStateId");
|
int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, ReflectionUtil.supports(18) ? "j" : "getStateId");
|
||||||
|
|
||||||
packet = ReflectionUtil.callConstructor(setSlotPacketClass,
|
packet = ReflectionUtil.callConstructor(setSlotPacketClass,
|
||||||
windowId,
|
windowId,
|
||||||
|
@ -168,7 +167,7 @@ public class MapWrapper {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtils.sendPacket(player, packet);
|
ReflectionUtil.sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -178,7 +177,7 @@ public class MapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInHand(Player player, boolean force) {
|
public void showInHand(Player player, boolean force) {
|
||||||
if (player.getInventory().getItemInMainHand().getType() != (ReflectionUtils.supports(13) ? Material.FILLED_MAP : Material.MAP) && !force)
|
if (player.getInventory().getItemInMainHand().getType() != (ReflectionUtil.supports(13) ? Material.FILLED_MAP : Material.MAP) && !force)
|
||||||
return;
|
return;
|
||||||
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
|
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
|
||||||
}
|
}
|
||||||
|
@ -195,7 +194,7 @@ public class MapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInFrame(Player player, ItemFrame frame, boolean force) {
|
public void showInFrame(Player player, ItemFrame frame, boolean force) {
|
||||||
if (frame.getItem().getType() != (ReflectionUtils.supports(13) ? Material.FILLED_MAP : Material.MAP) && !force)
|
if (frame.getItem().getType() != (ReflectionUtil.supports(13) ? Material.FILLED_MAP : Material.MAP) && !force)
|
||||||
return;
|
return;
|
||||||
showInFrame(player, frame.getEntityId());
|
showInFrame(player, frame.getEntityId());
|
||||||
}
|
}
|
||||||
|
@ -209,7 +208,7 @@ public class MapWrapper {
|
||||||
public void showInFrame(Player player, int entityId, String debugInfo) {
|
public void showInFrame(Player player, int entityId, String debugInfo) {
|
||||||
if (!isViewing(player)) return;
|
if (!isViewing(player)) return;
|
||||||
|
|
||||||
ItemStack stack = new ItemStack(ReflectionUtils.supports(13) ? Material.FILLED_MAP : Material.MAP, 1);
|
ItemStack stack = new ItemStack(ReflectionUtil.supports(13) ? Material.FILLED_MAP : Material.MAP, 1);
|
||||||
if (debugInfo != null) {
|
if (debugInfo != null) {
|
||||||
ItemMeta itemMeta = stack.getItemMeta();
|
ItemMeta itemMeta = stack.getItemMeta();
|
||||||
itemMeta.setDisplayName(debugInfo);
|
itemMeta.setDisplayName(debugInfo);
|
||||||
|
@ -239,11 +238,11 @@ public class MapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemFrame getItemFrameById(World world, int entityId) {
|
public ItemFrame getItemFrameById(World world, int entityId) {
|
||||||
Object worldHandle = ReflectionUtils.getHandle(world);
|
Object worldHandle = ReflectionUtil.getHandle(world);
|
||||||
Object nmsEntity = ReflectionUtil.callMethod(worldHandle, ReflectionUtils.supports(18) ? "a" : "getEntity");
|
Object nmsEntity = ReflectionUtil.callMethod(worldHandle, ReflectionUtil.supports(18) ? "a" : "getEntity");
|
||||||
if (nmsEntity == null) return null;
|
if (nmsEntity == null) return null;
|
||||||
|
|
||||||
if (!ReflectionUtils.supports(17)) {
|
if (!ReflectionUtil.supports(17)) {
|
||||||
nmsEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity");
|
nmsEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,14 +252,14 @@ public class MapWrapper {
|
||||||
|
|
||||||
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
|
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
|
||||||
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
|
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
|
||||||
Object nbtObject = ReflectionUtil.callMethod(nmsStack, ReflectionUtils.supports(19) ? "v" : ReflectionUtils.supports(18) ? "u" : ReflectionUtils.supports(13) ? "getOrCreateTag" : "getTag");
|
Object nbtObject = ReflectionUtil.callMethod(nmsStack, ReflectionUtil.supports(19) ? "v" : ReflectionUtil.supports(18) ? "u" : ReflectionUtil.supports(13) ? "getOrCreateTag" : "getTag");
|
||||||
|
|
||||||
if (!ReflectionUtils.supports(13) && nbtObject == null) { //1.12 has no getOrCreate, call create if null!
|
if (!ReflectionUtil.supports(13) && nbtObject == null) { //1.12 has no getOrCreate, call create if null!
|
||||||
Object tagCompound = ReflectionUtil.callConstructor(tagCompoundClass);
|
Object tagCompound = ReflectionUtil.callConstructor(tagCompoundClass);
|
||||||
ReflectionUtil.callMethod(nbtObject, "setTag", tagCompound);
|
ReflectionUtil.callMethod(nbtObject, "setTag", tagCompound);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtil.callMethod(nbtObject, ReflectionUtils.supports(18) ? "a" : "setInt", "map", mapId);
|
ReflectionUtil.callMethod(nbtObject, ReflectionUtil.supports(18) ? "a" : "setInt", "map", mapId);
|
||||||
Object dataWatcher = ReflectionUtil.callConstructor(dataWatcherClass, entityClass.cast(null));
|
Object dataWatcher = ReflectionUtil.callConstructor(dataWatcherClass, entityClass.cast(null));
|
||||||
|
|
||||||
Object packet = ReflectionUtil.callConstructor(entityMetadataPacketClass,
|
Object packet = ReflectionUtil.callConstructor(entityMetadataPacketClass,
|
||||||
|
@ -270,12 +269,12 @@ public class MapWrapper {
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Object> list = new ArrayList<>();
|
List<Object> list = new ArrayList<>();
|
||||||
Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, ReflectionUtils.supports(17) ? "ao" : ReflectionUtils.supports(14) ? "ITEM" : ReflectionUtils.supports(13) ? "e" : "c");
|
Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, ReflectionUtil.supports(17) ? "ao" : ReflectionUtil.supports(14) ? "ITEM" : ReflectionUtil.supports(13) ? "e" : "c");
|
||||||
Object dataWatcherItem = ReflectionUtil.callConstructor(dataWatcherItemClass, dataWatcherObject, nmsStack);
|
Object dataWatcherItem = ReflectionUtil.callConstructor(dataWatcherItemClass, dataWatcherObject, nmsStack);
|
||||||
list.add(dataWatcherItem);
|
list.add(dataWatcherItem);
|
||||||
ReflectionUtil.setDeclaredField(packet, "b", list);
|
ReflectionUtil.setDeclaredField(packet, "b", list);
|
||||||
|
|
||||||
ReflectionUtils.sendPacket(player, packet);
|
ReflectionUtil.sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,14 +23,22 @@
|
||||||
|
|
||||||
package tech.sbdevelopment.mapreflectionapi.utils;
|
package tech.sbdevelopment.mapreflectionapi.utils;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>ReflectionUtil</b> - Reflection handler for NMS and CraftBukkit.<br>
|
* <b>ReflectionUtil</b> - Reflection handler for NMS and CraftBukkit.<br>
|
||||||
|
@ -47,9 +55,131 @@ import java.util.Arrays;
|
||||||
* @version 2.1
|
* @version 2.1
|
||||||
*/
|
*/
|
||||||
public class ReflectionUtil {
|
public class ReflectionUtil {
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* Performance is not a concern for these specific statically initialized values.
|
||||||
|
*/
|
||||||
|
public static final String VERSION;
|
||||||
|
/**
|
||||||
|
* The raw minor version number.
|
||||||
|
* E.g. {@code v1_17_R1} to {@code 17}
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public static final int VER = Integer.parseInt(VERSION.substring(1).split("_")[1]);
|
||||||
|
/**
|
||||||
|
* Mojang remapped their NMS in 1.17 https://www.spigotmc.org/threads/spigot-bungeecord-1-17.510208/#post-4184317
|
||||||
|
*/
|
||||||
|
public static final String
|
||||||
|
CRAFTBUKKIT = "org.bukkit.craftbukkit." + VERSION + '.',
|
||||||
|
NMS = v(17, "net.minecraft.").orElse("net.minecraft.server." + 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.
|
||||||
|
* <p>
|
||||||
|
* This is also where the famous player {@code ping} field comes from!
|
||||||
|
*/
|
||||||
|
private static final MethodHandle GET_HANDLE;
|
||||||
|
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 { // 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'");
|
||||||
|
VERSION = found;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
MethodHandle sendPacket = null, getHandle = null, getHandleWorld = null, connection = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection = lookup.findGetter(entityPlayer,
|
||||||
|
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(playerConnection,
|
||||||
|
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 ReflectionUtil() {
|
private ReflectionUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is purely for readability.
|
||||||
|
* No performance is gained.
|
||||||
|
*
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public static <T> VersionHandler<T> v(int version, T handle) {
|
||||||
|
return new VersionHandler<>(version, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the server version is equal or greater than the given version.
|
||||||
|
*
|
||||||
|
* @param version the version to compare the server version with.
|
||||||
|
* @return true if the version is equal or newer, otherwise false.
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public static boolean supports(int version) {
|
||||||
|
return VER >= version;
|
||||||
|
}
|
||||||
|
|
||||||
private static Class<?> wrapperToPrimitive(Class<?> clazz) {
|
private static Class<?> wrapperToPrimitive(Class<?> clazz) {
|
||||||
if (clazz == Boolean.class) return boolean.class;
|
if (clazz == Boolean.class) return boolean.class;
|
||||||
if (clazz == Integer.class) return int.class;
|
if (clazz == Integer.class) return int.class;
|
||||||
|
@ -162,4 +292,141 @@ public class ReflectionUtil {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility.
|
||||||
|
*
|
||||||
|
* @param newPackage the 1.17 package name.
|
||||||
|
* @param name the name of the class.
|
||||||
|
* @return the NMS class or null if not found.
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@javax.annotation.Nullable
|
||||||
|
public static Class<?> getNMSClass(@Nonnull String newPackage, @Nonnull String name) {
|
||||||
|
if (supports(17)) name = newPackage + '.' + name;
|
||||||
|
return getNMSClass(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a NMS (net.minecraft.server) class.
|
||||||
|
*
|
||||||
|
* @param name the name of the class.
|
||||||
|
* @return the NMS class or null if not found.
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@javax.annotation.Nullable
|
||||||
|
public static Class<?> getNMSClass(@Nonnull String name) {
|
||||||
|
try {
|
||||||
|
return Class.forName(NMS + name);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<Void> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@javax.annotation.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@javax.annotation.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
@javax.annotation.Nullable
|
||||||
|
public static Class<?> getCraftClass(@Nonnull String name) {
|
||||||
|
try {
|
||||||
|
return Class.forName(CRAFTBUKKIT + name);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class VersionHandler<T> {
|
||||||
|
private int version;
|
||||||
|
private T handle;
|
||||||
|
|
||||||
|
private VersionHandler(int version, T handle) {
|
||||||
|
if (supports(version)) {
|
||||||
|
this.version = version;
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionHandler<T> v(int version, T 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(T handle) {
|
||||||
|
return this.version == 0 ? handle : this.handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,382 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of MapReflectionAPI.
|
|
||||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
|
||||||
*
|
|
||||||
* 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.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.Objects;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>ReflectionUtils</b> - Reflection handler for NMS and CraftBukkit.<br>
|
|
||||||
* Caches the packet related methods and is asynchronous.
|
|
||||||
* <p>
|
|
||||||
* This class does not handle null checks as most of the requests are from the
|
|
||||||
* other utility classes that already handle null checks.
|
|
||||||
* <p>
|
|
||||||
* <a href="https://wiki.vg/Protocol">Clientbound Packets</a> are considered fake
|
|
||||||
* updates to the client without changing the actual data. Since all the data is handled
|
|
||||||
* by the server.
|
|
||||||
* <p>
|
|
||||||
* A useful resource used to compare mappings is <a href="https://minidigger.github.io/MiniMappingViewer/#/spigot">Mini's Mapping Viewer</a>
|
|
||||||
*
|
|
||||||
* @author Crypto Morin
|
|
||||||
* @version 6.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.
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
* <p>
|
|
||||||
* Performance is not a concern for these specific statically initialized values.
|
|
||||||
*/
|
|
||||||
public static final String 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'");
|
|
||||||
VERSION = found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The raw minor version number.
|
|
||||||
* E.g. {@code v1_17_R1} to {@code 17}
|
|
||||||
*
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
public static final int VER = Integer.parseInt(VERSION.substring(1).split("_")[1]);
|
|
||||||
/**
|
|
||||||
* Mojang remapped their NMS in 1.17 https://www.spigotmc.org/threads/spigot-bungeecord-1-17.510208/#post-4184317
|
|
||||||
*/
|
|
||||||
public static final String
|
|
||||||
CRAFTBUKKIT = "org.bukkit.craftbukkit." + VERSION + '.',
|
|
||||||
NMS = v(17, "net.minecraft.").orElse("net.minecraft.server." + 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.
|
|
||||||
* <p>
|
|
||||||
* This is also where the famous player {@code ping} field comes from!
|
|
||||||
*/
|
|
||||||
private static final MethodHandle GET_HANDLE;
|
|
||||||
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");
|
|
||||||
|
|
||||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
||||||
MethodHandle sendPacket = null, getHandle = null, getHandleWorld = null, connection = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
connection = lookup.findGetter(entityPlayer,
|
|
||||||
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(playerConnection,
|
|
||||||
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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is purely for readability.
|
|
||||||
* No performance is gained.
|
|
||||||
*
|
|
||||||
* @since 5.0.0
|
|
||||||
*/
|
|
||||||
public static <T> VersionHandler<T> v(int version, T handle) {
|
|
||||||
return new VersionHandler<>(version, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> CallableVersionHandler<T> v(int version, Callable<T> handle) {
|
|
||||||
return new CallableVersionHandler<>(version, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the server version is equal or greater than the given version.
|
|
||||||
*
|
|
||||||
* @param version the version to compare the server version with.
|
|
||||||
* @return true if the version is equal or newer, otherwise false.
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
public static boolean supports(int version) {
|
|
||||||
return VER >= version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility.
|
|
||||||
*
|
|
||||||
* @param newPackage the 1.17 package name.
|
|
||||||
* @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(@Nonnull String newPackage, @Nonnull String name) {
|
|
||||||
if (supports(17)) name = newPackage + '.' + name;
|
|
||||||
return getNMSClass(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a NMS (net.minecraft.server) 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) {
|
|
||||||
try {
|
|
||||||
return Class.forName(NMS + name);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<Void> 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 + name);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class<?> getArrayClass(String clazz, boolean nms) {
|
|
||||||
clazz = "[L" + (nms ? NMS : CRAFTBUKKIT) + clazz + ';';
|
|
||||||
try {
|
|
||||||
return Class.forName(clazz);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<T> {
|
|
||||||
private int version;
|
|
||||||
private T handle;
|
|
||||||
|
|
||||||
private VersionHandler(int version, T handle) {
|
|
||||||
if (supports(version)) {
|
|
||||||
this.version = version;
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VersionHandler<T> v(int version, T 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(T handle) {
|
|
||||||
return this.version == 0 ? handle : this.handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class CallableVersionHandler<T> {
|
|
||||||
private int version;
|
|
||||||
private Callable<T> handle;
|
|
||||||
|
|
||||||
private CallableVersionHandler(int version, Callable<T> handle) {
|
|
||||||
if (supports(version)) {
|
|
||||||
this.version = version;
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CallableVersionHandler<T> v(int version, Callable<T> 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<T> handle) {
|
|
||||||
try {
|
|
||||||
return (this.version == 0 ? handle : this.handle).call();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue