Skip to content

Commit

Permalink
fix compat with 1.20.6+
Browse files Browse the repository at this point in the history
  • Loading branch information
Revxrsal committed Nov 29, 2024
1 parent 95dbf85 commit ec58ec9
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* This file is part of lamp, licensed under the MIT License.
*
* Copyright (c) Revxrsal <[email protected]>
*
* 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 revxrsal.commands.bukkit.brigadier;

import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
* A utility to modify {@link CommandNode}s reflectively.
*/
final class BrigadierUtil {

// CommandNode#children, CommandNode#literals, CommandNode#arguments fields
private static final Field CHILDREN_FIELD;
private static final Field LITERALS_FIELD;
private static final Field ARGUMENTS_FIELD;

// nms.CommandListenerWrapper#getBukkitSender method
private static final Method GET_BUKKIT_SENDER_METHOD;

// An array of the CommandNode fields above: [#children, #literals, #arguments]
private static final Field[] CHILDREN_FIELDS;

static {
try {
Class<?> commandListenerWrapper;
try {
if (BukkitVersion.supports(1, 16))
commandListenerWrapper = BukkitVersion.findNmsClass("commands.CommandListenerWrapper");
else
commandListenerWrapper = BukkitVersion.findNmsClass("CommandListenerWrapper");
} catch (Exception e) {
commandListenerWrapper = Class.forName("net.minecraft.commands.CommandListenerWrapper");
}

CHILDREN_FIELD = CommandNode.class.getDeclaredField("children");
LITERALS_FIELD = CommandNode.class.getDeclaredField("literals");
ARGUMENTS_FIELD = CommandNode.class.getDeclaredField("arguments");

CHILDREN_FIELDS = new Field[]{CHILDREN_FIELD, LITERALS_FIELD, ARGUMENTS_FIELD};
for (Field field : CHILDREN_FIELDS) {
field.setAccessible(true);
}

GET_BUKKIT_SENDER_METHOD = commandListenerWrapper.getDeclaredMethod("getBukkitSender");
GET_BUKKIT_SENDER_METHOD.setAccessible(true);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}

@SuppressWarnings({"rawtypes", "unchecked"})
public static void removeChild(RootCommandNode root, String name) {
try {
for (Field field : CHILDREN_FIELDS) {
Map<String, ?> children = (Map<String, ?>) field.get(root);
children.remove(name);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

public static <S> LiteralCommandNode<S> renameLiteralNode(LiteralCommandNode<S> node, String newLiteral) {
LiteralCommandNode<S> clone = new LiteralCommandNode<>(newLiteral, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
for (CommandNode<S> child : node.getChildren()) {
clone.addChild(child);
}
return clone;
}

/**
* Returns the underlying {@link CommandSender} of the given dispatcher
*
* @param commandSource The brigadier's command source
* @return The {@link CommandSender}
*/
public static @NotNull CommandSender getBukkitSender(Object commandSource) {
Objects.requireNonNull(commandSource, "commandSource");
try {
return (CommandSender) GET_BUKKIT_SENDER_METHOD.invoke(commandSource);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,114 +28,17 @@
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

abstract class Commodore {

// ArgumentCommandNode#customSuggestions field
protected static final Field CUSTOM_SUGGESTIONS_FIELD;

// CommandNode#command
protected static final Field COMMAND_EXECUTE_FUNCTION_FIELD;

// CommandNode#children, CommandNode#literals, CommandNode#arguments fields
protected static final Field CHILDREN_FIELD;
protected static final Field LITERALS_FIELD;
protected static final Field ARGUMENTS_FIELD;

// nms.CommandListenerWrapper#getBukkitSender method
private static final Method GET_BUKKIT_SENDER_METHOD;

// An array of the CommandNode fields above: [#children, #literals, #arguments]
protected static final Field[] CHILDREN_FIELDS;

// Dummy instance of Command used to ensure the executable bit gets set on
// mock commands when they're encoded into data sent to the client
protected static final com.mojang.brigadier.Command<?> DUMMY_COMMAND;
protected static final SuggestionProvider<?> DUMMY_SUGGESTION_PROVIDER;

static {
try {
final Class<?> commandListenerWrapper;
if (ReflectionUtil.minecraftVersion() > 16)
commandListenerWrapper = ReflectionUtil.mcClass("commands.CommandListenerWrapper");
else
commandListenerWrapper = ReflectionUtil.nmsClass("CommandListenerWrapper");

CUSTOM_SUGGESTIONS_FIELD = ArgumentCommandNode.class.getDeclaredField("customSuggestions");
CUSTOM_SUGGESTIONS_FIELD.setAccessible(true);

COMMAND_EXECUTE_FUNCTION_FIELD = CommandNode.class.getDeclaredField("command");
COMMAND_EXECUTE_FUNCTION_FIELD.setAccessible(true);

CHILDREN_FIELD = CommandNode.class.getDeclaredField("children");
LITERALS_FIELD = CommandNode.class.getDeclaredField("literals");
ARGUMENTS_FIELD = CommandNode.class.getDeclaredField("arguments");

CHILDREN_FIELDS = new Field[]{CHILDREN_FIELD, LITERALS_FIELD, ARGUMENTS_FIELD};
for (Field field : CHILDREN_FIELDS) {
field.setAccessible(true);
}

GET_BUKKIT_SENDER_METHOD = commandListenerWrapper.getDeclaredMethod("getBukkitSender");
GET_BUKKIT_SENDER_METHOD.setAccessible(true);

// should never be called
// if ReflectionCommodore: bukkit handling should override
// if PaperCommodore: this is only sent to the client, not used for actual command handling
DUMMY_COMMAND = (ctx) -> {throw new UnsupportedOperationException();};
// should never be called - only used in clientbound root node, and the server impl will pass anything through
// SuggestionProviders#safelySwap (swap it for the ASK_SERVER provider) before sending
DUMMY_SUGGESTION_PROVIDER = (context, builder) -> {throw new UnsupportedOperationException();};

} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}

@SuppressWarnings("rawtypes")
protected static void removeChild(RootCommandNode root, String name) {
try {
for (Field field : CHILDREN_FIELDS) {
Map<String, ?> children = (Map<String, ?>) field.get(root);
children.remove(name);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

protected static <S> LiteralCommandNode<S> renameLiteralNode(LiteralCommandNode<S> node, String newLiteral) {
LiteralCommandNode<S> clone = new LiteralCommandNode<>(newLiteral, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
for (CommandNode<S> child : node.getChildren()) {
clone.addChild(child);
}
return clone;
}

public CommandSender getBukkitSender(Object commandSource) {
Objects.requireNonNull(commandSource, "commandSource");
try {
return (CommandSender) GET_BUKKIT_SENDER_METHOD.invoke(commandSource);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

/**
* Gets the aliases known for the given command.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private void checkSupported() {

@Override public @NotNull CommandActor wrapSource(@NotNull Object commandSource) {
checkSupported();
return new BukkitActor(commodore.getBukkitSender(commandSource), handler);
return new BukkitActor(BrigadierUtil.getBukkitSender(commandSource), handler);
}

@Override public void disableNativePlayerCompletion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void register(Command command, LiteralCommandNode<?> node) {

Collection<String> aliases = getAliases(command);
if (!aliases.contains(node.getLiteral())) {
node = renameLiteralNode(node, command.getName());
node = BrigadierUtil.renameLiteralNode(node, command.getName());
}

for (String alias : aliases) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public void register(LiteralCommandNode<?> node) {
CommandDispatcher dispatcher = getDispatcher();
RootCommandNode root = dispatcher.getRoot();

removeChild(root, node.getName());
BrigadierUtil.removeChild(root, node.getName());
root.addChild(node);
registeredNodes.add(node);
}
Expand All @@ -131,7 +131,7 @@ public void register(Command command, LiteralCommandNode<?> node) {

Collection<String> aliases = getAliases(command);
if (!aliases.contains(node.getLiteral())) {
node = renameLiteralNode(node, command.getName());
node = BrigadierUtil.renameLiteralNode(node, command.getName());
}

for (String alias : aliases) {
Expand All @@ -156,7 +156,7 @@ public void onLoad(ServerLoadEvent e) {
RootCommandNode root = dispatcher.getRoot();

for (LiteralCommandNode<?> node : registeredNodes) {
removeChild(root, node.getName());
BrigadierUtil.removeChild(root, node.getName());
root.addChild(node);
}
}
Expand All @@ -169,7 +169,7 @@ public void onPluginDisable(PluginDisableEvent e) {
RootCommandNode root = dispatcher.getRoot();

for (LiteralCommandNode<?> node : registeredNodes) {
removeChild(root, node.getName());
BrigadierUtil.removeChild(root, node.getName());
}
}
}
Expand Down

0 comments on commit ec58ec9

Please sign in to comment.