Update cloud-bukkit & cloud-paper for Minecraft 1.19.3 (#412)

This commit is contained in:
Jason 2022-12-08 13:43:51 -07:00
parent 7777a85d41
commit 0bb6625de6
4 changed files with 163 additions and 15 deletions

View file

@ -29,6 +29,7 @@ import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.bukkit.argument.NamespacedKeyArgument; import cloud.commandframework.bukkit.argument.NamespacedKeyArgument;
import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier; import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier;
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
import cloud.commandframework.bukkit.internal.RegistryReflection;
import cloud.commandframework.bukkit.parsers.BlockPredicateArgument; import cloud.commandframework.bukkit.parsers.BlockPredicateArgument;
import cloud.commandframework.bukkit.parsers.EnchantmentArgument; import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
import cloud.commandframework.bukkit.parsers.ItemStackArgument; import cloud.commandframework.bukkit.parsers.ItemStackArgument;
@ -90,8 +91,17 @@ public final class BukkitBrigadierMapper<C> {
this.mapSimpleNMS(new TypeToken<NamespacedKeyArgument.Parser<C>>() { this.mapSimpleNMS(new TypeToken<NamespacedKeyArgument.Parser<C>>() {
}, "resource_location", true); }, "resource_location", true);
/* Map Enchantment */ /* Map Enchantment */
try {
// Pre-1.19.3
final Class<? extends ArgumentType<?>> ench = MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft(
"item_enchantment"));
this.mapSimpleNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() { this.mapSimpleNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() {
}, "item_enchantment"); }, "item_enchantment");
} catch (final IllegalArgumentException ignore) {
// 1.19.3+
this.mapNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() {
}, this::modernEnchantment);
}
/* Map Item arguments */ /* Map Item arguments */
this.mapSimpleContextNMS(new TypeToken<ItemStackArgument.Parser<C>>() { this.mapSimpleContextNMS(new TypeToken<ItemStackArgument.Parser<C>>() {
}, "item_stack"); }, "item_stack");
@ -117,6 +127,17 @@ public final class BukkitBrigadierMapper<C> {
}, this::argumentVec2); }, this::argumentVec2);
} }
private ArgumentType<?> modernEnchantment() {
try {
return (ArgumentType<?>) MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft("resource_key"))
.getDeclaredConstructors()[0]
.newInstance(RegistryReflection.registryKey(RegistryReflection.registryByName("enchantment")));
} catch (final Exception e) {
this.commandManager.getOwningPlugin().getLogger().log(Level.INFO, "Failed to retrieve enchantment argument", e);
return fallbackType();
}
}
/** /**
* @param single Whether the selector is for a single entity only (true), or for multiple entities (false) * @param single Whether the selector is for a single entity only (true), or for multiple entities (false)
* @param playersOnly Whether the selector is for players only (true), or for all entities (false) * @param playersOnly Whether the selector is for players only (true), or for all entities (false)

View file

@ -26,7 +26,9 @@ package cloud.commandframework.bukkit.internal;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* This is not API, and as such, may break, change, or be removed without any notice. * This is not API, and as such, may break, change, or be removed without any notice.
@ -35,11 +37,61 @@ import java.util.Arrays;
public final class CommandBuildContextSupplier { public final class CommandBuildContextSupplier {
private static final Class<?> COMMAND_BUILD_CONTEXT_CLASS = CraftBukkitReflection.needMCClass("commands.CommandBuildContext"); private static final Class<?> COMMAND_BUILD_CONTEXT_CLASS = CraftBukkitReflection.needMCClass("commands.CommandBuildContext");
private static final Constructor<?> COMMAND_BUILD_CONTEXT_CTR = COMMAND_BUILD_CONTEXT_CLASS.getDeclaredConstructors()[0]; private static final @Nullable Constructor<?> COMMAND_BUILD_CONTEXT_CTR;
private static final Class<?> REG_ACC_CLASS = COMMAND_BUILD_CONTEXT_CTR.getParameterTypes()[0]; private static final @Nullable Method CREATE_CONTEXT_METHOD;
private static final @Nullable Method GET_WORLD_DATA_METHOD;
private static final @Nullable Method GET_FEATURE_FLAGS_METHOD;
private static final Class<?> REG_ACC_CLASS;
private static final Class<?> MC_SERVER_CLASS = CraftBukkitReflection.needNMSClassOrElse( private static final Class<?> MC_SERVER_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"MinecraftServer", "net.minecraft.server.MinecraftServer" "MinecraftServer", "net.minecraft.server.MinecraftServer"
); );
static {
@Nullable Constructor<?> ctr;
try {
ctr = COMMAND_BUILD_CONTEXT_CLASS.getDeclaredConstructors()[0];
} catch (final Exception ex) {
ctr = null;
}
COMMAND_BUILD_CONTEXT_CTR = ctr;
if (COMMAND_BUILD_CONTEXT_CTR == null) {
CREATE_CONTEXT_METHOD = Arrays.stream(COMMAND_BUILD_CONTEXT_CLASS.getDeclaredMethods())
.filter(it -> it.getParameterCount() == 2 && COMMAND_BUILD_CONTEXT_CLASS.isAssignableFrom(it
.getReturnType()) && Modifier.isStatic(it.getModifiers()))
.skip(1)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find CommandBuildContext.configurable"));
final Class<?> worldDataCls = CraftBukkitReflection.firstNonNullOrThrow(
() -> "Could not find WorldData class",
CraftBukkitReflection.findMCClass("world.level.storage.SaveData"),
CraftBukkitReflection.findMCClass("world.level.storage.WorldData")
);
GET_WORLD_DATA_METHOD = Arrays.stream(MC_SERVER_CLASS.getDeclaredMethods())
.filter(it -> it.getParameterCount() == 0 && !Modifier.isStatic(it.getModifiers()) && it
.getReturnType()
.equals(worldDataCls))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find MinecraftServer#getWorldData method"));
final Class<?> featureFlagSetCls = CraftBukkitReflection.needMCClass("world.flag.FeatureFlagSet");
GET_FEATURE_FLAGS_METHOD = Arrays.stream(worldDataCls.getDeclaredMethods())
.filter(it -> it.getParameterCount() == 0 && it
.getReturnType()
.equals(featureFlagSetCls) && !Modifier.isStatic(it.getModifiers()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find enabledFeatures method"));
} else {
CREATE_CONTEXT_METHOD = null;
GET_WORLD_DATA_METHOD = null;
GET_FEATURE_FLAGS_METHOD = null;
}
REG_ACC_CLASS = COMMAND_BUILD_CONTEXT_CTR != null
? COMMAND_BUILD_CONTEXT_CTR.getParameterTypes()[0]
: CREATE_CONTEXT_METHOD.getParameterTypes()[0];
}
private static final Method GET_SERVER_METHOD; private static final Method GET_SERVER_METHOD;
private static final Method REGISTRY_ACCESS = Arrays.stream(MC_SERVER_CLASS.getDeclaredMethods()) private static final Method REGISTRY_ACCESS = Arrays.stream(MC_SERVER_CLASS.getDeclaredMethods())
.filter(m -> REG_ACC_CLASS.isAssignableFrom(m.getReturnType())) .filter(m -> REG_ACC_CLASS.isAssignableFrom(m.getReturnType()))
@ -58,11 +110,24 @@ public final class CommandBuildContextSupplier {
} }
public static Object commandBuildContext() { public static Object commandBuildContext() {
if (COMMAND_BUILD_CONTEXT_CTR != null) {
try { try {
final Object server = GET_SERVER_METHOD.invoke(null); final Object server = GET_SERVER_METHOD.invoke(null);
return COMMAND_BUILD_CONTEXT_CTR.newInstance(REGISTRY_ACCESS.invoke(server)); return COMMAND_BUILD_CONTEXT_CTR.newInstance(REGISTRY_ACCESS.invoke(server));
} catch (final ReflectiveOperationException e) { } catch (final ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} else if (CREATE_CONTEXT_METHOD != null && GET_WORLD_DATA_METHOD != null && GET_FEATURE_FLAGS_METHOD != null) {
try {
final Object server = GET_SERVER_METHOD.invoke(null);
final Object worldData = GET_WORLD_DATA_METHOD.invoke(server);
final Object flags = GET_FEATURE_FLAGS_METHOD.invoke(worldData);
return CREATE_CONTEXT_METHOD.invoke(null, REGISTRY_ACCESS.invoke(server), flags);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
} else {
throw new IllegalStateException();
}
} }
} }

View file

@ -24,9 +24,14 @@
package cloud.commandframework.bukkit.internal; package cloud.commandframework.bukkit.internal;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import io.leangen.geantyref.GenericTypeReflector;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -39,6 +44,7 @@ public final class RegistryReflection {
public static final @Nullable Field REGISTRY_REGISTRY; public static final @Nullable Field REGISTRY_REGISTRY;
public static final @Nullable Method REGISTRY_GET; public static final @Nullable Method REGISTRY_GET;
public static final @Nullable Method REGISTRY_KEY;
private static final Class<?> RESOURCE_LOCATION_CLASS = CraftBukkitReflection.needNMSClassOrElse( private static final Class<?> RESOURCE_LOCATION_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"MinecraftKey", "MinecraftKey",
@ -58,17 +64,14 @@ public final class RegistryReflection {
if (CraftBukkitReflection.MAJOR_REVISION < 17) { if (CraftBukkitReflection.MAJOR_REVISION < 17) {
REGISTRY_REGISTRY = null; REGISTRY_REGISTRY = null;
REGISTRY_GET = null; REGISTRY_GET = null;
REGISTRY_KEY = null;
} else { } else {
registryClass = CraftBukkitReflection.firstNonNullOrThrow( registryClass = CraftBukkitReflection.firstNonNullOrThrow(
() -> "Registry", () -> "Registry",
CraftBukkitReflection.findMCClass("core.IRegistry"), CraftBukkitReflection.findMCClass("core.IRegistry"),
CraftBukkitReflection.findMCClass("core.Registry") CraftBukkitReflection.findMCClass("core.Registry")
); );
final Class<?> registryClassFinal = registryClass; REGISTRY_REGISTRY = registryRegistryField(registryClass);
REGISTRY_REGISTRY = Arrays.stream(registryClass.getDeclaredFields())
.filter(it -> it.getType().equals(registryClassFinal))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find Registry Registry field"));
REGISTRY_REGISTRY.setAccessible(true); REGISTRY_REGISTRY.setAccessible(true);
final Class<?> resourceLocationClass = CraftBukkitReflection.firstNonNullOrThrow( final Class<?> resourceLocationClass = CraftBukkitReflection.firstNonNullOrThrow(
() -> "ResourceLocation class", () -> "ResourceLocation class",
@ -81,6 +84,21 @@ public final class RegistryReflection {
&& it.getReturnType().equals(Object.class)) && it.getReturnType().equals(Object.class))
.findFirst() .findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find Registry#get(ResourceLocation)")); .orElseThrow(() -> new IllegalStateException("Could not find Registry#get(ResourceLocation)"));
final Class<?> resourceKeyClass = CraftBukkitReflection.needMCClass("resources.ResourceKey");
REGISTRY_KEY = Arrays.stream(registryClass.getDeclaredMethods())
.filter(m -> m.getParameterCount() == 0 && m.getReturnType().equals(resourceKeyClass))
.findFirst()
.orElse(null);
}
}
public static Object registryKey(final Object registry) {
Objects.requireNonNull(REGISTRY_KEY, "REGISTRY_KEY");
try {
return REGISTRY_KEY.invoke(registry);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
} }
} }
@ -109,4 +127,36 @@ public final class RegistryReflection {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
private static Field registryRegistryField(final Class<?> registryClass) {
// Pre-1.19.3 we want the first Registry type field in Registry
// 1.19.3+ we want the only static final Registry<? extends Registry<?>> from BuiltInRegistries
// In 1.19.3+ there are no Registry type fields in Registry
return Arrays.stream(registryClass.getDeclaredFields())
.filter(it -> it.getType().equals(registryClass))
.findFirst()
.orElseGet(() -> registryRegistryFieldFromBuiltInRegistries(registryClass));
}
private static Field registryRegistryFieldFromBuiltInRegistries(final Class<?> registryClass) {
final Class<?> builtInRegistriesClass =
CraftBukkitReflection.needMCClass("core.registries.BuiltInRegistries");
return Arrays.stream(builtInRegistriesClass.getDeclaredFields())
.filter(it -> {
if (!it.getType().equals(registryClass) || !Modifier.isStatic(it.getModifiers())) {
return false;
}
final Type genericType = it.getGenericType();
if (!(genericType instanceof ParameterizedType)) {
return false;
}
Type valueType = ((ParameterizedType) genericType).getActualTypeArguments()[0];
while (valueType instanceof WildcardType) {
valueType = ((WildcardType) valueType).getUpperBounds()[0];
}
return GenericTypeReflector.erase(valueType).equals(registryClass);
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find Registry Registry field"));
}
} }

View file

@ -97,6 +97,7 @@ final class SelectorUtils {
} }
private static final class EntityArgumentParseFunction implements WrappedBrigadierParser.ParseFunction<Object> { private static final class EntityArgumentParseFunction implements WrappedBrigadierParser.ParseFunction<Object> {
static final EntityArgumentParseFunction INSTANCE = new EntityArgumentParseFunction(); static final EntityArgumentParseFunction INSTANCE = new EntityArgumentParseFunction();
@Override @Override
@ -132,6 +133,7 @@ final class SelectorUtils {
} }
private abstract static class SelectorParser<C, T> implements ArgumentParser<C, T>, SelectorMapper<T> { private abstract static class SelectorParser<C, T> implements ArgumentParser<C, T>, SelectorMapper<T> {
protected static final Supplier<Object> NO_PLAYERS_EXCEPTION_TYPE = protected static final Supplier<Object> NO_PLAYERS_EXCEPTION_TYPE =
Suppliers.memoize(() -> findExceptionType("argument.entity.notfound.player")); Suppliers.memoize(() -> findExceptionType("argument.entity.notfound.player"));
protected static final Supplier<Object> NO_ENTITIES_EXCEPTION_TYPE = protected static final Supplier<Object> NO_ENTITIES_EXCEPTION_TYPE =
@ -141,6 +143,7 @@ final class SelectorUtils {
// Hide brigadier references in inner class // Hide brigadier references in inner class
protected static final class Thrower { protected static final class Thrower {
private final Object type; private final Object type;
Thrower(final Object simpleCommandExceptionType) { Thrower(final Object simpleCommandExceptionType) {
@ -228,12 +231,14 @@ final class SelectorUtils {
} }
abstract static class EntitySelectorParser<C, T> extends SelectorParser<C, T> { abstract static class EntitySelectorParser<C, T> extends SelectorParser<C, T> {
protected EntitySelectorParser(final boolean single) { protected EntitySelectorParser(final boolean single) {
super(single, false); super(single, false);
} }
} }
abstract static class PlayerSelectorParser<C, T> extends SelectorParser<C, T> { abstract static class PlayerSelectorParser<C, T> extends SelectorParser<C, T> {
protected PlayerSelectorParser(final boolean single) { protected PlayerSelectorParser(final boolean single) {
super(single, true); super(single, true);
} }
@ -258,6 +263,7 @@ final class SelectorUtils {
} }
private static class ModernSelectorParser<C, T> implements ArgumentParser<C, T> { private static class ModernSelectorParser<C, T> implements ArgumentParser<C, T> {
private final ArgumentParser<C, Object> wrappedBrigadierParser; private final ArgumentParser<C, Object> wrappedBrigadierParser;
private final SelectorMapper<T> mapper; private final SelectorMapper<T> mapper;
@ -326,12 +332,14 @@ final class SelectorUtils {
} }
static final class EntitySelectorWrapper { static final class EntitySelectorWrapper {
private static volatile @MonotonicNonNull Methods methods; private static volatile @MonotonicNonNull Methods methods;
private final CommandContext<?> commandContext; private final CommandContext<?> commandContext;
private final Object selector; private final Object selector;
private static final class Methods { private static final class Methods {
private @MonotonicNonNull Method getBukkitEntity; private @MonotonicNonNull Method getBukkitEntity;
private @MonotonicNonNull Method entity; private @MonotonicNonNull Method entity;
private @MonotonicNonNull Method player; private @MonotonicNonNull Method player;
@ -342,7 +350,9 @@ final class SelectorUtils {
final Object nativeSender = commandContext.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER); final Object nativeSender = commandContext.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
final Class<?> nativeSenderClass = nativeSender.getClass(); final Class<?> nativeSenderClass = nativeSender.getClass();
for (final Method method : selector.getClass().getDeclaredMethods()) { for (final Method method : selector.getClass().getDeclaredMethods()) {
if (method.getParameterCount() != 1 || !method.getParameterTypes()[0].equals(nativeSenderClass)) { if (method.getParameterCount() != 1
|| !method.getParameterTypes()[0].equals(nativeSenderClass)
|| !Modifier.isPublic(method.getModifiers())) {
continue; continue;
} }
@ -475,6 +485,7 @@ final class SelectorUtils {
@FunctionalInterface @FunctionalInterface
interface ReflectiveOperation<T> { interface ReflectiveOperation<T> {
T run() throws ReflectiveOperationException; T run() throws ReflectiveOperationException;
} }
@ -494,6 +505,7 @@ final class SelectorUtils {
@FunctionalInterface @FunctionalInterface
interface SelectorMapper<T> { interface SelectorMapper<T> {
T mapResult(String input, EntitySelectorWrapper wrapper) throws Exception; // throws CommandSyntaxException T mapResult(String input, EntitySelectorWrapper wrapper) throws Exception; // throws CommandSyntaxException
} }