bukkit: Update for Minecraft 1.17

This commit is contained in:
Jason Penilla 2021-06-11 03:00:39 -07:00 committed by Jason
parent f7e756e901
commit 123341563d
10 changed files with 253 additions and 50 deletions

View file

@ -13,7 +13,7 @@ object Versions {
// MINECRAFT DEPENDENCIES
const val brigadier = "1.0.17"
const val bukkit = "1.13.2-R0.1-SNAPSHOT"
const val commodore = "1.9"
const val commodore = "1.10-SNAPSHOT"
const val bungeecord = "1.8-SNAPSHOT"
const val cloudburst = "1.0.0-SNAPSHOT"
const val adventureApi = "4.7.0"

View file

@ -27,6 +27,7 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.arguments.standard.UUIDArgument;
import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
import cloud.commandframework.bukkit.parsers.BlockPredicateArgument;
import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
import cloud.commandframework.bukkit.parsers.ItemStackArgument;
@ -41,6 +42,7 @@ import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import io.leangen.geantyref.GenericTypeReflector;
import io.leangen.geantyref.TypeToken;
import org.bukkit.NamespacedKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Constructor;
@ -59,7 +61,6 @@ public final class BukkitBrigadierMapper<C> {
private final BukkitCommandManager<C> commandManager;
private final CloudBrigadierManager<C, ?> brigadierManager;
/**
* Class that handles mapping argument types to Brigadier for Bukkit (Commodore) and Paper.
*
@ -87,19 +88,19 @@ public final class BukkitBrigadierMapper<C> {
if (CraftBukkitReflection.MAJOR_REVISION >= UUID_ARGUMENT_VERSION) {
/* Map UUID */
this.mapSimpleNMS(new TypeToken<UUIDArgument.UUIDParser<C>>() {
}, "UUID");
}, "uuid");
}
/* Map Enchantment */
this.mapSimpleNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() {
}, "Enchantment");
}, "item_enchantment");
/* Map Item arguments */
this.mapSimpleNMS(new TypeToken<ItemStackArgument.Parser<C>>() {
}, "ItemStack");
}, "item_stack");
this.mapSimpleNMS(new TypeToken<ItemStackPredicateArgument.Parser<C>>() {
}, "ItemPredicate");
}, "item_predicate");
/* Map Block arguments */
this.mapSimpleNMS(new TypeToken<BlockPredicateArgument.Parser<C>>() {
}, "BlockPredicate");
}, "block_predicate");
/* Map Entity Selectors */
this.mapNMS(new TypeToken<SingleEntitySelectorArgument.SingleEntitySelectorParser<C>>() {
}, this.entitySelectorArgumentSupplier(true, false));
@ -128,7 +129,8 @@ public final class BukkitBrigadierMapper<C> {
) {
return () -> {
try {
final Constructor<?> constructor = getNMSArgument("Entity").getDeclaredConstructors()[0];
final Constructor<?> constructor =
MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft("entity")).getDeclaredConstructors()[0];
constructor.setAccessible(true);
return (ArgumentType<?>) constructor.newInstance(single, playersOnly);
} catch (final Exception e) {
@ -140,7 +142,8 @@ public final class BukkitBrigadierMapper<C> {
private @NonNull ArgumentType<?> argumentVec3() {
try {
return (ArgumentType<?>) getNMSArgument("Vec3").getDeclaredConstructor(boolean.class)
return MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft("vec3"))
.getDeclaredConstructor(boolean.class)
.newInstance(true);
} catch (final Exception e) {
this.commandManager.getOwningPlugin().getLogger().log(Level.INFO, "Failed to retrieve Vec3D argument", e);
@ -150,45 +153,34 @@ public final class BukkitBrigadierMapper<C> {
private @NonNull ArgumentType<?> argumentVec2() {
try {
return (ArgumentType<?>) getNMSArgument("Vec2").getDeclaredConstructor().newInstance();
return MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft("vec2")).getDeclaredConstructor().newInstance();
} catch (final Exception e) {
this.commandManager.getOwningPlugin().getLogger().log(Level.INFO, "Failed to retrieve Vec2 argument", e);
return fallbackType();
}
}
/**
* Attempt to retrieve an NMS argument type
*
* @param argument Argument type name
* @return Argument class
* @throws RuntimeException when the class is not found
*/
private static @NonNull Class<?> getNMSArgument(final @NonNull String argument) throws RuntimeException {
return CraftBukkitReflection.needNMSClass("Argument" + argument);
}
/**
* 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 <T> argument parser type
* @param argumentName NMS Argument class name (without 'Argument' prefix)
* @param type Type to map
* @param <T> argument parser type
* @param argumentId network id of argument type
* @since 1.5.0
*/
public <T extends ArgumentParser<C, ?>> void mapSimpleNMS(
final @NonNull TypeToken<T> type,
final @NonNull String argumentName
final @NonNull String argumentId
) {
final Constructor<?> constructor;
try {
final Class<?> nmsArgument = getNMSArgument(argumentName);
final Class<?> nmsArgument = MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft(argumentId));
constructor = nmsArgument.getConstructor();
} catch (final RuntimeException | ReflectiveOperationException e) {
this.commandManager.getOwningPlugin().getLogger().log(
Level.WARNING,
String.format("Failed to create mapping for NMS brigadier argument type '%s'.", argumentName),
String.format("Failed to create mapping for NMS brigadier argument type '%s'.", argumentId),
e
);
return;

View file

@ -31,6 +31,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Utilities for doing reflection on CraftBukkit, used by the cloud implementation.
@ -41,6 +42,7 @@ import java.lang.reflect.Method;
public final class CraftBukkitReflection {
private static final String PREFIX_NMS = "net.minecraft.server";
private static final String PREFIX_MC = "net.minecraft.";
private static final String PREFIX_CRAFTBUKKIT = "org.bukkit.craftbukkit";
private static final String CRAFT_SERVER = "CraftServer";
private static final String VERSION;
@ -66,6 +68,31 @@ public final class CraftBukkitReflection {
return MAJOR_REVISION != -1 && VERSION != null;
}
public static @NonNull Class<?> needNMSClassOrElse(
final @NonNull String nms,
final @NonNull String... classNames
) throws RuntimeException {
final Class<?> nmsClass = findNMSClass(nms);
if (nmsClass != null) {
return nmsClass;
}
for (final String name : classNames) {
final Class<?> maybe = findClass(name);
if (maybe != null) {
return maybe;
}
}
throw new IllegalStateException(String.format(
"Couldn't find a class! NMS: '%s' or '%s'.",
nms,
Arrays.toString(classNames)
));
}
public static @NonNull Class<?> needMCClass(final @NonNull String name) throws RuntimeException {
return needClass(PREFIX_MC + name);
}
public static @NonNull Class<?> needNMSClass(final @NonNull String className) throws RuntimeException {
return needClass(PREFIX_NMS + VERSION + className);
}
@ -74,6 +101,18 @@ public final class CraftBukkitReflection {
return needClass(PREFIX_CRAFTBUKKIT + VERSION + className);
}
public static @Nullable Class<?> findMCClass(final @NonNull String name) throws RuntimeException {
return findClass(PREFIX_MC + name);
}
public static @Nullable Class<?> findNMSClass(final @NonNull String className) throws RuntimeException {
return findClass(PREFIX_NMS + VERSION + className);
}
public static @Nullable Class<?> findOBCClass(final @NonNull String className) throws RuntimeException {
return findClass(PREFIX_CRAFTBUKKIT + VERSION + className);
}
public static @NonNull Class<?> needClass(final @NonNull String className) throws RuntimeException {
try {
return Class.forName(className);

View file

@ -0,0 +1,127 @@
//
// 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.
//
/*
* This file is part of commodore, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) 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.mojang.brigadier.arguments.ArgumentType;
import org.bukkit.NamespacedKey;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 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).
*/
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 ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD;
static {
try {
final Class<?> minecraftKey;
final Class<?> argumentRegistry;
if (CraftBukkitReflection.MAJOR_REVISION > 16) {
minecraftKey = CraftBukkitReflection.needMCClass("resources.MinecraftKey");
argumentRegistry = CraftBukkitReflection.needMCClass("commands.synchronization.ArgumentRegistry");
} else {
minecraftKey = CraftBukkitReflection.needNMSClass("MinecraftKey");
argumentRegistry = CraftBukkitReflection.needNMSClass("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);
Class<?> argumentRegistryEntry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.getReturnType();
ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD = Arrays.stream(argumentRegistryEntry.getDeclaredFields())
.filter(field -> field.getType() == Class.class)
.findFirst().orElseThrow(NoSuchFieldException::new);
ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD.setAccessible(true);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* 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
*/
@SuppressWarnings("unchecked")
public static Class<? extends ArgumentType<?>> getClassByKey(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 Class<?> argument = (Class<?>) ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD.get(entry);
return (Class<? extends ArgumentType<?>>) argument;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -143,24 +143,45 @@ public final class BlockPredicateArgument<C> extends CommandArgument<C, BlockPre
static {
if (CraftBukkitReflection.MAJOR_REVISION >= 16) {
TAG_REGISTRY_CLASS = CraftBukkitReflection.needNMSClass("ITagRegistry");
TAG_REGISTRY_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ITagRegistry",
"net.minecraft.tags.ITagRegistry"
);
} else {
TAG_REGISTRY_CLASS = CraftBukkitReflection.needNMSClass("TagRegistry");
}
}
private static final Class<?> CRAFT_WORLD_CLASS = CraftBukkitReflection.needOBCClass("CraftWorld");
private static final Class<?> MINECRAFT_SERVER_CLASS = CraftBukkitReflection.needNMSClass("MinecraftServer");
private static final Class<?> COMMAND_LISTENER_WRAPPER_CLASS =
CraftBukkitReflection.needNMSClass("CommandListenerWrapper");
private static final Class<?> ARGUMENT_BLOCK_PREDICATE_CLASS =
CraftBukkitReflection.needNMSClass("ArgumentBlockPredicate");
private static final Class<?> ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS =
CraftBukkitReflection.needNMSClass("ArgumentBlockPredicate$b");
private static final Class<?> SHAPE_DETECTOR_BLOCK_CLASS = // BlockInWorld
CraftBukkitReflection.needNMSClass("ShapeDetectorBlock");
private static final Class<?> I_WORLD_READER_CLASS = CraftBukkitReflection.needNMSClass("IWorldReader");
private static final Class<?> BLOCK_POSITION_CLASS = CraftBukkitReflection.needNMSClass("BlockPosition");
private static final Class<?> MINECRAFT_SERVER_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"MinecraftServer",
"net.minecraft.server.MinecraftServer"
);
private static final Class<?> COMMAND_LISTENER_WRAPPER_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"CommandListenerWrapper",
"net.minecraft.commands.CommandListenerWrapper"
);
private static final Class<?> ARGUMENT_BLOCK_PREDICATE_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ArgumentBlockPredicate",
"net.minecraft.commands.arguments.blocks.ArgumentBlockPredicate"
);
private static final Class<?> ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ArgumentBlockPredicate$b",
"net.minecraft.commands.arguments.blocks.ArgumentBlockPredicate$b"
);
// BlockInWorld
private static final Class<?> SHAPE_DETECTOR_BLOCK_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ShapeDetectorBlock",
"net.minecraft.world.level.block.state.pattern.ShapeDetectorBlock"
);
private static final Class<?> I_WORLD_READER_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"IWorldReader",
"net.minecraft.world.level.IWorldReader"
);
private static final Class<?> BLOCK_POSITION_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"BlockPosition",
"net.minecraft.core.BlockPosition"
);
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

View file

@ -180,14 +180,24 @@ public final class ItemStackArgument<C> extends CommandArgument<C, ProtoItemStac
private static final class ModernParser<C> implements ArgumentParser<C, ProtoItemStack> {
private static final Class<?> NMS_ITEM_STACK_CLASS = CraftBukkitReflection.needNMSClass("ItemStack");
private static final Class<?> NMS_ITEM_STACK_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ItemStack",
"net.minecraft.world.item.ItemStack"
);
private static final Class<?> CRAFT_ITEM_STACK_CLASS =
CraftBukkitReflection.needOBCClass("inventory.CraftItemStack");
private static final Class<?> ARGUMENT_ITEM_STACK_CLASS =
CraftBukkitReflection.needNMSClass("ArgumentItemStack");
private static final Class<?> ARGUMENT_PREDICATE_ITEM_STACK_CLASS =
CraftBukkitReflection.needNMSClass("ArgumentPredicateItemStack");
private static final Class<?> NMS_ITEM_CLASS = CraftBukkitReflection.needNMSClass("Item");
private static final Class<?> ARGUMENT_ITEM_STACK_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ArgumentItemStack",
"net.minecraft.commands.arguments.item.ArgumentItemStack"
);
private static final Class<?> ARGUMENT_PREDICATE_ITEM_STACK_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ArgumentPredicateItemStack",
"net.minecraft.commands.arguments.item.ArgumentPredicateItemStack"
);
private static final Class<?> NMS_ITEM_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"Item",
"net.minecraft.world.item.Item"
);
private static final Class<?> CRAFT_MAGIC_NUMBERS_CLASS =
CraftBukkitReflection.needOBCClass("util.CraftMagicNumbers");
private static final Method GET_MATERIAL_METHOD = CraftBukkitReflection

View file

@ -142,10 +142,14 @@ public final class ItemStackPredicateArgument<C> extends CommandArgument<C, Item
private static final Class<?> CRAFT_ITEM_STACK_CLASS =
CraftBukkitReflection.needOBCClass("inventory.CraftItemStack");
private static final Class<?> ARGUMENT_ITEM_PREDICATE_CLASS =
CraftBukkitReflection.needNMSClass("ArgumentItemPredicate");
private static final Class<?> ARGUMENT_ITEM_PREDICATE_RESULT_CLASS =
CraftBukkitReflection.needNMSClass("ArgumentItemPredicate$b");
private static final Class<?> ARGUMENT_ITEM_PREDICATE_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ArgumentItemPredicate",
"net.minecraft.commands.arguments.item.ArgumentItemPredicate"
);
private static final Class<?> ARGUMENT_ITEM_PREDICATE_RESULT_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ArgumentItemPredicate$b",
"net.minecraft.commands.arguments.item.ArgumentItemPredicate$b"
);
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.needMethod(
ARGUMENT_ITEM_PREDICATE_RESULT_CLASS,
"create",

View file

@ -1,5 +1,6 @@
plugins {
id("com.github.johnrengelman.shadow")
id("xyz.jpenilla.run-paper") version "1.0.3-SNAPSHOT"
}
tasks {
@ -11,6 +12,11 @@ tasks {
build {
dependsOn(shadowJar)
}
runServer {
minecraftVersion("1.17")
paperclip(file("spigot-1.17.jar"))
legacyPluginLoading()
}
}
dependencies {

View file

@ -476,7 +476,10 @@ public final class ExamplePlugin extends JavaPlugin {
},
ArgumentDescription.of("The ItemStack to give")
)
.handler(ctx -> ((Player) ctx.getSender()).getInventory().addItem(ctx.get("itemstack"))));
.handler(ctx -> {
final ItemStack stack = ctx.get("itemstack");
((Player) ctx.getSender()).getInventory().addItem(stack);
}));
}
@CommandMethod("example help [query]")

View file

@ -2,6 +2,7 @@ pluginManagement {
repositories {
gradlePluginPortal()
maven("https://maven.fabricmc.net")
maven("https://repo.jpenilla.xyz/snapshots") // todo - for run-paper snapshot
}
}