From 1fe1b4a0d362ed07d3f78af0c7bf657ff5d1a1b3 Mon Sep 17 00:00:00 2001 From: Jason <11360596+jpenilla@users.noreply.github.com> Date: Thu, 9 Jun 2022 01:52:32 -0700 Subject: [PATCH] bukkit/paper: Update reflection for Minecraft 1.19 (#374) --- CHANGELOG.md | 1 + .../bukkit/BukkitBrigadierMapper.java | 39 +++- .../internal/CommandBuildContextSupplier.java | 69 +++++++ .../internal/MinecraftArgumentTypes.java | 170 +++++++++++------- .../bukkit/internal/RegistryReflection.java | 111 ++++++++++++ .../parsers/BlockPredicateArgument.java | 84 +++------ .../bukkit/parsers/ItemStackArgument.java | 27 ++- .../parsers/ItemStackPredicateArgument.java | 23 ++- 8 files changed, 387 insertions(+), 137 deletions(-) create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CommandBuildContextSupplier.java create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/RegistryReflection.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd4f341..0c64d081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Annotations: `@CommandMethod` annotation processing for compile-time validation ([#365](https://github.com/Incendo/cloud/pull/365)) - Add root command deletion support (core/pircbotx/javacord/jda/bukkit/paper) ([#369](https://github.com/Incendo/cloud/pull/369), [#371](https://github.com/Incendo/cloud/pull/371)) +- Bukkit/Paper: Full support for Minecraft 1.19 ### Fixed - Core: Fix missing caption registration for the regex caption ([#351](https://github.com/Incendo/cloud/pull/351)) diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitBrigadierMapper.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitBrigadierMapper.java index a1097b9e..ecb5c3c6 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitBrigadierMapper.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitBrigadierMapper.java @@ -26,6 +26,7 @@ package cloud.commandframework.bukkit; import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.standard.UUIDArgument; import cloud.commandframework.brigadier.CloudBrigadierManager; +import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.bukkit.parsers.BlockPredicateArgument; import cloud.commandframework.bukkit.parsers.EnchantmentArgument; @@ -88,12 +89,12 @@ public final class BukkitBrigadierMapper { this.mapSimpleNMS(new TypeToken>() { }, "item_enchantment"); /* Map Item arguments */ - this.mapSimpleNMS(new TypeToken>() { + this.mapSimpleContextNMS(new TypeToken>() { }, "item_stack"); - this.mapSimpleNMS(new TypeToken>() { + this.mapSimpleContextNMS(new TypeToken>() { }, "item_predicate"); /* Map Block arguments */ - this.mapSimpleNMS(new TypeToken>() { + this.mapSimpleContextNMS(new TypeToken>() { }, "block_predicate"); /* Map Entity Selectors */ this.mapNMS(new TypeToken>() { @@ -156,13 +157,37 @@ public final class BukkitBrigadierMapper { } } + /** + * Attempt to register a mapping between a cloud argument parser type and an NMS brigadier argument type which + * has a single-arg constructor taking CommandBuildContext. + * + * @param type Type to map + * @param argument parser type + * @param argumentId registry id of argument type + * @since 1.7.0 + */ + public > void mapSimpleContextNMS( + final @NonNull TypeToken type, + final @NonNull String argumentId + ) { + this.mapNMS(type, () -> { + try { + return (ArgumentType) MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft(argumentId)) + .getDeclaredConstructors()[0] + .newInstance(CommandBuildContextSupplier.commandBuildContext()); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }); + } + /** * Attempt to register a mapping between a cloud argument parser type and an NMS brigadier argument type which * has a no-args constructor. * * @param type Type to map * @param argument parser type - * @param argumentId network id of argument type + * @param argumentId registry id of argument type * @since 1.5.0 */ public > void mapSimpleNMS( @@ -176,9 +201,9 @@ public final class BukkitBrigadierMapper { * Attempt to register a mapping between a cloud argument parser type and an NMS brigadier argument type which * has a no-args constructor. * - * @param type Type to map - * @param argument parser type - * @param argumentId network id of argument type + * @param type Type to map + * @param argument parser type + * @param argumentId registry id of argument type * @param useCloudSuggestions whether to use cloud suggestions * @since 1.6.0 */ diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CommandBuildContextSupplier.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CommandBuildContextSupplier.java new file mode 100644 index 00000000..5404e6b6 --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CommandBuildContextSupplier.java @@ -0,0 +1,69 @@ +// +// MIT License +// +// Copyright (c) 2021 Alexander Söderberg & Contributors +// +// 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 cloud.commandframework.bukkit.internal; + +import com.google.common.annotations.Beta; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * This is not API, and as such, may break, change, or be removed without any notice. + */ +@Beta +public final class CommandBuildContextSupplier { + + 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 Class REG_ACC_CLASS = COMMAND_BUILD_CONTEXT_CTR.getParameterTypes()[0]; + private static final Class MC_SERVER_CLASS = CraftBukkitReflection.needNMSClassOrElse( + "MinecraftServer", "net.minecraft.server.MinecraftServer" + ); + private static final Method GET_SERVER_METHOD; + private static final Method REGISTRY_ACCESS = Arrays.stream(MC_SERVER_CLASS.getDeclaredMethods()) + .filter(m -> REG_ACC_CLASS.isAssignableFrom(m.getReturnType())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Cannot find MinecraftServer#registryAccess")); + + static { + try { + GET_SERVER_METHOD = MC_SERVER_CLASS.getDeclaredMethod("getServer"); + } catch (final NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private CommandBuildContextSupplier() { + } + + public static Object commandBuildContext() { + try { + final Object server = GET_SERVER_METHOD.invoke(null); + return COMMAND_BUILD_CONTEXT_CTR.newInstance(REGISTRY_ACCESS.invoke(server)); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/MinecraftArgumentTypes.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/MinecraftArgumentTypes.java index 318e150e..d8f58d91 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/MinecraftArgumentTypes.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/MinecraftArgumentTypes.java @@ -62,8 +62,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; /** * A registry of the {@link ArgumentType}s provided by Minecraft. - *

- * This file is taken from MIT licensed code in commodore (https://github.com/lucko/commodore). * *

This is not API, and as such, may break, change, or be removed without any notice.

*/ @@ -73,54 +71,13 @@ public final class MinecraftArgumentTypes { private MinecraftArgumentTypes() { } - private static final Constructor MINECRAFT_KEY_CONSTRUCTOR; - private static final Method ARGUMENT_REGISTRY_GET_BY_KEY_METHOD; - private static final Field BY_CLASS_MAP_FIELD; + private static final ArgumentTypeGetter ARGUMENT_TYPE_GETTER; static { - try { - final Class minecraftKey; - final Class argumentRegistry; - - if (CraftBukkitReflection.findMCClass("resources.ResourceLocation") != null) { - minecraftKey = CraftBukkitReflection.needMCClass("resources.ResourceLocation"); - argumentRegistry = CraftBukkitReflection.needMCClass("commands.synchronization.ArgumentTypes"); - } else { - minecraftKey = CraftBukkitReflection.needNMSClassOrElse( - "MinecraftKey", - "net.minecraft.resources.MinecraftKey" - ); - argumentRegistry = CraftBukkitReflection.needNMSClassOrElse( - "ArgumentRegistry", - "net.minecraft.commands.synchronization.ArgumentRegistry" - ); - } - - MINECRAFT_KEY_CONSTRUCTOR = minecraftKey.getConstructor(String.class, String.class); - MINECRAFT_KEY_CONSTRUCTOR.setAccessible(true); - - ARGUMENT_REGISTRY_GET_BY_KEY_METHOD = Arrays.stream(argumentRegistry.getDeclaredMethods()) - .filter(method -> method.getParameterCount() == 1) - .filter(method -> minecraftKey.equals(method.getParameterTypes()[0])) - .findFirst().orElseThrow(NoSuchMethodException::new); - ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.setAccessible(true); - - BY_CLASS_MAP_FIELD = Arrays.stream(argumentRegistry.getDeclaredFields()) - .filter(field -> Modifier.isStatic(field.getModifiers())) - .filter(field -> field.getType().equals(Map.class)) - .filter(field -> { - final ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); - final Type param = parameterizedType.getActualTypeArguments()[0]; - if (!(param instanceof ParameterizedType)) { - return false; - } - return ((ParameterizedType) param).getRawType().equals(Class.class); - }) - .findFirst() - .orElseThrow(NoSuchFieldException::new); - BY_CLASS_MAP_FIELD.setAccessible(true); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); + if (CraftBukkitReflection.classExists("org.bukkit.entity.Warden")) { + ARGUMENT_TYPE_GETTER = new ArgumentTypeGetterImpl(); // 1.19+ + } else { + ARGUMENT_TYPE_GETTER = new LegacyArgumentTypeGetter(); // 1.13-1.18.2 } } @@ -131,26 +88,117 @@ public final class MinecraftArgumentTypes { * @return the returned argument type class * @throws IllegalArgumentException if no such argument is registered */ - @SuppressWarnings("unchecked") public static Class> getClassByKey( final @NonNull NamespacedKey key ) throws IllegalArgumentException { - try { - Object minecraftKey = MINECRAFT_KEY_CONSTRUCTOR.newInstance(key.getNamespace(), key.getKey()); - Object entry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.invoke(null, minecraftKey); - if (entry == null) { - throw new IllegalArgumentException(key.toString()); - } + return ARGUMENT_TYPE_GETTER.getClassByKey(key); + } - final Map, Object> map = (Map, Object>) BY_CLASS_MAP_FIELD.get(null); - for (final Map.Entry, Object> mapEntry : map.entrySet()) { - if (mapEntry.getValue() == entry) { - return (Class>) mapEntry.getKey(); + private interface ArgumentTypeGetter { + Class> getClassByKey(@NonNull NamespacedKey key) throws IllegalArgumentException; + } + + @SuppressWarnings("unchecked") + private static final class ArgumentTypeGetterImpl implements MinecraftArgumentTypes.ArgumentTypeGetter { + private final Object argumentRegistry; + private final Map byClassMap; + + private ArgumentTypeGetterImpl() { + this.argumentRegistry = RegistryReflection.registryByName("command_argument_type"); + try { + final Field declaredField = CraftBukkitReflection.needMCClass("commands.synchronization.ArgumentTypeInfos") + .getDeclaredFields()[0]; + declaredField.setAccessible(true); + this.byClassMap = (Map) declaredField.get(null); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Class> getClassByKey(final @NonNull NamespacedKey key) throws IllegalArgumentException { + final Object argTypeInfo = RegistryReflection.get(this.argumentRegistry, key.getNamespace() + ":" + key.getKey()); + for (final Map.Entry entry : this.byClassMap.entrySet()) { + if (entry.getValue() == argTypeInfo) { + return (Class>) entry.getKey(); } } throw new IllegalArgumentException(key.toString()); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + private static final class LegacyArgumentTypeGetter implements ArgumentTypeGetter { + private static final Constructor MINECRAFT_KEY_CONSTRUCTOR; + private static final Method ARGUMENT_REGISTRY_GET_BY_KEY_METHOD; + private static final Field BY_CLASS_MAP_FIELD; + + static { + try { + final Class minecraftKey; + final Class argumentRegistry; + + if (CraftBukkitReflection.findMCClass("resources.ResourceLocation") != null) { + minecraftKey = CraftBukkitReflection.needMCClass("resources.ResourceLocation"); + argumentRegistry = CraftBukkitReflection.needMCClass("commands.synchronization.ArgumentTypes"); + } else { + minecraftKey = CraftBukkitReflection.needNMSClassOrElse( + "MinecraftKey", + "net.minecraft.resources.MinecraftKey" + ); + argumentRegistry = CraftBukkitReflection.needNMSClassOrElse( + "ArgumentRegistry", + "net.minecraft.commands.synchronization.ArgumentRegistry" + ); + } + + MINECRAFT_KEY_CONSTRUCTOR = minecraftKey.getConstructor(String.class, String.class); + MINECRAFT_KEY_CONSTRUCTOR.setAccessible(true); + + ARGUMENT_REGISTRY_GET_BY_KEY_METHOD = Arrays.stream(argumentRegistry.getDeclaredMethods()) + .filter(method -> method.getParameterCount() == 1) + .filter(method -> minecraftKey.equals(method.getParameterTypes()[0])) + .findFirst().orElseThrow(NoSuchMethodException::new); + ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.setAccessible(true); + + BY_CLASS_MAP_FIELD = Arrays.stream(argumentRegistry.getDeclaredFields()) + .filter(field -> Modifier.isStatic(field.getModifiers())) + .filter(field -> field.getType().equals(Map.class)) + .filter(field -> { + final ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); + final Type param = parameterizedType.getActualTypeArguments()[0]; + if (!(param instanceof ParameterizedType)) { + return false; + } + return ((ParameterizedType) param).getRawType().equals(Class.class); + }) + .findFirst() + .orElseThrow(NoSuchFieldException::new); + BY_CLASS_MAP_FIELD.setAccessible(true); + } catch (final ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + @Override + public Class> getClassByKey(final @NonNull NamespacedKey key) throws IllegalArgumentException { + try { + Object minecraftKey = MINECRAFT_KEY_CONSTRUCTOR.newInstance(key.getNamespace(), key.getKey()); + Object entry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.invoke(null, minecraftKey); + if (entry == null) { + throw new IllegalArgumentException(key.toString()); + } + + final Map, Object> map = (Map, Object>) BY_CLASS_MAP_FIELD.get(null); + for (final Map.Entry, Object> mapEntry : map.entrySet()) { + if (mapEntry.getValue() == entry) { + return (Class>) mapEntry.getKey(); + } + } + throw new IllegalArgumentException(key.toString()); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/RegistryReflection.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/RegistryReflection.java new file mode 100644 index 00000000..3e512de8 --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/RegistryReflection.java @@ -0,0 +1,111 @@ +// +// MIT License +// +// Copyright (c) 2021 Alexander Söderberg & Contributors +// +// 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 cloud.commandframework.bukkit.internal; + +import com.google.common.annotations.Beta; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This is not API, and as such, may break, change, or be removed without any notice. + */ +@Beta +public final class RegistryReflection { + public static final @Nullable Field REGISTRY_REGISTRY; + public static final @Nullable Method REGISTRY_GET; + + private static final Class RESOURCE_LOCATION_CLASS = CraftBukkitReflection.needNMSClassOrElse( + "MinecraftKey", + "net.minecraft.resources.MinecraftKey", + "net.minecraft.resources.ResourceLocation" + ); + private static final Constructor RESOURCE_LOCATION_CTR = CraftBukkitReflection.needConstructor( + RESOURCE_LOCATION_CLASS, + String.class + ); + + private RegistryReflection() { + } + + static { + Class registryClass; + if (CraftBukkitReflection.MAJOR_REVISION < 17) { + REGISTRY_REGISTRY = null; + REGISTRY_GET = null; + } else { + registryClass = CraftBukkitReflection.firstNonNullOrThrow( + () -> "Registry", + CraftBukkitReflection.findMCClass("core.IRegistry"), + CraftBukkitReflection.findMCClass("core.Registry") + ); + final Class registryClassFinal = 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); + final Class resourceLocationClass = CraftBukkitReflection.firstNonNullOrThrow( + () -> "ResourceLocation class", + CraftBukkitReflection.findMCClass("resources.ResourceLocation"), + CraftBukkitReflection.findMCClass("resources.MinecraftKey") + ); + REGISTRY_GET = Arrays.stream(registryClass.getDeclaredMethods()) + .filter(it -> it.getParameterCount() == 1 + && it.getParameterTypes()[0].equals(resourceLocationClass) + && it.getReturnType().equals(Object.class)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Could not find Registry#get(ResourceLocation)")); + } + } + + public static Object get(final Object registry, final String resourceLocation) { + Objects.requireNonNull(REGISTRY_GET, "REGISTRY_GET"); + try { + return REGISTRY_GET.invoke(registry, RegistryReflection.createResourceLocation(resourceLocation)); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public static Object registryByName(final String name) { + Objects.requireNonNull(REGISTRY_REGISTRY, "REGISTRY_REGISTRY"); + try { + return get(REGISTRY_REGISTRY.get(null), name); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public static Object createResourceLocation(final String str) { + try { + return RESOURCE_LOCATION_CTR.newInstance(str); + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/BlockPredicateArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/BlockPredicateArgument.java index 46038f49..ed4aa48b 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/BlockPredicateArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/BlockPredicateArgument.java @@ -30,15 +30,15 @@ import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.data.BlockPredicate; +import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier; import cloud.commandframework.bukkit.internal.CraftBukkitReflection; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; +import cloud.commandframework.bukkit.internal.RegistryReflection; import cloud.commandframework.context.CommandContext; import com.mojang.brigadier.arguments.ArgumentType; import io.leangen.geantyref.TypeToken; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Queue; @@ -144,60 +144,19 @@ public final class BlockPredicateArgument extends CommandArgument implements ArgumentParser { private static final Class TAG_CONTAINER_CLASS; - private static final @Nullable Field REGISTRY_REGISTRY; - private static final @Nullable Method REGISTRY_GET; - private static final @Nullable Object BLOCK_REGISTRY_RESOURCE_LOCATION; static { Class tagContainerClass; if (CraftBukkitReflection.MAJOR_REVISION > 12 && CraftBukkitReflection.MAJOR_REVISION < 16) { tagContainerClass = CraftBukkitReflection.needNMSClass("TagRegistry"); - REGISTRY_REGISTRY = null; - REGISTRY_GET = null; - BLOCK_REGISTRY_RESOURCE_LOCATION = null; } else { - tagContainerClass = CraftBukkitReflection.firstNonNullOrNull( + tagContainerClass = CraftBukkitReflection.firstNonNullOrThrow( + () -> "tagContainerClass", CraftBukkitReflection.findNMSClass("ITagRegistry"), CraftBukkitReflection.findMCClass("tags.ITagRegistry"), - CraftBukkitReflection.findMCClass("tags.TagContainer") + CraftBukkitReflection.findMCClass("tags.TagContainer"), + String.class // fail ); - if (tagContainerClass == null) { - tagContainerClass = CraftBukkitReflection.firstNonNullOrThrow( - () -> "Registry", - CraftBukkitReflection.findMCClass("core.IRegistry"), - CraftBukkitReflection.findMCClass("core.Registry") - ); - final Class tagContainerClassFinal = tagContainerClass; - REGISTRY_REGISTRY = Arrays.stream(tagContainerClass.getDeclaredFields()) - .filter(it -> it.getType().equals(tagContainerClassFinal)) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Could not find Registry Registry field")); - REGISTRY_REGISTRY.setAccessible(true); - final Class resourceLocationClass = CraftBukkitReflection.firstNonNullOrThrow( - () -> "ResourceLocation class", - CraftBukkitReflection.findMCClass("resources.ResourceLocation"), - CraftBukkitReflection.findMCClass("resources.MinecraftKey") - ); - REGISTRY_GET = Arrays.stream(tagContainerClass.getDeclaredMethods()) - .filter(it -> it.getParameterCount() == 1 - && it.getParameterTypes()[0].equals(resourceLocationClass) - && it.getReturnType().equals(Object.class)) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Could not find Registry#get(ResourceLocation)")); - final Constructor resourceLocationCtr = CraftBukkitReflection.needConstructor( - resourceLocationClass, - String.class - ); - try { - BLOCK_REGISTRY_RESOURCE_LOCATION = resourceLocationCtr.newInstance("block"); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } else { - REGISTRY_REGISTRY = null; - REGISTRY_GET = null; - BLOCK_REGISTRY_RESOURCE_LOCATION = null; - } } TAG_CONTAINER_CLASS = tagContainerClass; } @@ -244,8 +203,7 @@ public final class BlockPredicateArgument extends CommandArgument SHAPE_DETECTOR_BLOCK_CTR = CraftBukkitReflection .needConstructor(SHAPE_DETECTOR_BLOCK_CLASS, LEVEL_READER_CLASS, BLOCK_POSITION_CLASS, boolean.class); private static final Method GET_HANDLE_METHOD = CraftBukkitReflection.needMethod(CRAFT_WORLD_CLASS, "getHandle"); - private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrThrow( - () -> "create on BlockPredicateArgument$Result", + private static final @Nullable Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrNull( CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_CONTAINER_CLASS), CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "a", TAG_CONTAINER_CLASS) ); @@ -279,22 +237,30 @@ public final class BlockPredicateArgument extends CommandArgument createParser() throws ReflectiveOperationException { - return new WrappedBrigadierParser( - (ArgumentType) ARGUMENT_BLOCK_PREDICATE_CLASS.getConstructor().newInstance() - ).map((ctx, result) -> { + final Constructor ctr = ARGUMENT_BLOCK_PREDICATE_CLASS.getDeclaredConstructors()[0]; + final ArgumentType inst; + if (ctr.getParameterCount() == 0) { + inst = (ArgumentType) ctr.newInstance(); + } else { + // 1.19+ + inst = (ArgumentType) ctr.newInstance(CommandBuildContextSupplier.commandBuildContext()); + } + return new WrappedBrigadierParser(inst).map((ctx, result) -> { + if (result instanceof Predicate) { + // 1.19+ + return ArgumentParseResult.success(new BlockPredicateImpl((Predicate) result)); + } final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER); try { final Object server = GET_SERVER_METHOD.invoke(commandSourceStack); - final Object tagRegistry; + final Object obj; if (GET_TAG_REGISTRY_METHOD != null) { - tagRegistry = GET_TAG_REGISTRY_METHOD.invoke(server); + obj = GET_TAG_REGISTRY_METHOD.invoke(server); } else { - Objects.requireNonNull(REGISTRY_GET, "REGISTRY_GET"); - Objects.requireNonNull(REGISTRY_REGISTRY, "REGISTRY_REGISTRY"); - final Object registryRegistry = REGISTRY_REGISTRY.get(null); - tagRegistry = REGISTRY_GET.invoke(registryRegistry, BLOCK_REGISTRY_RESOURCE_LOCATION); + obj = RegistryReflection.registryByName("block"); } - final Predicate predicate = (Predicate) CREATE_PREDICATE_METHOD.invoke(result, tagRegistry); + Objects.requireNonNull(CREATE_PREDICATE_METHOD, "create on BlockPredicateArgument$Result"); + final Predicate predicate = (Predicate) CREATE_PREDICATE_METHOD.invoke(result, obj); return ArgumentParseResult.success(new BlockPredicateImpl(predicate)); } catch (final ReflectiveOperationException ex) { throw new RuntimeException(ex); diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackArgument.java index cf3bdb3f..5450fd44 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackArgument.java @@ -30,11 +30,13 @@ import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.data.ProtoItemStack; +import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier; import cloud.commandframework.bukkit.internal.CraftBukkitReflection; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.context.CommandContext; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -179,7 +181,7 @@ public final class ItemStackArgument extends CommandArgument findItemInputClass() { - final Class[] classes = new Class[] { + final Class[] classes = new Class[]{ CraftBukkitReflection.findNMSClass("ArgumentPredicateItemStack"), CraftBukkitReflection.findMCClass("commands.arguments.item.ArgumentPredicateItemStack"), CraftBukkitReflection.findMCClass("commands.arguments.item.ItemInput") @@ -228,6 +230,10 @@ public final class ItemStackArgument extends CommandArgument HOLDER_CLASS = CraftBukkitReflection.findMCClass("core.Holder"); + private static final @Nullable Method VALUE_METHOD = HOLDER_CLASS == null + ? null + : CraftBukkitReflection.needMethod(HOLDER_CLASS, "value"); private final ArgumentParser parser; @@ -241,9 +247,16 @@ public final class ItemStackArgument extends CommandArgument createParser() throws ReflectiveOperationException { - return new WrappedBrigadierParser( - (ArgumentType) ARGUMENT_ITEM_STACK_CLASS.getConstructor().newInstance() - ).map((ctx, itemInput) -> ArgumentParseResult.success(new ModernProtoItemStack(itemInput))); + final Constructor ctr = ARGUMENT_ITEM_STACK_CLASS.getDeclaredConstructors()[0]; + final ArgumentType inst; + if (ctr.getParameterCount() == 0) { + inst = (ArgumentType) ctr.newInstance(); + } else { + // 1.19+ + inst = (ArgumentType) ctr.newInstance(CommandBuildContextSupplier.commandBuildContext()); + } + return new WrappedBrigadierParser(inst) + .map((ctx, itemInput) -> ArgumentParseResult.success(new ModernProtoItemStack(itemInput))); } @Override @@ -272,7 +285,11 @@ public final class ItemStackArgument extends CommandArgument extends CommandArgument "ItemPredicateArgument$Result#create", + private static final @Nullable Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrNull( CraftBukkitReflection.findMethod( ARGUMENT_ITEM_PREDICATE_RESULT_CLASS, "create", @@ -184,11 +186,22 @@ public final class ItemStackPredicateArgument extends CommandArgument createParser() throws ReflectiveOperationException { - return new WrappedBrigadierParser( - (ArgumentType) ARGUMENT_ITEM_PREDICATE_CLASS.getConstructor().newInstance() - ).map((ctx, result) -> { + final Constructor ctr = ARGUMENT_ITEM_PREDICATE_CLASS.getDeclaredConstructors()[0]; + final ArgumentType inst; + if (ctr.getParameterCount() == 0) { + inst = (ArgumentType) ctr.newInstance(); + } else { + // 1.19+ + inst = (ArgumentType) ctr.newInstance(CommandBuildContextSupplier.commandBuildContext()); + } + return new WrappedBrigadierParser(inst).map((ctx, result) -> { + if (result instanceof Predicate) { + // 1.19+ + return ArgumentParseResult.success(new ItemStackPredicateImpl((Predicate) result)); + } final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER); final com.mojang.brigadier.context.CommandContext dummy = createDummyContext(ctx, commandSourceStack); + Objects.requireNonNull(CREATE_PREDICATE_METHOD, "ItemPredicateArgument$Result#create"); try { final Predicate predicate = (Predicate) CREATE_PREDICATE_METHOD.invoke(result, dummy); return ArgumentParseResult.success(new ItemStackPredicateImpl(predicate));