Skip to content

Commit 4c64425

Browse files
committed
Update CraftBukkit reflection for 1.18
1 parent a832a00 commit 4c64425

6 files changed

Lines changed: 162 additions & 59 deletions

File tree

cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/internal/CraftBukkitReflection.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import java.lang.reflect.Field;
3333
import java.lang.reflect.Method;
3434
import java.util.Arrays;
35+
import java.util.function.Function;
3536
import java.util.function.Supplier;
37+
import java.util.stream.Stream;
3638

3739
/**
3840
* Utilities for doing reflection on CraftBukkit, used by the cloud implementation.
@@ -65,16 +67,28 @@ public final class CraftBukkitReflection {
6567
}
6668

6769
@SafeVarargs
68-
public static <T> @NonNull T firstNonNullOrThrow(
69-
final @NonNull Supplier<@NonNull String> errorMessage,
70+
public static <T> @Nullable T firstNonNullOrNull(
7071
final @Nullable T @NonNull... elements
7172
) {
7273
for (final T element : elements) {
7374
if (element != null) {
7475
return element;
7576
}
7677
}
77-
throw new IllegalArgumentException(errorMessage.get());
78+
return null;
79+
}
80+
81+
@SafeVarargs
82+
@SuppressWarnings("varargs")
83+
public static <T> @NonNull T firstNonNullOrThrow(
84+
final @NonNull Supplier<@NonNull String> errorMessage,
85+
final @Nullable T @NonNull... elements
86+
) {
87+
final @Nullable T t = firstNonNullOrNull(elements);
88+
if (t == null) {
89+
throw new IllegalArgumentException(errorMessage.get());
90+
}
91+
return t;
7892
}
7993

8094
public static @NonNull Class<?> needNMSClassOrElse(
@@ -191,6 +205,13 @@ public static boolean classExists(final @NonNull String className) {
191205
}
192206
}
193207

208+
public static <T> T streamMethods(
209+
final @NonNull Class<?> clazz,
210+
final @NonNull Function<Stream<Method>, T> function
211+
) {
212+
return function.apply(Arrays.stream(clazz.getDeclaredMethods()));
213+
}
214+
194215
private CraftBukkitReflection() {
195216
}
196217

cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/BlockPredicateArgument.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,32 @@ public static final class Parser<C> implements ArgumentParser<C, BlockPredicate>
193193
CraftBukkitReflection.findMCClass("core.BlockPosition"),
194194
CraftBukkitReflection.findMCClass("core.BlockPos")
195195
);
196+
private static final @Nullable Class<?> TAG_CONTAINER_CLASS = CraftBukkitReflection.firstNonNullOrNull(
197+
CraftBukkitReflection.findClass("net.minecraft.tags.TagContainer"),
198+
CraftBukkitReflection.findClass("net.minecraft.tags.ITagRegistry")
199+
);
196200
private static final Constructor<?> BLOCK_POSITION_CTR =
197201
CraftBukkitReflection.needConstructor(BLOCK_POSITION_CLASS, int.class, int.class, int.class);
198202
private static final Constructor<?> SHAPE_DETECTOR_BLOCK_CTR = CraftBukkitReflection
199203
.needConstructor(SHAPE_DETECTOR_BLOCK_CLASS, LEVEL_READER_CLASS, BLOCK_POSITION_CLASS, boolean.class);
200204
private static final Method GET_HANDLE_METHOD = CraftBukkitReflection.needMethod(CRAFT_WORLD_CLASS, "getHandle");
201-
private static final Method CREATE_PREDICATE_METHOD =
202-
CraftBukkitReflection.needMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_REGISTRY_CLASS);
203-
private static final Method GET_SERVER_METHOD =
204-
CraftBukkitReflection.needMethod(COMMAND_LISTENER_WRAPPER_CLASS, "getServer");
205+
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrThrow(
206+
() -> "create on BlockPredicateArgument$Result",
207+
CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_REGISTRY_CLASS),
208+
CraftBukkitReflection.findMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "a", TAG_REGISTRY_CLASS)
209+
);
210+
private static final Method GET_SERVER_METHOD = CraftBukkitReflection.streamMethods(
211+
COMMAND_LISTENER_WRAPPER_CLASS,
212+
stream -> stream.filter(it -> it.getReturnType().equals(MINECRAFT_SERVER_CLASS) && it.getParameterCount() == 0)
213+
.findFirst().orElseThrow(() -> new IllegalStateException("Could not find CommandSourceStack#getServer."))
214+
);
205215
private static final Method GET_TAG_REGISTRY_METHOD = CraftBukkitReflection.firstNonNullOrThrow(
206216
() -> "getTags method on MinecraftServer",
207217
CraftBukkitReflection.findMethod(MINECRAFT_SERVER_CLASS, "getTagRegistry"),
208-
CraftBukkitReflection.findMethod(MINECRAFT_SERVER_CLASS, "getTags")
218+
CraftBukkitReflection.findMethod(MINECRAFT_SERVER_CLASS, "getTags"),
219+
TAG_CONTAINER_CLASS == null ? null : CraftBukkitReflection.streamMethods(MINECRAFT_SERVER_CLASS, stream ->
220+
stream.filter(it -> it.getReturnType().equals(TAG_CONTAINER_CLASS) && it.getParameterCount() == 0)
221+
.findFirst().orElse(null))
209222
);
210223

211224
private final ArgumentParser<C, BlockPredicate> parser;

cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/ItemStackPredicateArgument.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,18 @@ public static final class Parser<C> implements ArgumentParser<C, ItemStackPredic
152152
CraftBukkitReflection.findMCClass("commands.arguments.item.ArgumentItemPredicate$b"),
153153
CraftBukkitReflection.findMCClass("commands.arguments.item.ItemPredicateArgument$Result")
154154
);
155-
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.needMethod(
156-
ARGUMENT_ITEM_PREDICATE_RESULT_CLASS,
157-
"create",
158-
com.mojang.brigadier.context.CommandContext.class
155+
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.firstNonNullOrThrow(
156+
() -> "ItemPredicateArgument$Result#create",
157+
CraftBukkitReflection.findMethod(
158+
ARGUMENT_ITEM_PREDICATE_RESULT_CLASS,
159+
"create",
160+
com.mojang.brigadier.context.CommandContext.class
161+
),
162+
CraftBukkitReflection.findMethod(
163+
ARGUMENT_ITEM_PREDICATE_RESULT_CLASS,
164+
"a",
165+
com.mojang.brigadier.context.CommandContext.class
166+
)
159167
);
160168
private static final Method AS_NMS_COPY_METHOD =
161169
CraftBukkitReflection.needMethod(CRAFT_ITEM_STACK_CLASS, "asNMSCopy", ItemStack.class);

examples/example-bukkit/build.gradle.kts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,14 @@ tasks {
3333
minecraftVersion("1.17.1")
3434
runDirectory(file("run/latest"))
3535
javaLauncher.set(project.javaToolchains.launcherFor {
36-
languageVersion.set(JavaLanguageVersion.of(16))
36+
languageVersion.set(JavaLanguageVersion.of(17))
3737
})
3838
}
3939

4040
// Setup a run task for each supported version
4141
mapOf(
42-
setOf("1.8.8", "1.9.4", "1.10.2", "1.11.2", "1.12.2") to 8,
43-
setOf("1.13.2", "1.14.4", "1.15.2") to 11,
44-
setOf("1.16.5", "1.17.1") to 16
42+
setOf("1.8.8", "1.9.4", "1.10.2", "1.11.2") to 11,
43+
setOf("1.12.2", "1.13.2", "1.14.4", "1.15.2", "1.16.5", "1.17.1") to 17,
4544
).forEach { (minecraftVersions, javaVersion) ->
4645
for (version in minecraftVersions) {
4746
createVersionedRun(version, javaVersion)
@@ -57,6 +56,7 @@ fun TaskContainerScope.createVersionedRun(
5756
pluginJars.from(shadowJar.flatMap { it.archiveFile })
5857
minecraftVersion(version)
5958
runDirectory(file("run/$version"))
59+
systemProperty("Paper.IgnoreJavaVersion", true)
6060
javaLauncher.set(project.javaToolchains.launcherFor {
6161
languageVersion.set(JavaLanguageVersion.of(javaVersion))
6262
})

examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,8 @@
4444
import cloud.commandframework.bukkit.BukkitCommandManager;
4545
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
4646
import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector;
47-
import cloud.commandframework.bukkit.data.ItemStackPredicate;
4847
import cloud.commandframework.bukkit.data.ProtoItemStack;
4948
import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
50-
import cloud.commandframework.bukkit.parsers.ItemStackArgument;
51-
import cloud.commandframework.bukkit.parsers.ItemStackPredicateArgument;
5249
import cloud.commandframework.bukkit.parsers.MaterialArgument;
5350
import cloud.commandframework.bukkit.parsers.WorldArgument;
5451
import cloud.commandframework.bukkit.parsers.selector.SingleEntitySelectorArgument;
@@ -383,47 +380,9 @@ private void constructCommands() {
383380
)))
384381
);
385382

386-
// MC 1.13+ commands todo: move to separate class
383+
// Commands using MC 1.13+ argument types
387384
if (this.manager.queryCapability(CloudBukkitCapabilities.BRIGADIER)) {
388-
/*
389-
this.manager.command(builder.literal("replace")
390-
.senderType(Player.class)
391-
.argument(BlockPredicateArgument.of("predicate"))
392-
.literal("with")
393-
.argument(MaterialArgument.of("block")) // todo: use BlockDataArgument
394-
.argument(IntegerArgument.<CommandSender>newBuilder("radius").withMin(1))
395-
.handler(ctx -> {
396-
final BlockData block = ctx.<Material>get("block").createBlockData();
397-
final BlockPredicate predicate = ctx.get("predicate");
398-
final int radius = ctx.get("radius");
399-
400-
final Player player = (Player) ctx.getSender();
401-
final Location loc = player.getLocation();
402-
403-
this.manager.taskRecipe().begin(ctx).synchronous(context -> {
404-
for (double x = loc.getX() - radius; x < loc.getX() + radius; x++) {
405-
for (double y = loc.getY() - radius; y < loc.getY() + radius; y++) {
406-
for (double z = loc.getZ() - radius; z < loc.getZ() + radius; z++) {
407-
final Block blockAt = player.getWorld().getBlockAt((int) x, (int) y, (int) z);
408-
if (predicate.test(blockAt)) {
409-
blockAt.setBlockData(block);
410-
}
411-
}
412-
}
413-
}
414-
}).execute();
415-
}));
416-
*/
417-
this.manager.command(builder.literal("test_item")
418-
.argument(ItemStackArgument.of("item"))
419-
.literal("is")
420-
.argument(ItemStackPredicateArgument.of("predicate"))
421-
.handler(ctx -> {
422-
final ProtoItemStack protoItemStack = ctx.get("item");
423-
final ItemStackPredicate predicate = ctx.get("predicate");
424-
ctx.getSender().sendMessage("result: " + predicate.test(
425-
protoItemStack.createItemStack(1, true)));
426-
}));
385+
new Mc113(this.manager).registerCommands();
427386
}
428387

429388
//
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2021 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.examples.bukkit;
25+
26+
import cloud.commandframework.Command;
27+
import cloud.commandframework.arguments.standard.IntegerArgument;
28+
import cloud.commandframework.bukkit.BukkitCommandManager;
29+
import cloud.commandframework.bukkit.data.BlockPredicate;
30+
import cloud.commandframework.bukkit.data.ItemStackPredicate;
31+
import cloud.commandframework.bukkit.data.ProtoItemStack;
32+
import cloud.commandframework.bukkit.parsers.BlockPredicateArgument;
33+
import cloud.commandframework.bukkit.parsers.ItemStackArgument;
34+
import cloud.commandframework.bukkit.parsers.ItemStackPredicateArgument;
35+
import cloud.commandframework.bukkit.parsers.MaterialArgument;
36+
import cloud.commandframework.context.CommandContext;
37+
import org.bukkit.Location;
38+
import org.bukkit.Material;
39+
import org.bukkit.block.Block;
40+
import org.bukkit.block.data.BlockData;
41+
import org.bukkit.command.CommandSender;
42+
import org.bukkit.entity.Player;
43+
import org.checkerframework.checker.nullness.qual.NonNull;
44+
import org.checkerframework.framework.qual.DefaultQualifier;
45+
46+
@DefaultQualifier(NonNull.class)
47+
final class Mc113 {
48+
private final BukkitCommandManager<CommandSender> manager;
49+
50+
Mc113(final BukkitCommandManager<CommandSender> manager) {
51+
this.manager = manager;
52+
}
53+
54+
void registerCommands() {
55+
final Command.Builder<CommandSender> builder = this.manager.commandBuilder("example")
56+
.literal("mc113");
57+
58+
this.manager.command(builder.literal("replace")
59+
.senderType(Player.class)
60+
.argument(BlockPredicateArgument.of("predicate"))
61+
.literal("with")
62+
.argument(MaterialArgument.of("block")) // todo: use BlockDataArgument
63+
.argument(IntegerArgument.<CommandSender>newBuilder("radius").withMin(1))
64+
.handler(this::executeReplace));
65+
66+
this.manager.command(builder.literal("test_item")
67+
.argument(ItemStackArgument.of("item"))
68+
.literal("is")
69+
.argument(ItemStackPredicateArgument.of("predicate"))
70+
.handler(Mc113::executeTestItem));
71+
}
72+
73+
private void executeReplace(final CommandContext<CommandSender> ctx) {
74+
final BlockData block = ctx.<Material>get("block").createBlockData();
75+
final BlockPredicate predicate = ctx.get("predicate");
76+
final int radius = ctx.get("radius");
77+
78+
final Player player = (Player) ctx.getSender();
79+
final Location loc = player.getLocation();
80+
81+
this.manager.taskRecipe().begin(ctx).synchronous(context -> {
82+
for (double x = loc.getX() - radius; x < loc.getX() + radius; x++) {
83+
for (double y = loc.getY() - radius; y < loc.getY() + radius; y++) {
84+
for (double z = loc.getZ() - radius; z < loc.getZ() + radius; z++) {
85+
final Block blockAt = player.getWorld().getBlockAt((int) x, (int) y, (int) z);
86+
if (predicate.test(blockAt)) {
87+
blockAt.setBlockData(block);
88+
}
89+
}
90+
}
91+
}
92+
}).execute();
93+
}
94+
95+
private static void executeTestItem(final CommandContext<CommandSender> ctx) {
96+
final ProtoItemStack protoItemStack = ctx.get("item");
97+
final ItemStackPredicate predicate = ctx.get("predicate");
98+
ctx.getSender().sendMessage("result: " + predicate.test(
99+
protoItemStack.createItemStack(1, true)));
100+
}
101+
102+
}

0 commit comments

Comments
 (0)