From 0bb6625de608b033fc531b7101a76c5453cbbfed Mon Sep 17 00:00:00 2001 From: Jason <11360596+jpenilla@users.noreply.github.com> Date: Thu, 8 Dec 2022 13:43:51 -0700 Subject: [PATCH] Update cloud-bukkit & cloud-paper for Minecraft 1.19.3 (#412) --- .../bukkit/BukkitBrigadierMapper.java | 25 +++++- .../internal/CommandBuildContextSupplier.java | 79 +++++++++++++++++-- .../bukkit/internal/RegistryReflection.java | 60 ++++++++++++-- .../parsers/selector/SelectorUtils.java | 14 +++- 4 files changed, 163 insertions(+), 15 deletions(-) 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 ec6c7454..6b940642 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 @@ -29,6 +29,7 @@ import cloud.commandframework.brigadier.CloudBrigadierManager; import cloud.commandframework.bukkit.argument.NamespacedKeyArgument; import cloud.commandframework.bukkit.internal.CommandBuildContextSupplier; import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes; +import cloud.commandframework.bukkit.internal.RegistryReflection; import cloud.commandframework.bukkit.parsers.BlockPredicateArgument; import cloud.commandframework.bukkit.parsers.EnchantmentArgument; import cloud.commandframework.bukkit.parsers.ItemStackArgument; @@ -90,8 +91,17 @@ public final class BukkitBrigadierMapper { this.mapSimpleNMS(new TypeToken>() { }, "resource_location", true); /* Map Enchantment */ - this.mapSimpleNMS(new TypeToken>() { - }, "item_enchantment"); + try { + // Pre-1.19.3 + final Class> ench = MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft( + "item_enchantment")); + this.mapSimpleNMS(new TypeToken>() { + }, "item_enchantment"); + } catch (final IllegalArgumentException ignore) { + // 1.19.3+ + this.mapNMS(new TypeToken>() { + }, this::modernEnchantment); + } /* Map Item arguments */ this.mapSimpleContextNMS(new TypeToken>() { }, "item_stack"); @@ -117,6 +127,17 @@ public final class BukkitBrigadierMapper { }, 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 playersOnly Whether the selector is for players only (true), or for all entities (false) 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 index 87a582a7..f011c6f6 100644 --- 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 @@ -26,7 +26,9 @@ package cloud.commandframework.bukkit.internal; import com.google.common.annotations.Beta; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; 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. @@ -35,11 +37,61 @@ import java.util.Arrays; 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 @Nullable Constructor COMMAND_BUILD_CONTEXT_CTR; + 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( "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 REGISTRY_ACCESS = Arrays.stream(MC_SERVER_CLASS.getDeclaredMethods()) .filter(m -> REG_ACC_CLASS.isAssignableFrom(m.getReturnType())) @@ -58,11 +110,24 @@ public final class 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); + if (COMMAND_BUILD_CONTEXT_CTR != null) { + 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); + } + } 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(); } } } 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 index 3e7e5efe..1bfd3053 100644 --- 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 @@ -24,9 +24,14 @@ package cloud.commandframework.bukkit.internal; import com.google.common.annotations.Beta; +import io.leangen.geantyref.GenericTypeReflector; import java.lang.reflect.Constructor; import java.lang.reflect.Field; 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.Objects; 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 Method REGISTRY_GET; + public static final @Nullable Method REGISTRY_KEY; private static final Class RESOURCE_LOCATION_CLASS = CraftBukkitReflection.needNMSClassOrElse( "MinecraftKey", @@ -58,17 +64,14 @@ public final class RegistryReflection { if (CraftBukkitReflection.MAJOR_REVISION < 17) { REGISTRY_REGISTRY = null; REGISTRY_GET = null; + REGISTRY_KEY = 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 = registryRegistryField(registryClass); REGISTRY_REGISTRY.setAccessible(true); final Class resourceLocationClass = CraftBukkitReflection.firstNonNullOrThrow( () -> "ResourceLocation class", @@ -81,6 +84,21 @@ public final class RegistryReflection { && it.getReturnType().equals(Object.class)) .findFirst() .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); } } + + 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> 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")); + } } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/SelectorUtils.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/SelectorUtils.java index 218b09da..7d5c51a4 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/SelectorUtils.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/SelectorUtils.java @@ -97,6 +97,7 @@ final class SelectorUtils { } private static final class EntityArgumentParseFunction implements WrappedBrigadierParser.ParseFunction { + static final EntityArgumentParseFunction INSTANCE = new EntityArgumentParseFunction(); @Override @@ -132,6 +133,7 @@ final class SelectorUtils { } private abstract static class SelectorParser implements ArgumentParser, SelectorMapper { + protected static final Supplier NO_PLAYERS_EXCEPTION_TYPE = Suppliers.memoize(() -> findExceptionType("argument.entity.notfound.player")); protected static final Supplier NO_ENTITIES_EXCEPTION_TYPE = @@ -141,6 +143,7 @@ final class SelectorUtils { // Hide brigadier references in inner class protected static final class Thrower { + private final Object type; Thrower(final Object simpleCommandExceptionType) { @@ -228,12 +231,14 @@ final class SelectorUtils { } abstract static class EntitySelectorParser extends SelectorParser { + protected EntitySelectorParser(final boolean single) { super(single, false); } } abstract static class PlayerSelectorParser extends SelectorParser { + protected PlayerSelectorParser(final boolean single) { super(single, true); } @@ -258,6 +263,7 @@ final class SelectorUtils { } private static class ModernSelectorParser implements ArgumentParser { + private final ArgumentParser wrappedBrigadierParser; private final SelectorMapper mapper; @@ -326,12 +332,14 @@ final class SelectorUtils { } static final class EntitySelectorWrapper { + private static volatile @MonotonicNonNull Methods methods; private final CommandContext commandContext; private final Object selector; private static final class Methods { + private @MonotonicNonNull Method getBukkitEntity; private @MonotonicNonNull Method entity; private @MonotonicNonNull Method player; @@ -342,7 +350,9 @@ final class SelectorUtils { final Object nativeSender = commandContext.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER); final Class nativeSenderClass = nativeSender.getClass(); 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; } @@ -475,6 +485,7 @@ final class SelectorUtils { @FunctionalInterface interface ReflectiveOperation { + T run() throws ReflectiveOperationException; } @@ -494,6 +505,7 @@ final class SelectorUtils { @FunctionalInterface interface SelectorMapper { + T mapResult(String input, EntitySelectorWrapper wrapper) throws Exception; // throws CommandSyntaxException }