From 66c803852ca91b8b46ecece357c3968888c04a47 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Thu, 25 Nov 2021 14:23:19 -0800 Subject: [PATCH] Update CraftBukkit reflection for 1.18 --- .../internal/CraftBukkitReflection.java | 27 ++++- .../parsers/BlockPredicateArgument.java | 23 +++- .../parsers/ItemStackPredicateArgument.java | 16 ++- examples/example-bukkit/build.gradle.kts | 8 +- .../examples/bukkit/ExamplePlugin.java | 45 +------- .../examples/bukkit/Mc113.java | 102 ++++++++++++++++++ 6 files changed, 162 insertions(+), 59 deletions(-) create mode 100644 examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/Mc113.java diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CraftBukkitReflection.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CraftBukkitReflection.java index 223ea3de..0dd17846 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CraftBukkitReflection.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CraftBukkitReflection.java @@ -32,7 +32,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Stream; /** * Utilities for doing reflection on CraftBukkit, used by the cloud implementation. @@ -65,8 +67,7 @@ public final class CraftBukkitReflection { } @SafeVarargs - public static @NonNull T firstNonNullOrThrow( - final @NonNull Supplier<@NonNull String> errorMessage, + public static @Nullable T firstNonNullOrNull( final @Nullable T @NonNull... elements ) { for (final T element : elements) { @@ -74,7 +75,20 @@ public final class CraftBukkitReflection { return element; } } - throw new IllegalArgumentException(errorMessage.get()); + return null; + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static @NonNull T firstNonNullOrThrow( + final @NonNull Supplier<@NonNull String> errorMessage, + final @Nullable T @NonNull... elements + ) { + final @Nullable T t = firstNonNullOrNull(elements); + if (t == null) { + throw new IllegalArgumentException(errorMessage.get()); + } + return t; } public static @NonNull Class needNMSClassOrElse( @@ -191,6 +205,13 @@ public final class CraftBukkitReflection { } } + public static T streamMethods( + final @NonNull Class clazz, + final @NonNull Function, T> function + ) { + return function.apply(Arrays.stream(clazz.getDeclaredMethods())); + } + private CraftBukkitReflection() { } 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 36b65eea..9849a6bd 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 @@ -193,19 +193,32 @@ public final class BlockPredicateArgument extends CommandArgument TAG_CONTAINER_CLASS = CraftBukkitReflection.firstNonNullOrNull( + CraftBukkitReflection.findClass("net.minecraft.tags.TagContainer"), + CraftBukkitReflection.findClass("net.minecraft.tags.ITagRegistry") + ); private static final Constructor BLOCK_POSITION_CTR = CraftBukkitReflection.needConstructor(BLOCK_POSITION_CLASS, int.class, int.class, int.class); private static final Constructor 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.needMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_REGISTRY_CLASS); - private static final Method GET_SERVER_METHOD = - CraftBukkitReflection.needMethod(COMMAND_LISTENER_WRAPPER_CLASS, "getServer"); + private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrThrow( + () -> "create on BlockPredicateArgument$Result", + CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_REGISTRY_CLASS), + CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "a", TAG_REGISTRY_CLASS) + ); + private static final Method GET_SERVER_METHOD = CraftBukkitReflection.streamMethods( + COMMAND_LISTENER_WRAPPER_CLASS, + stream -> stream.filter(it -> it.getReturnType().equals(MINECRAFT_SERVER_CLASS) && it.getParameterCount() == 0) + .findFirst().orElseThrow(() -> new IllegalStateException("Could not find CommandSourceStack#getServer.")) + ); private static final Method GET_TAG_REGISTRY_METHOD = CraftBukkitReflection.firstNonNullOrThrow( () -> "getTags method on MinecraftServer", CraftBukkitReflection.findMethod(MINECRAFT_SERVER_CLASS, "getTagRegistry"), - CraftBukkitReflection.findMethod(MINECRAFT_SERVER_CLASS, "getTags") + CraftBukkitReflection.findMethod(MINECRAFT_SERVER_CLASS, "getTags"), + TAG_CONTAINER_CLASS == null ? null : CraftBukkitReflection.streamMethods(MINECRAFT_SERVER_CLASS, stream -> + stream.filter(it -> it.getReturnType().equals(TAG_CONTAINER_CLASS) && it.getParameterCount() == 0) + .findFirst().orElse(null)) ); private final ArgumentParser parser; diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackPredicateArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackPredicateArgument.java index b9fd36cf..162043d2 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackPredicateArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackPredicateArgument.java @@ -152,10 +152,18 @@ public final class ItemStackPredicateArgument extends CommandArgument "ItemPredicateArgument$Result#create", + CraftBukkitReflection.findMethod( + ARGUMENT_ITEM_PREDICATE_RESULT_CLASS, + "create", + com.mojang.brigadier.context.CommandContext.class + ), + CraftBukkitReflection.findMethod( + ARGUMENT_ITEM_PREDICATE_RESULT_CLASS, + "a", + com.mojang.brigadier.context.CommandContext.class + ) ); private static final Method AS_NMS_COPY_METHOD = CraftBukkitReflection.needMethod(CRAFT_ITEM_STACK_CLASS, "asNMSCopy", ItemStack.class); diff --git a/examples/example-bukkit/build.gradle.kts b/examples/example-bukkit/build.gradle.kts index a55b4012..649a86e0 100644 --- a/examples/example-bukkit/build.gradle.kts +++ b/examples/example-bukkit/build.gradle.kts @@ -33,15 +33,14 @@ tasks { minecraftVersion("1.17.1") runDirectory(file("run/latest")) javaLauncher.set(project.javaToolchains.launcherFor { - languageVersion.set(JavaLanguageVersion.of(16)) + languageVersion.set(JavaLanguageVersion.of(17)) }) } // Setup a run task for each supported version mapOf( - setOf("1.8.8", "1.9.4", "1.10.2", "1.11.2", "1.12.2") to 8, - setOf("1.13.2", "1.14.4", "1.15.2") to 11, - setOf("1.16.5", "1.17.1") to 16 + setOf("1.8.8", "1.9.4", "1.10.2", "1.11.2") to 11, + setOf("1.12.2", "1.13.2", "1.14.4", "1.15.2", "1.16.5", "1.17.1") to 17, ).forEach { (minecraftVersions, javaVersion) -> for (version in minecraftVersions) { createVersionedRun(version, javaVersion) @@ -57,6 +56,7 @@ fun TaskContainerScope.createVersionedRun( pluginJars.from(shadowJar.flatMap { it.archiveFile }) minecraftVersion(version) runDirectory(file("run/$version")) + systemProperty("Paper.IgnoreJavaVersion", true) javaLauncher.set(project.javaToolchains.launcherFor { languageVersion.set(JavaLanguageVersion.of(javaVersion)) }) diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java index f8293baf..1972f989 100644 --- a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java @@ -44,11 +44,8 @@ import cloud.commandframework.arguments.standard.StringArrayArgument; import cloud.commandframework.bukkit.BukkitCommandManager; import cloud.commandframework.bukkit.CloudBukkitCapabilities; import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector; -import cloud.commandframework.bukkit.data.ItemStackPredicate; import cloud.commandframework.bukkit.data.ProtoItemStack; import cloud.commandframework.bukkit.parsers.EnchantmentArgument; -import cloud.commandframework.bukkit.parsers.ItemStackArgument; -import cloud.commandframework.bukkit.parsers.ItemStackPredicateArgument; import cloud.commandframework.bukkit.parsers.MaterialArgument; import cloud.commandframework.bukkit.parsers.WorldArgument; import cloud.commandframework.bukkit.parsers.selector.SingleEntitySelectorArgument; @@ -383,47 +380,9 @@ public final class ExamplePlugin extends JavaPlugin { ))) ); - // MC 1.13+ commands todo: move to separate class + // Commands using MC 1.13+ argument types if (this.manager.queryCapability(CloudBukkitCapabilities.BRIGADIER)) { - /* - this.manager.command(builder.literal("replace") - .senderType(Player.class) - .argument(BlockPredicateArgument.of("predicate")) - .literal("with") - .argument(MaterialArgument.of("block")) // todo: use BlockDataArgument - .argument(IntegerArgument.newBuilder("radius").withMin(1)) - .handler(ctx -> { - final BlockData block = ctx.get("block").createBlockData(); - final BlockPredicate predicate = ctx.get("predicate"); - final int radius = ctx.get("radius"); - - final Player player = (Player) ctx.getSender(); - final Location loc = player.getLocation(); - - this.manager.taskRecipe().begin(ctx).synchronous(context -> { - for (double x = loc.getX() - radius; x < loc.getX() + radius; x++) { - for (double y = loc.getY() - radius; y < loc.getY() + radius; y++) { - for (double z = loc.getZ() - radius; z < loc.getZ() + radius; z++) { - final Block blockAt = player.getWorld().getBlockAt((int) x, (int) y, (int) z); - if (predicate.test(blockAt)) { - blockAt.setBlockData(block); - } - } - } - } - }).execute(); - })); - */ - this.manager.command(builder.literal("test_item") - .argument(ItemStackArgument.of("item")) - .literal("is") - .argument(ItemStackPredicateArgument.of("predicate")) - .handler(ctx -> { - final ProtoItemStack protoItemStack = ctx.get("item"); - final ItemStackPredicate predicate = ctx.get("predicate"); - ctx.getSender().sendMessage("result: " + predicate.test( - protoItemStack.createItemStack(1, true))); - })); + new Mc113(this.manager).registerCommands(); } // diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/Mc113.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/Mc113.java new file mode 100644 index 00000000..6d49e7b5 --- /dev/null +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/Mc113.java @@ -0,0 +1,102 @@ +// +// 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.examples.bukkit; + +import cloud.commandframework.Command; +import cloud.commandframework.arguments.standard.IntegerArgument; +import cloud.commandframework.bukkit.BukkitCommandManager; +import cloud.commandframework.bukkit.data.BlockPredicate; +import cloud.commandframework.bukkit.data.ItemStackPredicate; +import cloud.commandframework.bukkit.data.ProtoItemStack; +import cloud.commandframework.bukkit.parsers.BlockPredicateArgument; +import cloud.commandframework.bukkit.parsers.ItemStackArgument; +import cloud.commandframework.bukkit.parsers.ItemStackPredicateArgument; +import cloud.commandframework.bukkit.parsers.MaterialArgument; +import cloud.commandframework.context.CommandContext; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +final class Mc113 { + private final BukkitCommandManager manager; + + Mc113(final BukkitCommandManager manager) { + this.manager = manager; + } + + void registerCommands() { + final Command.Builder builder = this.manager.commandBuilder("example") + .literal("mc113"); + + this.manager.command(builder.literal("replace") + .senderType(Player.class) + .argument(BlockPredicateArgument.of("predicate")) + .literal("with") + .argument(MaterialArgument.of("block")) // todo: use BlockDataArgument + .argument(IntegerArgument.newBuilder("radius").withMin(1)) + .handler(this::executeReplace)); + + this.manager.command(builder.literal("test_item") + .argument(ItemStackArgument.of("item")) + .literal("is") + .argument(ItemStackPredicateArgument.of("predicate")) + .handler(Mc113::executeTestItem)); + } + + private void executeReplace(final CommandContext ctx) { + final BlockData block = ctx.get("block").createBlockData(); + final BlockPredicate predicate = ctx.get("predicate"); + final int radius = ctx.get("radius"); + + final Player player = (Player) ctx.getSender(); + final Location loc = player.getLocation(); + + this.manager.taskRecipe().begin(ctx).synchronous(context -> { + for (double x = loc.getX() - radius; x < loc.getX() + radius; x++) { + for (double y = loc.getY() - radius; y < loc.getY() + radius; y++) { + for (double z = loc.getZ() - radius; z < loc.getZ() + radius; z++) { + final Block blockAt = player.getWorld().getBlockAt((int) x, (int) y, (int) z); + if (predicate.test(blockAt)) { + blockAt.setBlockData(block); + } + } + } + } + }).execute(); + } + + private static void executeTestItem(final CommandContext ctx) { + final ProtoItemStack protoItemStack = ctx.get("item"); + final ItemStackPredicate predicate = ctx.get("predicate"); + ctx.getSender().sendMessage("result: " + predicate.test( + protoItemStack.createItemStack(1, true))); + } + +}