2022-12-07 19:50:01 +01:00
|
|
|
/*
|
|
|
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
|
|
|
* Copyright (C) 2012 Kristian S. Stangeland
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
|
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with this program;
|
|
|
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
* 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.comphenix.protocol.utility;
|
|
|
|
|
|
|
|
import java.lang.reflect.Array;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.lang.reflect.Modifier;
|
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
import com.comphenix.protocol.PacketType;
|
|
|
|
import com.comphenix.protocol.ProtocolLogger;
|
|
|
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
|
|
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
|
|
|
import com.comphenix.protocol.wrappers.EnumWrappers;
|
|
|
|
import io.netty.buffer.Unpooled;
|
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.Material;
|
|
|
|
import org.bukkit.Server;
|
|
|
|
import org.bukkit.entity.Player;
|
|
|
|
import org.bukkit.inventory.ItemStack;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Methods and constants specifically used in conjuction with reflecting Minecraft object.
|
|
|
|
*
|
|
|
|
* @author Kristian
|
|
|
|
*/
|
|
|
|
public final class MinecraftReflection {
|
|
|
|
|
|
|
|
private static final ClassSource CLASS_SOURCE = ClassSource.fromClassLoader();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Regular expression that matches a canonical Java class.
|
|
|
|
*/
|
|
|
|
private static final String CANONICAL_REGEX = "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)+\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
|
|
|
|
private static final String MINECRAFT_CLASS_NAME_REGEX = "net\\.minecraft\\." + CANONICAL_REGEX;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a regular expression that will match the version string in a package: org.bukkit.craftbukkit.v1_6_R2 ->
|
|
|
|
* v1_6_R2
|
|
|
|
*/
|
|
|
|
private static final Pattern PACKAGE_VERSION_MATCHER = Pattern.compile(".*\\.(v\\d+_\\d+_\\w*\\d+)");
|
|
|
|
|
|
|
|
// Cache of getBukkitEntity
|
|
|
|
private static final Map<Class<?>, MethodAccessor> BUKKIT_ENTITY_CACHE = new HashMap<>();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The Entity package in Forge 1.5.2
|
|
|
|
*/
|
|
|
|
private static final String FORGE_ENTITY_PACKAGE = "net.minecraft.entity";
|
|
|
|
|
|
|
|
// Package private for the purpose of unit testing
|
|
|
|
static CachedPackage minecraftPackage;
|
|
|
|
static CachedPackage craftbukkitPackage;
|
|
|
|
static CachedPackage libraryPackage;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Regular expression computed dynamically.
|
|
|
|
*/
|
|
|
|
private static String DYNAMIC_PACKAGE_MATCHER = null;
|
|
|
|
/**
|
|
|
|
* The package name of all the classes that belongs to the native code in Minecraft.
|
|
|
|
*/
|
|
|
|
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
|
|
|
|
private static String MINECRAFT_FULL_PACKAGE = null;
|
|
|
|
private static String CRAFTBUKKIT_PACKAGE = null;
|
|
|
|
|
|
|
|
// fuzzy matcher for minecraft class objects
|
|
|
|
private static AbstractFuzzyMatcher<Class<?>> fuzzyMatcher;
|
|
|
|
// The NMS version
|
|
|
|
private static String packageVersion;
|
|
|
|
// net.minecraft.server
|
|
|
|
private static Class<?> itemStackArrayClass;
|
|
|
|
// Whether we are using netty
|
|
|
|
private static Boolean cachedWatcherObject;
|
|
|
|
|
|
|
|
// ---- ItemStack conversions
|
|
|
|
private static Object itemStackAir = null;
|
|
|
|
private static Boolean nullEnforced = null;
|
|
|
|
|
|
|
|
private static MethodAccessor asNMSCopy = null;
|
|
|
|
private static MethodAccessor asCraftMirror = null;
|
|
|
|
private static MethodAccessor isEmpty = null;
|
|
|
|
|
|
|
|
private MinecraftReflection() {
|
|
|
|
// No need to make this constructable.
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a regular expression that can match Minecraft package objects.
|
|
|
|
*
|
|
|
|
* @return Minecraft package matcher.
|
|
|
|
*/
|
|
|
|
public static String getMinecraftObjectRegex() {
|
|
|
|
if (DYNAMIC_PACKAGE_MATCHER == null) {
|
|
|
|
getMinecraftPackage();
|
|
|
|
}
|
|
|
|
return DYNAMIC_PACKAGE_MATCHER;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a abstract fuzzy class matcher for Minecraft objects.
|
|
|
|
*
|
|
|
|
* @return A matcher for Minecraft objects.
|
|
|
|
*/
|
|
|
|
public static AbstractFuzzyMatcher<Class<?>> getMinecraftObjectMatcher() {
|
|
|
|
if (fuzzyMatcher == null) {
|
|
|
|
fuzzyMatcher = FuzzyMatchers.matchRegex(getMinecraftObjectRegex());
|
|
|
|
}
|
|
|
|
return fuzzyMatcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the name of the Minecraft server package.
|
|
|
|
*
|
|
|
|
* @return Full canonical name of the Minecraft server package.
|
|
|
|
*/
|
|
|
|
public static String getMinecraftPackage() {
|
|
|
|
// Speed things up
|
|
|
|
if (MINECRAFT_FULL_PACKAGE != null) {
|
|
|
|
return MINECRAFT_FULL_PACKAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// get the bukkit version we're running on
|
|
|
|
Server craftServer = Bukkit.getServer();
|
|
|
|
CRAFTBUKKIT_PACKAGE = craftServer.getClass().getPackage().getName();
|
|
|
|
|
|
|
|
// Parse the package version
|
|
|
|
Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
|
|
|
|
if (packageMatcher.matches()) {
|
|
|
|
packageVersion = packageMatcher.group(1);
|
|
|
|
} else {
|
|
|
|
MinecraftVersion version = new MinecraftVersion(craftServer);
|
|
|
|
|
|
|
|
// Just assume R1 - it's probably fine (warn anyway)
|
|
|
|
packageVersion = "v" + version.getMajor() + "_" + version.getMinor() + "_R1";
|
|
|
|
ProtocolLogger.log(Level.SEVERE, "Assuming package version: " + packageVersion);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
|
|
|
// total rework of the NMS structure in 1.17 (at least there's no versioning)
|
|
|
|
MINECRAFT_FULL_PACKAGE = MINECRAFT_PREFIX_PACKAGE = "net.minecraft";
|
|
|
|
setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
|
|
|
|
} else {
|
|
|
|
// extract the server version from the return type of "getHandle" in CraftEntity
|
|
|
|
Method getHandle = getCraftEntityClass().getMethod("getHandle");
|
|
|
|
MINECRAFT_FULL_PACKAGE = getHandle.getReturnType().getPackage().getName();
|
|
|
|
|
|
|
|
// Pretty important invariant
|
|
|
|
if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
|
|
|
|
// See if we got the Forge entity package
|
|
|
|
if (MINECRAFT_FULL_PACKAGE.equals(FORGE_ENTITY_PACKAGE)) {
|
|
|
|
// Use the standard NMS versioned package
|
|
|
|
MINECRAFT_FULL_PACKAGE = CachedPackage.combine(MINECRAFT_PREFIX_PACKAGE, packageVersion);
|
|
|
|
} else {
|
|
|
|
// Assume they're the same instead
|
|
|
|
MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The package is usually flat, so go with that assumption
|
|
|
|
String matcher =
|
|
|
|
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ? Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + CANONICAL_REGEX;
|
|
|
|
|
|
|
|
// We'll still accept the default location, however
|
|
|
|
setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_CLASS_NAME_REGEX + ")");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Use the standard matcher
|
|
|
|
setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MINECRAFT_FULL_PACKAGE;
|
|
|
|
} catch (NoSuchMethodException exception) {
|
|
|
|
throw new IllegalStateException("Cannot find getHandle() in CraftEntity", exception);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the package version of the underlying CraftBukkit server.
|
|
|
|
*
|
|
|
|
* @return The craftbukkit package version.
|
|
|
|
*/
|
|
|
|
public static String getPackageVersion() {
|
|
|
|
getMinecraftPackage();
|
|
|
|
return packageVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the dynamic package matcher.
|
|
|
|
*
|
|
|
|
* @param regex - the Minecraft package regex.
|
|
|
|
*/
|
|
|
|
private static void setDynamicPackageMatcher(String regex) {
|
|
|
|
DYNAMIC_PACKAGE_MATCHER = regex;
|
|
|
|
// Ensure that the matcher is regenerated
|
|
|
|
fuzzyMatcher = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used during debugging and testing.
|
|
|
|
*
|
|
|
|
* @param minecraftPackage - the current Minecraft package.
|
|
|
|
* @param craftBukkitPackage - the current CraftBukkit package.
|
|
|
|
*/
|
|
|
|
static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) {
|
|
|
|
MINECRAFT_FULL_PACKAGE = minecraftPackage;
|
|
|
|
CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
|
|
|
|
|
|
|
|
// Make sure it exists
|
|
|
|
if (getMinecraftServerClass() == null) {
|
|
|
|
throw new IllegalArgumentException("Cannot find MinecraftServer for package " + minecraftPackage);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Standard matcher
|
|
|
|
setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the name of the root CraftBukkit package.
|
|
|
|
*
|
|
|
|
* @return Full canonical name of the root CraftBukkit package.
|
|
|
|
*/
|
|
|
|
public static String getCraftBukkitPackage() {
|
|
|
|
// Ensure it has been initialized
|
|
|
|
if (CRAFTBUKKIT_PACKAGE == null) {
|
|
|
|
getMinecraftPackage();
|
|
|
|
}
|
|
|
|
|
|
|
|
return CRAFTBUKKIT_PACKAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dynamically retrieve the Bukkit entity from a given entity.
|
|
|
|
*
|
|
|
|
* @param nmsObject - the NMS entity.
|
|
|
|
* @return A bukkit entity.
|
|
|
|
* @throws RuntimeException If we were unable to retrieve the Bukkit entity.
|
|
|
|
*/
|
|
|
|
public static Object getBukkitEntity(Object nmsObject) {
|
|
|
|
if (nmsObject == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We will have to do this dynamically, unfortunately
|
|
|
|
try {
|
|
|
|
Class<?> clazz = nmsObject.getClass();
|
|
|
|
MethodAccessor accessor = BUKKIT_ENTITY_CACHE.get(clazz);
|
|
|
|
|
|
|
|
if (accessor == null) {
|
|
|
|
MethodAccessor created = Accessors.getMethodAccessor(clazz, "getBukkitEntity");
|
|
|
|
accessor = BUKKIT_ENTITY_CACHE.putIfAbsent(clazz, created);
|
|
|
|
|
|
|
|
// We won the race
|
|
|
|
if (accessor == null) {
|
|
|
|
accessor = created;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return accessor.invoke(nmsObject);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new IllegalArgumentException("Cannot get Bukkit entity from " + nmsObject, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the Bukkit player from a given PlayerConnection.
|
|
|
|
*
|
|
|
|
* @param playerConnection The PlayerConnection.
|
|
|
|
* @return A bukkit player.
|
|
|
|
* @throws RuntimeException If we were unable to retrieve the Bukkit player.
|
|
|
|
*/
|
|
|
|
public static Player getBukkitPlayerFromConnection(Object playerConnection) {
|
|
|
|
try {
|
|
|
|
return (Player) getBukkitEntity(MinecraftFields.getPlayerFromConnection(playerConnection));
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new IllegalArgumentException("Cannot get Bukkit entity from connection " + playerConnection, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a given object can be found within the package net.minecraft.server.
|
|
|
|
*
|
|
|
|
* @param obj - the object to test.
|
|
|
|
* @return TRUE if it can, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isMinecraftObject(Object obj) {
|
|
|
|
if (obj == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Doesn't matter if we don't check for the version here
|
|
|
|
return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given class is found within the package net.minecraft.server, or any equivalent package.
|
|
|
|
*
|
|
|
|
* @param clazz - the class to test.
|
|
|
|
* @return TRUE if it can, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isMinecraftClass(Class<?> clazz) {
|
|
|
|
if (clazz == null) {
|
|
|
|
throw new IllegalArgumentException("clazz cannot be NULL.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return getMinecraftObjectMatcher().isMatch(clazz, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a given object is found in net.minecraft.server, and has the given name.
|
|
|
|
*
|
|
|
|
* @param obj - the object to test.
|
|
|
|
* @param className - the class name to test.
|
|
|
|
* @return TRUE if it can, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isMinecraftObject(Object obj, String className) {
|
|
|
|
if (obj == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
String javaName = obj.getClass().getName();
|
|
|
|
return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a given Object is compatible with a given Class. That is, whether or not the Object is an instance of
|
|
|
|
* that Class or one of its subclasses. If either is null, false is returned.
|
|
|
|
*
|
|
|
|
* @param clazz Class to test for, may be null
|
|
|
|
* @param object the Object to test, may be null
|
|
|
|
* @return True if it is, false if not
|
|
|
|
* @see Class#isAssignableFrom(Class)
|
|
|
|
*/
|
|
|
|
public static boolean is(Class<?> clazz, Object object) {
|
|
|
|
if (clazz == null || object == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return clazz.isAssignableFrom(object.getClass());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Equivalent to {@link #is(Class, Object)} but we don't call getClass again
|
|
|
|
*/
|
|
|
|
public static boolean is(Class<?> clazz, Class<?> test) {
|
|
|
|
if (clazz == null || test == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return clazz.isAssignableFrom(test);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a given object is a BlockPosition.
|
|
|
|
*
|
|
|
|
* @param obj - the object to test.
|
|
|
|
* @return TRUE if it can, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isBlockPosition(Object obj) {
|
|
|
|
return is(getBlockPositionClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is an NMS ChunkCoordIntPar.
|
|
|
|
*
|
|
|
|
* @param obj - the object.
|
|
|
|
* @return TRUE if it can, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isChunkCoordIntPair(Object obj) {
|
|
|
|
return is(getChunkCoordIntPair(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is actually a Minecraft packet.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isPacketClass(Object obj) {
|
|
|
|
return is(getPacketClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is assignable to a NetServerHandler (PlayerConnection)
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isServerHandler(Object obj) {
|
|
|
|
return is(getPlayerConnectionClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is actually a Minecraft packet.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isMinecraftEntity(Object obj) {
|
|
|
|
return is(getEntityClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is a NMS ItemStack.
|
|
|
|
*
|
|
|
|
* @param value - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isItemStack(Object value) {
|
|
|
|
return is(getItemStackClass(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is a CraftPlayer class.
|
|
|
|
*
|
|
|
|
* @param value - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isCraftPlayer(Object value) {
|
|
|
|
return is(getCraftPlayerClass(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is a Minecraft player entity.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isMinecraftPlayer(Object obj) {
|
|
|
|
return is(getEntityPlayerClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is a data watcher object.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isDataWatcher(Object obj) {
|
|
|
|
return is(getDataWatcherClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is an IntHashMap object.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isIntHashMap(Object obj) {
|
|
|
|
return is(getIntHashMapClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is a CraftItemStack instancey.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isCraftItemStack(Object obj) {
|
|
|
|
return is(getCraftItemStackClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the EntityPlayer (NMS) class.
|
|
|
|
*
|
|
|
|
* @return The entity class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getEntityPlayerClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("server.level.EntityPlayer", "server.level.ServerPlayer", "EntityPlayer");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
try {
|
|
|
|
// Grab CraftPlayer's handle
|
|
|
|
Method getHandle = FuzzyReflection
|
|
|
|
.fromClass(getCraftBukkitClass("entity.CraftPlayer"))
|
|
|
|
.getMethodByName("getHandle");
|
|
|
|
|
|
|
|
// EntityPlayer is the return type
|
|
|
|
return setMinecraftClass("EntityPlayer", getHandle.getReturnType());
|
|
|
|
} catch (IllegalArgumentException e1) {
|
|
|
|
throw new RuntimeException("Could not find EntityPlayer class.", e1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the EntityHuman class.
|
|
|
|
*
|
|
|
|
* @return The entity human class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getEntityHumanClass() {
|
|
|
|
// Assume its the direct superclass
|
|
|
|
return getEntityPlayerClass().getSuperclass();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the GameProfile class.
|
|
|
|
*
|
|
|
|
* @return The game profile class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getGameProfileClass() {
|
|
|
|
return getClass("com.mojang.authlib.GameProfile");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the entity (NMS) class.
|
|
|
|
*
|
|
|
|
* @return The entity class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getEntityClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("world.entity.Entity", "Entity");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
return fallbackMethodReturn("Entity", "entity.CraftEntity", "getHandle");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the CraftChatMessage.
|
|
|
|
*
|
|
|
|
* @return The CraftChatMessage class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftChatMessage() {
|
|
|
|
return getCraftBukkitClass("util.CraftChatMessage");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the WorldServer (NMS) class.
|
|
|
|
*
|
|
|
|
* @return The WorldServer class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getWorldServerClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("server.level.WorldServer", "server.level.ServerLevel", "WorldServer");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
return fallbackMethodReturn("WorldServer", "CraftWorld", "getHandle");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the World (NMS) class.
|
|
|
|
*
|
|
|
|
* @return The world class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getNmsWorldClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("world.level.World", "world.level.Level", "World");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
return setMinecraftClass("World", getWorldServerClass().getSuperclass());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fallback on the return value of a named method in order to get a NMS class.
|
|
|
|
*
|
|
|
|
* @param nmsClass - the expected name of the Minecraft class.
|
|
|
|
* @param craftClass - a CraftBukkit class to look at.
|
|
|
|
* @param methodName - the method we will use.
|
|
|
|
* @return The return value of this method, which will be saved to the package cache.
|
|
|
|
*/
|
|
|
|
private static Class<?> fallbackMethodReturn(String nmsClass, String craftClass, String methodName) {
|
|
|
|
Class<?> result = FuzzyReflection.fromClass(getCraftBukkitClass(craftClass))
|
|
|
|
.getMethodByName(methodName)
|
|
|
|
.getReturnType();
|
|
|
|
// Save the result
|
|
|
|
return setMinecraftClass(nmsClass, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the packet class.
|
|
|
|
*
|
|
|
|
* @return The packet class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getPacketClass() {
|
|
|
|
return getMinecraftClass("network.protocol.Packet", "Packet");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getByteBufClass() {
|
|
|
|
return getClass("io.netty.buffer.ByteBuf");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the EnumProtocol class.
|
|
|
|
*
|
|
|
|
* @return The Enum protocol class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getEnumProtocolClass() {
|
|
|
|
return getMinecraftClass("network.EnumProtocol", "network.ConnectionProtocol", "EnumProtocol");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the IChatBaseComponent class.
|
|
|
|
*
|
|
|
|
* @return The IChatBaseComponent.
|
|
|
|
*/
|
|
|
|
public static Class<?> getIChatBaseComponentClass() {
|
|
|
|
return getMinecraftClass("network.chat.IChatBaseComponent", "network.chat.IChatbaseComponent", "network.chat.Component", "IChatBaseComponent");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getIChatBaseComponentArrayClass() {
|
|
|
|
return getArrayClass(getIChatBaseComponentClass());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NMS chat component text class.
|
|
|
|
*
|
|
|
|
* @return The chat component class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getChatComponentTextClass() {
|
|
|
|
return getMinecraftClass("network.chat.ChatComponentText", "network.chat.TextComponent", "ChatComponentText");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempt to find the ChatSerializer class.
|
|
|
|
*
|
|
|
|
* @return The serializer class.
|
|
|
|
* @throws IllegalStateException If the class could not be found or deduced.
|
|
|
|
*/
|
|
|
|
public static Class<?> getChatSerializerClass() {
|
|
|
|
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the ServerPing class.
|
|
|
|
*
|
|
|
|
* @return The ServerPing class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getServerPingClass() {
|
|
|
|
return getMinecraftClass("network.protocol.status.ServerPing", "network.protocol.status.ServerStatus", "ServerPing");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the ServerPingServerData class.
|
|
|
|
*
|
|
|
|
* @return The ServerPingServerData class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getServerPingServerDataClass() {
|
|
|
|
return getMinecraftClass("network.protocol.status.ServerPing$ServerData", "network.protocol.status.ServerStatus$Version", "ServerPing$ServerData");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the ServerPingPlayerSample class.
|
|
|
|
*
|
|
|
|
* @return The ServerPingPlayerSample class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getServerPingPlayerSampleClass() {
|
|
|
|
return getMinecraftClass(
|
|
|
|
"network.protocol.status.ServerPing$ServerPingPlayerSample",
|
|
|
|
"network.protocol.status.ServerStatus$Players",
|
|
|
|
"ServerPing$ServerPingPlayerSample");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the MinecraftServer class.
|
|
|
|
*
|
|
|
|
* @return MinecraftServer class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getMinecraftServerClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("server.MinecraftServer", "MinecraftServer");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
// Reset cache and try again
|
|
|
|
setMinecraftClass("MinecraftServer", null);
|
|
|
|
|
|
|
|
useFallbackServer();
|
|
|
|
return getMinecraftClass("MinecraftServer");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NMS statistics class.
|
|
|
|
*
|
|
|
|
* @return The statistics class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getStatisticClass() {
|
|
|
|
return getMinecraftClass("stats.Statistic", "stats.Stat", "Statistic");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NMS statistic list class.
|
|
|
|
*
|
|
|
|
* @return The statistic list class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getStatisticListClass() {
|
|
|
|
return getMinecraftClass("stats.StatisticList", "stats.Stats", "StatisticList");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the player list class (or ServerConfigurationManager),
|
|
|
|
*
|
|
|
|
* @return The player list class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getPlayerListClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("server.players.PlayerList", "PlayerList");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
// Reset cache and try again
|
|
|
|
setMinecraftClass("PlayerList", null);
|
|
|
|
|
|
|
|
useFallbackServer();
|
|
|
|
return getMinecraftClass("PlayerList");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the PlayerConnection class.
|
|
|
|
*
|
|
|
|
* @return The PlayerConnection class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getPlayerConnectionClass() {
|
|
|
|
return getMinecraftClass("server.network.PlayerConnection", "server.network.ServerGamePacketListenerImpl", "PlayerConnection");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NetworkManager class.
|
|
|
|
*
|
|
|
|
* @return The NetworkManager class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getNetworkManagerClass() {
|
|
|
|
return getMinecraftClass("network.NetworkManager", "network.Connection", "NetworkManager");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NMS ItemStack class.
|
|
|
|
*
|
|
|
|
* @return The ItemStack class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getItemStackClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("world.item.ItemStack", "ItemStack");
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
// Use the handle reference
|
|
|
|
return setMinecraftClass("ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
|
|
|
|
.getFieldByName("handle")
|
|
|
|
.getType());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the Block (NMS) class.
|
|
|
|
*
|
|
|
|
* @return Block (NMS) class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getBlockClass() {
|
|
|
|
return getMinecraftClass("world.level.block.Block", "Block");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getItemClass() {
|
|
|
|
return getNullableNMS("world.item.Item", "Item");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getFluidTypeClass() {
|
|
|
|
return getNullableNMS("world.level.material.FluidType", "world.level.material.Fluid", "FluidType");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getParticleTypeClass() {
|
|
|
|
return getNullableNMS("core.particles.ParticleType", "core.particles.SimpleParticleType", "ParticleType");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the WorldType class.
|
|
|
|
*
|
|
|
|
* @return The WorldType class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getWorldTypeClass() {
|
|
|
|
return getMinecraftClass("WorldType");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the DataWatcher class.
|
|
|
|
*
|
|
|
|
* @return The DataWatcher class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getDataWatcherClass() {
|
|
|
|
return getMinecraftClass("network.syncher.DataWatcher", "network.syncher.SynchedEntityData", "DataWatcher");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the BlockPosition class.
|
|
|
|
*
|
|
|
|
* @return The BlockPosition class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getBlockPositionClass() {
|
|
|
|
return getMinecraftClass("core.BlockPosition", "core.BlockPos", "BlockPosition");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the Vec3D class.
|
|
|
|
*
|
|
|
|
* @return The Vec3D class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getVec3DClass() {
|
|
|
|
return getMinecraftClass("world.phys.Vec3D", "world.phys.Vec3", "Vec3D");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the ChunkCoordIntPair class.
|
|
|
|
*
|
|
|
|
* @return The ChunkCoordIntPair class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getChunkCoordIntPair() {
|
|
|
|
return getMinecraftClass("world.level.ChunkCoordIntPair", "world.level.ChunkPos", "ChunkCoordIntPair");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the DataWatcher Item class.
|
|
|
|
*
|
|
|
|
* @return The class
|
|
|
|
*/
|
|
|
|
public static Class<?> getDataWatcherItemClass() {
|
|
|
|
return getMinecraftClass("network.syncher.DataWatcher$Item", "network.syncher.SynchedEntityData$DataItem", "DataWatcher$Item", "DataWatcher$WatchableObject");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getDataWatcherObjectClass() {
|
|
|
|
return getNullableNMS("network.syncher.DataWatcherObject", "network.syncher.EntityDataAccessor", "DataWatcherObject");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean watcherObjectExists() {
|
|
|
|
if (cachedWatcherObject == null) {
|
|
|
|
cachedWatcherObject = getDataWatcherObjectClass() != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cachedWatcherObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getDataWatcherSerializerClass() {
|
|
|
|
return getNullableNMS("network.syncher.DataWatcherSerializer", "network.syncher.EntityDataSerializer", "DataWatcherSerializer");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getDataWatcherRegistryClass() {
|
|
|
|
return getMinecraftClass("network.syncher.DataWatcherRegistry", "network.syncher.EntityDataSerializers", "DataWatcherRegistry");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getMinecraftKeyClass() {
|
|
|
|
return getMinecraftClass("resources.MinecraftKey", "resources.ResourceLocation", "MinecraftKey");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getMobEffectListClass() {
|
|
|
|
return getMinecraftClass("world.effect.MobEffectList", "MobEffectList", "world.effect.MobEffect");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getSoundEffectClass() {
|
|
|
|
return getNullableNMS("sounds.SoundEffect", "sounds.SoundEvent", "SoundEffect");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the ServerConnection abstract class.
|
|
|
|
*
|
|
|
|
* @return The ServerConnection class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getServerConnectionClass() {
|
|
|
|
return getMinecraftClass("server.network.ServerConnection", "server.network.ServerConnectionListener", "ServerConnection");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NBT base class.
|
|
|
|
*
|
|
|
|
* @return The NBT base class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getNBTBaseClass() {
|
|
|
|
return getMinecraftClass("nbt.NBTBase", "nbt.Tag", "NBTBase");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NBT read limiter class.
|
|
|
|
*
|
|
|
|
* @return The NBT read limiter.
|
|
|
|
*/
|
|
|
|
public static Class<?> getNBTReadLimiterClass() {
|
|
|
|
return getMinecraftClass("nbt.NBTReadLimiter", "nbt.NbtAccounter", "NBTReadLimiter");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NBT Compound class.
|
|
|
|
*
|
|
|
|
* @return The NBT Compond class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getNBTCompoundClass() {
|
|
|
|
return getMinecraftClass("nbt.NBTTagCompound", "nbt.CompoundTag", "NBTTagCompound");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the EntityTracker (NMS) class.
|
|
|
|
*
|
|
|
|
* @return EntityTracker class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getEntityTrackerClass() {
|
|
|
|
return getMinecraftClass("server.level.PlayerChunkMap$EntityTracker", "server.level.ChunkMap$TrackedEntity", "EntityTracker");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the attribute snapshot class.
|
|
|
|
* <p>
|
|
|
|
* This stores the final value of an attribute, along with all the associated computational steps.
|
|
|
|
*
|
|
|
|
* @return The attribute snapshot class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getAttributeSnapshotClass() {
|
|
|
|
return getMinecraftClass(
|
|
|
|
"network.protocol.game.PacketPlayOutUpdateAttributes$AttributeSnapshot",
|
|
|
|
"network.protocol.game.ClientboundUpdateAttributesPacket$AttributeSnapshot",
|
|
|
|
"AttributeSnapshot",
|
|
|
|
"PacketPlayOutUpdateAttributes$AttributeSnapshot");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the IntHashMap class.
|
|
|
|
*
|
|
|
|
* @return IntHashMap class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getIntHashMapClass() {
|
|
|
|
return getNullableNMS("IntHashMap");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the attribute modifier class.
|
|
|
|
*
|
|
|
|
* @return Attribute modifier class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getAttributeModifierClass() {
|
|
|
|
return getMinecraftClass("world.entity.ai.attributes.AttributeModifier", "AttributeModifier");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the net.minecraft.server.MobEffect class.
|
|
|
|
*
|
|
|
|
* @return The mob effect class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getMobEffectClass() {
|
|
|
|
return getMinecraftClass("world.effect.MobEffect", "world.effect.MobEffectInstance", "MobEffect");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the packet data serializer class that overrides ByteBuf.
|
|
|
|
*
|
|
|
|
* @return The data serializer class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getPacketDataSerializerClass() {
|
|
|
|
return getMinecraftClass("network.PacketDataSerializer", "network.FriendlyByteBuf", "PacketDataSerializer");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NBTCompressedStreamTools class.
|
|
|
|
*
|
|
|
|
* @return The NBTCompressedStreamTools class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getNbtCompressedStreamToolsClass() {
|
|
|
|
return getMinecraftClass("nbt.NBTCompressedStreamTools", "nbt.NbtIo", "NBTCompressedStreamTools");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the NMS tile entity class.
|
|
|
|
*
|
|
|
|
* @return The tile entity class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getTileEntityClass() {
|
|
|
|
return getMinecraftClass("world.level.block.entity.TileEntity", "world.level.block.entity.BlockEntity", "TileEntity");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the Gson class used by Minecraft.
|
|
|
|
*
|
|
|
|
* @return The Gson class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getMinecraftGsonClass() {
|
|
|
|
return getMinecraftLibraryClass("com.google.gson.Gson");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the ItemStack[] class.
|
|
|
|
*
|
|
|
|
* @return The ItemStack[] class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getItemStackArrayClass() {
|
|
|
|
if (itemStackArrayClass == null) {
|
|
|
|
itemStackArrayClass = getArrayClass(getItemStackClass());
|
|
|
|
}
|
|
|
|
return itemStackArrayClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the array class of a given component type.
|
|
|
|
*
|
|
|
|
* @param componentType - type of each element in the array.
|
|
|
|
* @return The class of the array.
|
|
|
|
*/
|
|
|
|
public static Class<?> getArrayClass(Class<?> componentType) {
|
|
|
|
// A bit of a hack, but it works
|
|
|
|
return Array.newInstance(componentType, 0).getClass();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the CraftItemStack class.
|
|
|
|
*
|
|
|
|
* @return The CraftItemStack class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftItemStackClass() {
|
|
|
|
return getCraftBukkitClass("inventory.CraftItemStack");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the CraftPlayer class.
|
|
|
|
*
|
|
|
|
* @return CraftPlayer class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftPlayerClass() {
|
|
|
|
return getCraftBukkitClass("entity.CraftPlayer");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the CraftWorld class.
|
|
|
|
*
|
|
|
|
* @return The CraftWorld class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftWorldClass() {
|
|
|
|
return getCraftBukkitClass("CraftWorld");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the CraftEntity class.
|
|
|
|
*
|
|
|
|
* @return CraftEntity class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftEntityClass() {
|
|
|
|
return getCraftBukkitClass("entity.CraftEntity");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the CraftChatMessage introduced in 1.7.2
|
|
|
|
*
|
|
|
|
* @return The CraftChatMessage class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftMessageClass() {
|
|
|
|
return getCraftBukkitClass("util.CraftChatMessage");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the PlayerInfoData class in 1.8.
|
|
|
|
*
|
|
|
|
* @return The PlayerInfoData class
|
|
|
|
*/
|
|
|
|
public static Class<?> getPlayerInfoDataClass() {
|
|
|
|
return getMinecraftClass(
|
|
|
|
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
|
|
|
|
"network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
|
|
|
|
"PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the entity use action class in 1.17.
|
|
|
|
*
|
|
|
|
* @return The EntityUseAction class
|
|
|
|
*/
|
|
|
|
public static Class<?> getEnumEntityUseActionClass() {
|
|
|
|
Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
|
|
|
|
FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
|
|
|
|
try {
|
|
|
|
return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
|
|
|
|
} catch (IllegalArgumentException ignored) {
|
|
|
|
return fuzzyReflection.getFieldByType("^.*(Action)").getType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a method accessor to get the actual use action out of the wrapping EnumEntityUseAction in 1.17.
|
|
|
|
*
|
|
|
|
* @return a method accessor to get the actual use action
|
|
|
|
*/
|
|
|
|
public static MethodAccessor getEntityUseActionEnumMethodAccessor() {
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
|
|
|
|
return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
|
|
|
.returnTypeExact(EnumWrappers.getEntityUseActionClass())
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a field accessor for the hand in the wrapping EnumEntityUseAction in 1.17.
|
|
|
|
*
|
|
|
|
* @param enumEntityUseAction the object instance of the action, the field is not present in attack.
|
|
|
|
* @return a field accessor for the hand in the wrapping EnumEntityUseAction
|
|
|
|
*/
|
|
|
|
public static FieldAccessor getHandEntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
|
|
|
|
return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(EnumWrappers.getHandClass())
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a field accessor for the vec3d in the wrapping EnumEntityUseAction in 1.17.
|
|
|
|
*
|
|
|
|
* @param enumEntityUseAction the object instance of the action, the field is not present in attack.
|
|
|
|
* @return a field accessor for the hand in the wrapping EnumEntityUseAction
|
|
|
|
*/
|
|
|
|
public static FieldAccessor getVec3EntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
|
|
|
|
return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
|
|
|
|
.typeExact(MinecraftReflection.getVec3DClass())
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the given object is a PlayerInfoData.
|
|
|
|
*
|
|
|
|
* @param obj - the given object.
|
|
|
|
* @return TRUE if it is, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean isPlayerInfoData(Object obj) {
|
|
|
|
return is(getPlayerInfoDataClass(), obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the IBlockData class in 1.8.
|
|
|
|
*
|
|
|
|
* @return The IBlockData class
|
|
|
|
*/
|
|
|
|
public static Class<?> getIBlockDataClass() {
|
|
|
|
return getMinecraftClass("world.level.block.state.IBlockData", "world.level.block.state.BlockState", "IBlockData");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the MultiBlockChangeInfo class in 1.8.
|
|
|
|
*
|
|
|
|
* @return The MultiBlockChangeInfo class
|
|
|
|
*/
|
|
|
|
public static Class<?> getMultiBlockChangeInfoClass() {
|
|
|
|
return getMinecraftClass("MultiBlockChangeInfo", "PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the MultiBlockChangeInfo array class in 1.8.
|
|
|
|
*
|
|
|
|
* @return The MultiBlockChangeInfo array class
|
|
|
|
*/
|
|
|
|
public static Class<?> getMultiBlockChangeInfoArrayClass() {
|
|
|
|
return getArrayClass(getMultiBlockChangeInfoClass());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the PacketPlayOutGameStateChange.a class, aka GameState in 1.16
|
|
|
|
*
|
|
|
|
* @return The GameState class
|
|
|
|
*/
|
|
|
|
public static Class<?> getGameStateClass() {
|
|
|
|
// it's called "a" so there's not a lot we can do to identify it
|
|
|
|
Class<?> packetClass = PacketType.Play.Server.GAME_STATE_CHANGE.getPacketClass();
|
|
|
|
return packetClass.getClasses()[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean signUpdateExists() {
|
|
|
|
return getNullableNMS("PacketPlayOutUpdateSign") != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getNonNullListClass() {
|
|
|
|
return getMinecraftClass("core.NonNullList", "NonNullList");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static MethodAccessor getNonNullListCreateAccessor() {
|
|
|
|
try {
|
|
|
|
Class<?> nonNullListType = MinecraftReflection.getNonNullListClass();
|
|
|
|
Method method = FuzzyReflection.fromClass(nonNullListType).getMethod(FuzzyMethodContract.newBuilder()
|
|
|
|
.returnTypeExact(nonNullListType)
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.build());
|
|
|
|
return Accessors.getMethodAccessor(method);
|
|
|
|
} catch (Exception ex) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getCraftSoundClass() {
|
|
|
|
return getCraftBukkitClass("CraftSound");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getSectionPositionClass() {
|
|
|
|
return getMinecraftClass("core.SectionPosition", "core.SectionPos", "SectionPosition");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the Bukkit equivalent of a NMS ItemStack. This method should preserve NBT data and will never return null
|
|
|
|
* when supplied with a valid ItemStack. Empty ItemStacks are treated as AIR.
|
|
|
|
*
|
|
|
|
* @param generic NMS ItemStack
|
|
|
|
* @return The Bukkit equivalent
|
|
|
|
*/
|
|
|
|
public static ItemStack getBukkitItemStack(Object generic) {
|
|
|
|
if (generic == null) {
|
|
|
|
// Convert null to AIR - 1.11 behavior
|
|
|
|
return new ItemStack(Material.AIR);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (generic instanceof ItemStack) {
|
|
|
|
ItemStack bukkit = (ItemStack) generic;
|
|
|
|
|
|
|
|
// They're probably looking for the CraftItemStack
|
|
|
|
// If it's one already our work is done
|
|
|
|
if (is(getCraftItemStackClass(), generic)) {
|
|
|
|
return bukkit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not, convert it to one
|
|
|
|
Object nmsStack = getMinecraftItemStack(bukkit);
|
|
|
|
return getBukkitItemStack(nmsStack);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is(getItemStackClass(), generic)) {
|
|
|
|
// We can't do anything with non-ItemStacks
|
|
|
|
throw new IllegalArgumentException(generic + " is not an ItemStack!");
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Check null enforcement - 1.11 behavior
|
|
|
|
if (nullEnforced == null) {
|
|
|
|
isEmpty = Accessors.getMethodAccessor(getItemStackClass().getMethod("isEmpty"));
|
|
|
|
nullEnforced = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nullEnforced) {
|
|
|
|
if ((boolean) isEmpty.invoke(generic)) {
|
|
|
|
return new ItemStack(Material.AIR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
nullEnforced = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asCraftMirror == null) {
|
|
|
|
try {
|
|
|
|
Method asMirrorMethod = getCraftItemStackClass().getMethod("asCraftMirror", getItemStackClass());
|
|
|
|
asCraftMirror = Accessors.getMethodAccessor(asMirrorMethod);
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
throw new RuntimeException("Failed to obtain CraftItemStack.asCraftMirror", ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Convert to a craft mirror to preserve NBT data
|
|
|
|
return (ItemStack) asCraftMirror.invoke(nullEnforced, generic);
|
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
throw new RuntimeException("Failed to obtain craft mirror of " + generic, ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the NMS equivalent of a Bukkit ItemStack. This method will never return null and should preserve NBT
|
|
|
|
* data. Null inputs are treated as empty (AIR) ItemStacks.
|
|
|
|
*
|
|
|
|
* @param specific Bukkit ItemStack
|
|
|
|
* @return The NMS equivalent
|
|
|
|
*/
|
|
|
|
public static Object getMinecraftItemStack(ItemStack specific) {
|
|
|
|
if (asNMSCopy == null) {
|
|
|
|
try {
|
|
|
|
Method asNmsCopyMethod = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
|
|
|
|
asNMSCopy = Accessors.getMethodAccessor(asNmsCopyMethod);
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
throw new RuntimeException("Failed to obtain CraftItemStack.asNMSCopy", ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is(getCraftItemStackClass(), specific)) {
|
|
|
|
// If it's already a CraftItemStack, use its handle
|
|
|
|
Object unwrapped = BukkitUnwrapper.getInstance().unwrapItem(specific);
|
|
|
|
if (unwrapped != null) {
|
|
|
|
return unwrapped;
|
|
|
|
} else {
|
|
|
|
if (itemStackAir == null) {
|
|
|
|
// Easiest way to get the Material.AIR ItemStack?
|
|
|
|
itemStackAir = getMinecraftItemStack(new ItemStack(Material.AIR));
|
|
|
|
}
|
|
|
|
return itemStackAir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// If not, grab a NMS copy
|
|
|
|
return asNMSCopy.invoke(null, specific);
|
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
throw new RuntimeException("Failed to make NMS copy of " + specific, ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the given class by name.
|
|
|
|
*
|
|
|
|
* @param className - name of the class.
|
|
|
|
* @return The class.
|
|
|
|
*/
|
|
|
|
private static Class<?> getClass(String className) {
|
|
|
|
try {
|
|
|
|
return getClassSource().loadClass(className);
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
throw new RuntimeException("Cannot find class " + className, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the class object of a specific CraftBukkit class.
|
|
|
|
*
|
|
|
|
* @param className - the specific CraftBukkit class.
|
|
|
|
* @return Class object.
|
|
|
|
* @throws RuntimeException If we are unable to find the given class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getCraftBukkitClass(String className) {
|
|
|
|
if (craftbukkitPackage == null) {
|
|
|
|
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage(), getClassSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
return craftbukkitPackage.getPackageClass(className)
|
|
|
|
.orElseThrow(() -> new RuntimeException("Failed to find CraftBukkit class: " + className));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the class object of a specific Minecraft class.
|
|
|
|
*
|
|
|
|
* @param className - the specific Minecraft class.
|
|
|
|
* @return Class object.
|
|
|
|
* @throws RuntimeException If we are unable to find the given class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getMinecraftClass(String className) {
|
|
|
|
if (minecraftPackage == null) {
|
|
|
|
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
return minecraftPackage.getPackageClass(className)
|
|
|
|
.orElseThrow(() -> new RuntimeException("Failed to find NMS class: " + className));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getNullableNMS(String className, String... aliases) {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass(className, aliases);
|
|
|
|
} catch (RuntimeException ex) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the class object for the specific Minecraft class.
|
|
|
|
*
|
|
|
|
* @param className - name of the Minecraft class.
|
|
|
|
* @param clazz - the new class object.
|
|
|
|
* @return The provided clazz object.
|
|
|
|
*/
|
|
|
|
private static Class<?> setMinecraftClass(String className, Class<?> clazz) {
|
|
|
|
if (minecraftPackage == null) {
|
|
|
|
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
minecraftPackage.setPackageClass(className, clazz);
|
|
|
|
return clazz;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the current class source.
|
|
|
|
*
|
|
|
|
* @return The class source.
|
|
|
|
*/
|
|
|
|
private static ClassSource getClassSource() {
|
|
|
|
return CLASS_SOURCE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the first class that matches a specified Minecraft name.
|
|
|
|
*
|
|
|
|
* @param className - the specific Minecraft class.
|
|
|
|
* @param aliases - alternative names for this Minecraft class.
|
|
|
|
* @return Class object.
|
|
|
|
* @throws RuntimeException If we are unable to find any of the given classes.
|
|
|
|
*/
|
|
|
|
public static Class<?> getMinecraftClass(String className, String... aliases) {
|
|
|
|
if (minecraftPackage == null) {
|
|
|
|
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
return minecraftPackage.getPackageClass(className).orElseGet(() -> {
|
|
|
|
Class<?> resolved = null;
|
|
|
|
for (String alias : aliases) {
|
|
|
|
// try to resolve the class and stop searching if we found it
|
|
|
|
resolved = minecraftPackage.getPackageClass(alias).orElse(null);
|
|
|
|
if (resolved != null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we resolved the class cache it and return the result
|
|
|
|
if (resolved != null) {
|
|
|
|
minecraftPackage.setPackageClass(className, resolved);
|
|
|
|
return resolved;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unable to find the class
|
|
|
|
throw new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases)));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the class object of a specific Minecraft library class.
|
|
|
|
*
|
|
|
|
* @param className - the specific library Minecraft class.
|
|
|
|
* @return Class object.
|
|
|
|
* @throws RuntimeException If we are unable to find the given class.
|
|
|
|
*/
|
|
|
|
public static Class<?> getMinecraftLibraryClass(String className) {
|
|
|
|
if (libraryPackage == null) {
|
|
|
|
libraryPackage = new CachedPackage("", getClassSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
return libraryPackage.getPackageClass(className)
|
|
|
|
.orElseThrow(() -> new RuntimeException("Failed to find class: " + className));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the class object for the specific library class.
|
|
|
|
*
|
|
|
|
* @param className - name of the Minecraft library class.
|
|
|
|
* @param clazz - the new class object.
|
|
|
|
* @return The provided clazz object.
|
|
|
|
*/
|
|
|
|
private static Class<?> setMinecraftLibraryClass(String className, Class<?> clazz) {
|
|
|
|
if (libraryPackage == null) {
|
|
|
|
libraryPackage = new CachedPackage("", getClassSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
libraryPackage.setPackageClass(className, clazz);
|
|
|
|
return clazz;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dynamically retrieve the NetworkManager name.
|
|
|
|
*
|
|
|
|
* @return Name of the NetworkManager class.
|
|
|
|
*/
|
|
|
|
public static String getNetworkManagerName() {
|
|
|
|
return getNetworkManagerClass().getSimpleName();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve an instance of the packet data serializer wrapper.
|
|
|
|
*
|
|
|
|
* @param buffer - the buffer.
|
|
|
|
* @return The instance.
|
|
|
|
*/
|
|
|
|
public static Object getPacketDataSerializer(Object buffer) {
|
|
|
|
try {
|
|
|
|
// TODO: move this to MinecraftMethods, or at least, cache the constructor accessor
|
|
|
|
Class<?> packetSerializer = getPacketDataSerializerClass();
|
|
|
|
return packetSerializer.getConstructor(getByteBufClass()).newInstance(buffer);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException("Cannot construct packet serializer.", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Object createPacketDataSerializer(int initialSize) {
|
|
|
|
// validate the initial size
|
|
|
|
if (initialSize <= 0) {
|
|
|
|
initialSize = 256;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object buffer = Unpooled.buffer(initialSize);
|
|
|
|
return getPacketDataSerializer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getNbtTagTypes() {
|
|
|
|
return getMinecraftClass("nbt.NBTTagTypes", "nbt.TagTypes", "NBTTagTypes");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getChatDeserializer() {
|
|
|
|
return getMinecraftClass("util.ChatDeserializer", "util.GsonHelper", "ChatDeserializer");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getChatMutableComponentClass() {
|
|
|
|
return getMinecraftClass("network.chat.IChatMutableComponent", "network.chat.MutableComponent");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getDimensionManager() {
|
|
|
|
return getMinecraftClass("world.level.dimension.DimensionManager", "world.level.dimension.DimensionType", "DimensionManager");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getMerchantRecipeList() {
|
|
|
|
return getMinecraftClass("world.item.trading.MerchantRecipeList", "world.item.trading.MerchantOffers", "MerchantRecipeList");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getResourceKey() {
|
|
|
|
return getMinecraftClass("resources.ResourceKey", "ResourceKey");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getEntityTypes() {
|
|
|
|
return getMinecraftClass("world.entity.EntityTypes", "world.entity.EntityType", "EntityTypes");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getParticleParam() {
|
|
|
|
return getMinecraftClass("core.particles.ParticleParam", "core.particles.ParticleOptions", "ParticleParam");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getSectionPosition() {
|
|
|
|
return getMinecraftClass("core.SectionPosition", "core.SectionPos", "SectionPosition");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getChunkProviderServer() {
|
|
|
|
return getMinecraftClass("server.level.ChunkProviderServer", "server.level.ServerChunkCache", "ChunkProviderServer");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getPlayerChunkMap() {
|
|
|
|
return getMinecraftClass("server.level.PlayerChunkMap", "server.level.ChunkMap", "PlayerChunkMap");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getIRegistry() {
|
|
|
|
return getNullableNMS("core.IRegistry", "core.Registry", "IRegistry");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getAttributeBase() {
|
|
|
|
return getMinecraftClass("world.entity.ai.attributes.AttributeBase", "world.entity.ai.attributes.Attribute", "AttributeBase");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getProfilePublicKeyClass() {
|
|
|
|
return getMinecraftClass("world.entity.player.ProfilePublicKey");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getSaltedSignatureClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("SaltedSignature");
|
|
|
|
} catch (RuntimeException runtimeException) {
|
|
|
|
Class<?> minecraftEncryption = getMinecraftClass("util.MinecraftEncryption", "util.Crypt", "MinecraftEncryption");
|
|
|
|
FuzzyMethodContract constructorContract = FuzzyMethodContract.newBuilder()
|
|
|
|
.parameterCount(2)
|
|
|
|
.parameterExactType(Long.TYPE, 0)
|
|
|
|
.parameterExactType(byte[].class, 1)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
for (Class<?> subclass : minecraftEncryption.getClasses()) {
|
|
|
|
FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(subclass, true);
|
|
|
|
List<Constructor<?>> constructors = fuzzyReflection.getConstructorList(constructorContract);
|
|
|
|
|
|
|
|
if (!constructors.isEmpty()) {
|
|
|
|
return setMinecraftClass("SaltedSignature", subclass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Class<?> messageSigClass = getMinecraftClass("network.chat.MessageSignature", "MessageSignature");
|
|
|
|
FuzzyClassContract signatureContract = FuzzyClassContract.newBuilder()
|
|
|
|
.constructor(FuzzyMethodContract.newBuilder()
|
|
|
|
.parameterCount(2)
|
|
|
|
.parameterSuperOf(Long.TYPE, 0)
|
|
|
|
.parameterSuperOf(byte[].class, 1)
|
|
|
|
.build())
|
|
|
|
.build();
|
|
|
|
|
|
|
|
FuzzyFieldContract fuzzyFieldContract = FuzzyFieldContract.newBuilder()
|
|
|
|
.typeMatches(getMinecraftObjectMatcher().and(signatureContract))
|
|
|
|
.build();
|
|
|
|
|
|
|
|
Class<?> signatureClass = FuzzyReflection.fromClass(messageSigClass, true)
|
|
|
|
.getField(fuzzyFieldContract)
|
|
|
|
.getType();
|
|
|
|
return setMinecraftClass("SaltedSignature", signatureClass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getProfilePublicKeyDataClass() {
|
|
|
|
return getProfilePublicKeyClass().getClasses()[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getFastUtilClass(String className) {
|
|
|
|
return getLibraryClass("it.unimi.dsi.fastutil." + className);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getInt2ObjectMapClass() {
|
|
|
|
return getFastUtilClass("ints.Int2ObjectMap");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getIntArrayListClass() {
|
|
|
|
return getFastUtilClass("ints.IntArrayList");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getLibraryClass(String classname) {
|
|
|
|
try {
|
|
|
|
return getMinecraftLibraryClass(classname);
|
|
|
|
} catch (RuntimeException ex) {
|
|
|
|
Class<?> clazz = getMinecraftLibraryClass("org.bukkit.craftbukkit.libs." + classname);
|
|
|
|
setMinecraftLibraryClass(classname, clazz);
|
|
|
|
return clazz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fallback method that can determine the MinecraftServer and the ServerConfigurationManager.
|
|
|
|
*/
|
|
|
|
private static void useFallbackServer() {
|
|
|
|
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
|
|
|
|
Constructor<?> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
|
|
|
|
.getConstructor(FuzzyMethodContract.newBuilder()
|
|
|
|
.parameterMatches(getMinecraftObjectMatcher(), 0)
|
|
|
|
.parameterCount(2)
|
|
|
|
.build());
|
|
|
|
Class<?>[] params = selected.getParameterTypes();
|
|
|
|
|
|
|
|
// Jackpot - two classes at the same time!
|
|
|
|
setMinecraftClass("MinecraftServer", params[0]);
|
|
|
|
setMinecraftClass("PlayerList", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getLevelChunkPacketDataClass() {
|
|
|
|
return getNullableNMS("network.protocol.game.ClientboundLevelChunkPacketData");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getLightUpdatePacketDataClass() {
|
|
|
|
return getNullableNMS("network.protocol.game.ClientboundLightUpdatePacketData");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getBlockEntityTypeClass() {
|
|
|
|
return getMinecraftClass("world.level.block.entity.BlockEntityType", "world.level.block.entity.TileEntityTypes", "TileEntityTypes");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class<?> getBlockEntityInfoClass() {
|
|
|
|
try {
|
|
|
|
return getMinecraftClass("BlockEntityInfo");
|
|
|
|
} catch (RuntimeException expected) {
|
|
|
|
Class<?> infoClass = (Class<?>) ((ParameterizedType) FuzzyReflection.fromClass(getLevelChunkPacketDataClass(),
|
|
|
|
true).getFieldListByType(List.class).get(0).getGenericType()).getActualTypeArguments()[0];
|
|
|
|
|
|
|
|
setMinecraftClass("BlockEntityInfo", infoClass);
|
|
|
|
|
|
|
|
return infoClass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|