bukkit/paper: Update reflection for Minecraft 1.19 (#374)

This commit is contained in:
Jason 2022-06-09 01:52:32 -07:00
parent 2572b73c4b
commit 1fe1b4a0d3
8 changed files with 387 additions and 137 deletions

View file

@ -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)) - 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), - 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)) [#371](https://github.com/Incendo/cloud/pull/371))
- Bukkit/Paper: Full support for Minecraft 1.19
### Fixed ### Fixed
- Core: Fix missing caption registration for the regex caption ([#351](https://github.com/Incendo/cloud/pull/351)) - Core: Fix missing caption registration for the regex caption ([#351](https://github.com/Incendo/cloud/pull/351))

View file

@ -26,6 +26,7 @@ package cloud.commandframework.bukkit;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.arguments.standard.UUIDArgument; import cloud.commandframework.arguments.standard.UUIDArgument;
import cloud.commandframework.brigadier.CloudBrigadierManager; import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier;
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
import cloud.commandframework.bukkit.parsers.BlockPredicateArgument; import cloud.commandframework.bukkit.parsers.BlockPredicateArgument;
import cloud.commandframework.bukkit.parsers.EnchantmentArgument; import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
@ -88,12 +89,12 @@ public final class BukkitBrigadierMapper<C> {
this.mapSimpleNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() { this.mapSimpleNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() {
}, "item_enchantment"); }, "item_enchantment");
/* Map Item arguments */ /* Map Item arguments */
this.mapSimpleNMS(new TypeToken<ItemStackArgument.Parser<C>>() { this.mapSimpleContextNMS(new TypeToken<ItemStackArgument.Parser<C>>() {
}, "item_stack"); }, "item_stack");
this.mapSimpleNMS(new TypeToken<ItemStackPredicateArgument.Parser<C>>() { this.mapSimpleContextNMS(new TypeToken<ItemStackPredicateArgument.Parser<C>>() {
}, "item_predicate"); }, "item_predicate");
/* Map Block arguments */ /* Map Block arguments */
this.mapSimpleNMS(new TypeToken<BlockPredicateArgument.Parser<C>>() { this.mapSimpleContextNMS(new TypeToken<BlockPredicateArgument.Parser<C>>() {
}, "block_predicate"); }, "block_predicate");
/* Map Entity Selectors */ /* Map Entity Selectors */
this.mapNMS(new TypeToken<SingleEntitySelectorArgument.SingleEntitySelectorParser<C>>() { this.mapNMS(new TypeToken<SingleEntitySelectorArgument.SingleEntitySelectorParser<C>>() {
@ -156,13 +157,37 @@ public final class BukkitBrigadierMapper<C> {
} }
} }
/**
* 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 <T> argument parser type
* @param argumentId registry id of argument type
* @since 1.7.0
*/
public <T extends ArgumentParser<C, ?>> void mapSimpleContextNMS(
final @NonNull TypeToken<T> 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 * Attempt to register a mapping between a cloud argument parser type and an NMS brigadier argument type which
* has a no-args constructor. * has a no-args constructor.
* *
* @param type Type to map * @param type Type to map
* @param <T> argument parser type * @param <T> argument parser type
* @param argumentId network id of argument type * @param argumentId registry id of argument type
* @since 1.5.0 * @since 1.5.0
*/ */
public <T extends ArgumentParser<C, ?>> void mapSimpleNMS( public <T extends ArgumentParser<C, ?>> void mapSimpleNMS(
@ -178,7 +203,7 @@ public final class BukkitBrigadierMapper<C> {
* *
* @param type Type to map * @param type Type to map
* @param <T> argument parser type * @param <T> argument parser type
* @param argumentId network id of argument type * @param argumentId registry id of argument type
* @param useCloudSuggestions whether to use cloud suggestions * @param useCloudSuggestions whether to use cloud suggestions
* @since 1.6.0 * @since 1.6.0
*/ */

View file

@ -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);
}
}
}

View file

@ -62,8 +62,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* A registry of the {@link ArgumentType}s provided by Minecraft. * A registry of the {@link ArgumentType}s provided by Minecraft.
* <p>
* This file is taken from MIT licensed code in commodore (https://github.com/lucko/commodore).
* *
* <p>This is not API, and as such, may break, change, or be removed without any notice.</p> * <p>This is not API, and as such, may break, change, or be removed without any notice.</p>
*/ */
@ -73,6 +71,64 @@ public final class MinecraftArgumentTypes {
private MinecraftArgumentTypes() { private MinecraftArgumentTypes() {
} }
private static final ArgumentTypeGetter ARGUMENT_TYPE_GETTER;
static {
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
}
}
/**
* Gets a registered argument type class by key.
*
* @param key the key
* @return the returned argument type class
* @throws IllegalArgumentException if no such argument is registered
*/
public static Class<? extends ArgumentType<?>> getClassByKey(
final @NonNull NamespacedKey key
) throws IllegalArgumentException {
return ARGUMENT_TYPE_GETTER.getClassByKey(key);
}
private interface ArgumentTypeGetter {
Class<? extends ArgumentType<?>> 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<? extends ArgumentType<?>> 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<? extends ArgumentType<?>>) entry.getKey();
}
}
throw new IllegalArgumentException(key.toString());
}
}
@SuppressWarnings("unchecked")
private static final class LegacyArgumentTypeGetter implements ArgumentTypeGetter {
private static final Constructor<?> MINECRAFT_KEY_CONSTRUCTOR; private static final Constructor<?> MINECRAFT_KEY_CONSTRUCTOR;
private static final Method ARGUMENT_REGISTRY_GET_BY_KEY_METHOD; private static final Method ARGUMENT_REGISTRY_GET_BY_KEY_METHOD;
private static final Field BY_CLASS_MAP_FIELD; private static final Field BY_CLASS_MAP_FIELD;
@ -119,22 +175,13 @@ public final class MinecraftArgumentTypes {
.findFirst() .findFirst()
.orElseThrow(NoSuchFieldException::new); .orElseThrow(NoSuchFieldException::new);
BY_CLASS_MAP_FIELD.setAccessible(true); BY_CLASS_MAP_FIELD.setAccessible(true);
} catch (ReflectiveOperationException e) { } catch (final ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e); throw new ExceptionInInitializerError(e);
} }
} }
/** @Override
* Gets a registered argument type class by key. public Class<? extends ArgumentType<?>> getClassByKey(final @NonNull NamespacedKey key) throws IllegalArgumentException {
*
* @param key the key
* @return the returned argument type class
* @throws IllegalArgumentException if no such argument is registered
*/
@SuppressWarnings("unchecked")
public static Class<? extends ArgumentType<?>> getClassByKey(
final @NonNull NamespacedKey key
) throws IllegalArgumentException {
try { try {
Object minecraftKey = MINECRAFT_KEY_CONSTRUCTOR.newInstance(key.getNamespace(), key.getKey()); Object minecraftKey = MINECRAFT_KEY_CONSTRUCTOR.newInstance(key.getNamespace(), key.getKey());
Object entry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.invoke(null, minecraftKey); Object entry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.invoke(null, minecraftKey);
@ -153,5 +200,6 @@ public final class MinecraftArgumentTypes {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
}
} }

View file

@ -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);
}
}
}

View file

@ -30,15 +30,15 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.BukkitCommandManager;
import cloud.commandframework.bukkit.data.BlockPredicate; import cloud.commandframework.bukkit.data.BlockPredicate;
import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier;
import cloud.commandframework.bukkit.internal.CraftBukkitReflection; import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
import cloud.commandframework.bukkit.internal.RegistryReflection;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import io.leangen.geantyref.TypeToken; import io.leangen.geantyref.TypeToken;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Queue; import java.util.Queue;
@ -144,60 +144,19 @@ public final class BlockPredicateArgument<C> extends CommandArgument<C, BlockPre
public static final class Parser<C> implements ArgumentParser<C, BlockPredicate> { public static final class Parser<C> implements ArgumentParser<C, BlockPredicate> {
private static final Class<?> TAG_CONTAINER_CLASS; 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 { static {
Class<?> tagContainerClass; Class<?> tagContainerClass;
if (CraftBukkitReflection.MAJOR_REVISION > 12 && CraftBukkitReflection.MAJOR_REVISION < 16) { if (CraftBukkitReflection.MAJOR_REVISION > 12 && CraftBukkitReflection.MAJOR_REVISION < 16) {
tagContainerClass = CraftBukkitReflection.needNMSClass("TagRegistry"); tagContainerClass = CraftBukkitReflection.needNMSClass("TagRegistry");
REGISTRY_REGISTRY = null;
REGISTRY_GET = null;
BLOCK_REGISTRY_RESOURCE_LOCATION = null;
} else { } else {
tagContainerClass = CraftBukkitReflection.firstNonNullOrNull( tagContainerClass = CraftBukkitReflection.firstNonNullOrThrow(
() -> "tagContainerClass",
CraftBukkitReflection.findNMSClass("ITagRegistry"), CraftBukkitReflection.findNMSClass("ITagRegistry"),
CraftBukkitReflection.findMCClass("tags.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; TAG_CONTAINER_CLASS = tagContainerClass;
} }
@ -244,8 +203,7 @@ public final class BlockPredicateArgument<C> extends CommandArgument<C, BlockPre
private static final Constructor<?> SHAPE_DETECTOR_BLOCK_CTR = CraftBukkitReflection private static final Constructor<?> SHAPE_DETECTOR_BLOCK_CTR = CraftBukkitReflection
.needConstructor(SHAPE_DETECTOR_BLOCK_CLASS, LEVEL_READER_CLASS, BLOCK_POSITION_CLASS, boolean.class); .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 GET_HANDLE_METHOD = CraftBukkitReflection.needMethod(CRAFT_WORLD_CLASS, "getHandle");
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrThrow( private static final @Nullable Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrNull(
() -> "create on BlockPredicateArgument$Result",
CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_CONTAINER_CLASS), CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_CONTAINER_CLASS),
CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "a", TAG_CONTAINER_CLASS) CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "a", TAG_CONTAINER_CLASS)
); );
@ -279,22 +237,30 @@ public final class BlockPredicateArgument<C> extends CommandArgument<C, BlockPre
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private ArgumentParser<C, BlockPredicate> createParser() throws ReflectiveOperationException { private ArgumentParser<C, BlockPredicate> createParser() throws ReflectiveOperationException {
return new WrappedBrigadierParser<C, Object>( final Constructor<?> ctr = ARGUMENT_BLOCK_PREDICATE_CLASS.getDeclaredConstructors()[0];
(ArgumentType<Object>) ARGUMENT_BLOCK_PREDICATE_CLASS.getConstructor().newInstance() final ArgumentType<Object> inst;
).map((ctx, result) -> { if (ctr.getParameterCount() == 0) {
inst = (ArgumentType<Object>) ctr.newInstance();
} else {
// 1.19+
inst = (ArgumentType<Object>) ctr.newInstance(CommandBuildContextSupplier.commandBuildContext());
}
return new WrappedBrigadierParser<C, Object>(inst).map((ctx, result) -> {
if (result instanceof Predicate) {
// 1.19+
return ArgumentParseResult.success(new BlockPredicateImpl((Predicate<Object>) result));
}
final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER); final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
try { try {
final Object server = GET_SERVER_METHOD.invoke(commandSourceStack); final Object server = GET_SERVER_METHOD.invoke(commandSourceStack);
final Object tagRegistry; final Object obj;
if (GET_TAG_REGISTRY_METHOD != null) { if (GET_TAG_REGISTRY_METHOD != null) {
tagRegistry = GET_TAG_REGISTRY_METHOD.invoke(server); obj = GET_TAG_REGISTRY_METHOD.invoke(server);
} else { } else {
Objects.requireNonNull(REGISTRY_GET, "REGISTRY_GET"); obj = RegistryReflection.registryByName("block");
Objects.requireNonNull(REGISTRY_REGISTRY, "REGISTRY_REGISTRY");
final Object registryRegistry = REGISTRY_REGISTRY.get(null);
tagRegistry = REGISTRY_GET.invoke(registryRegistry, BLOCK_REGISTRY_RESOURCE_LOCATION);
} }
final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, tagRegistry); Objects.requireNonNull(CREATE_PREDICATE_METHOD, "create on BlockPredicateArgument$Result");
final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, obj);
return ArgumentParseResult.success(new BlockPredicateImpl(predicate)); return ArgumentParseResult.success(new BlockPredicateImpl(predicate));
} catch (final ReflectiveOperationException ex) { } catch (final ReflectiveOperationException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);

View file

@ -30,11 +30,13 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.BukkitCommandManager;
import cloud.commandframework.bukkit.data.ProtoItemStack; import cloud.commandframework.bukkit.data.ProtoItemStack;
import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier;
import cloud.commandframework.bukkit.internal.CraftBukkitReflection; import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -228,6 +230,10 @@ public final class ItemStackArgument<C> extends CommandArgument<C, ProtoItemStac
CraftBukkitReflection.findField(ITEM_INPUT_CLASS, "c"), CraftBukkitReflection.findField(ITEM_INPUT_CLASS, "c"),
CraftBukkitReflection.findField(ITEM_INPUT_CLASS, "tag") CraftBukkitReflection.findField(ITEM_INPUT_CLASS, "tag")
); );
private static final Class<?> 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<C, ProtoItemStack> parser; private final ArgumentParser<C, ProtoItemStack> parser;
@ -241,9 +247,16 @@ public final class ItemStackArgument<C> extends CommandArgument<C, ProtoItemStac
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private ArgumentParser<C, ProtoItemStack> createParser() throws ReflectiveOperationException { private ArgumentParser<C, ProtoItemStack> createParser() throws ReflectiveOperationException {
return new WrappedBrigadierParser<C, Object>( final Constructor<?> ctr = ARGUMENT_ITEM_STACK_CLASS.getDeclaredConstructors()[0];
(ArgumentType<Object>) ARGUMENT_ITEM_STACK_CLASS.getConstructor().newInstance() final ArgumentType<Object> inst;
).map((ctx, itemInput) -> ArgumentParseResult.success(new ModernProtoItemStack(itemInput))); if (ctr.getParameterCount() == 0) {
inst = (ArgumentType<Object>) ctr.newInstance();
} else {
// 1.19+
inst = (ArgumentType<Object>) ctr.newInstance(CommandBuildContextSupplier.commandBuildContext());
}
return new WrappedBrigadierParser<C, Object>(inst)
.map((ctx, itemInput) -> ArgumentParseResult.success(new ModernProtoItemStack(itemInput)));
} }
@Override @Override
@ -272,7 +285,11 @@ public final class ItemStackArgument<C> extends CommandArgument<C, ProtoItemStac
ModernProtoItemStack(final @NonNull Object itemInput) { ModernProtoItemStack(final @NonNull Object itemInput) {
this.itemInput = itemInput; this.itemInput = itemInput;
try { try {
this.material = (Material) GET_MATERIAL_METHOD.invoke(null, ITEM_FIELD.get(itemInput)); Object item = ITEM_FIELD.get(itemInput);
if (HOLDER_CLASS != null && HOLDER_CLASS.isInstance(item)) {
item = VALUE_METHOD.invoke(item);
}
this.material = (Material) GET_MATERIAL_METHOD.invoke(null, item);
final Object compoundTag = COMPOUND_TAG_FIELD.get(itemInput); final Object compoundTag = COMPOUND_TAG_FIELD.get(itemInput);
if (compoundTag != null) { if (compoundTag != null) {
this.snbt = compoundTag.toString(); this.snbt = compoundTag.toString();

View file

@ -30,15 +30,18 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.BukkitCommandManager;
import cloud.commandframework.bukkit.data.ItemStackPredicate; import cloud.commandframework.bukkit.data.ItemStackPredicate;
import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier;
import cloud.commandframework.bukkit.internal.CraftBukkitReflection; import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.context.StringRange;
import io.leangen.geantyref.TypeToken; import io.leangen.geantyref.TypeToken;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -151,8 +154,7 @@ public final class ItemStackPredicateArgument<C> extends CommandArgument<C, Item
CraftBukkitReflection.findMCClass("commands.arguments.item.ArgumentItemPredicate$b"), CraftBukkitReflection.findMCClass("commands.arguments.item.ArgumentItemPredicate$b"),
CraftBukkitReflection.findMCClass("commands.arguments.item.ItemPredicateArgument$Result") CraftBukkitReflection.findMCClass("commands.arguments.item.ItemPredicateArgument$Result")
); );
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrThrow( private static final @Nullable Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrNull(
() -> "ItemPredicateArgument$Result#create",
CraftBukkitReflection.findMethod( CraftBukkitReflection.findMethod(
ARGUMENT_ITEM_PREDICATE_RESULT_CLASS, ARGUMENT_ITEM_PREDICATE_RESULT_CLASS,
"create", "create",
@ -184,11 +186,22 @@ public final class ItemStackPredicateArgument<C> extends CommandArgument<C, Item
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private ArgumentParser<C, ItemStackPredicate> createParser() throws ReflectiveOperationException { private ArgumentParser<C, ItemStackPredicate> createParser() throws ReflectiveOperationException {
return new WrappedBrigadierParser<C, Object>( final Constructor<?> ctr = ARGUMENT_ITEM_PREDICATE_CLASS.getDeclaredConstructors()[0];
(ArgumentType<Object>) ARGUMENT_ITEM_PREDICATE_CLASS.getConstructor().newInstance() final ArgumentType<Object> inst;
).map((ctx, result) -> { if (ctr.getParameterCount() == 0) {
inst = (ArgumentType<Object>) ctr.newInstance();
} else {
// 1.19+
inst = (ArgumentType<Object>) ctr.newInstance(CommandBuildContextSupplier.commandBuildContext());
}
return new WrappedBrigadierParser<C, Object>(inst).map((ctx, result) -> {
if (result instanceof Predicate) {
// 1.19+
return ArgumentParseResult.success(new ItemStackPredicateImpl((Predicate<Object>) result));
}
final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER); final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
final com.mojang.brigadier.context.CommandContext<Object> dummy = createDummyContext(ctx, commandSourceStack); final com.mojang.brigadier.context.CommandContext<Object> dummy = createDummyContext(ctx, commandSourceStack);
Objects.requireNonNull(CREATE_PREDICATE_METHOD, "ItemPredicateArgument$Result#create");
try { try {
final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, dummy); final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, dummy);
return ArgumentParseResult.success(new ItemStackPredicateImpl(predicate)); return ArgumentParseResult.success(new ItemStackPredicateImpl(predicate));