Skip to content

Commit 4b650fe

Browse files
committed
Fix hidden commands on brigadier
1 parent 6477a08 commit 4b650fe

5 files changed

Lines changed: 335 additions & 20 deletions

File tree

brigadier/src/main/java/me/vaperion/blade/brigadier/BladeBrigadierBuilder.java

Lines changed: 145 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,7 @@ public LiteralCommandNode<T> buildLiteral(
4040
.executes(executor);
4141

4242
LiteralCommandNode<T> root = builder.build();
43-
44-
if (node.isLeaf()) {
45-
registerParams(node, root, suggestionProvider, executor);
46-
} else if (blade.configuration().registerDefaultHelpArguments() && !node.children().isEmpty()) {
47-
registerHelpParams(node, root, suggestionProvider, executor);
48-
}
43+
registerNodeData(node, root, suggestionProvider, executor);
4944

5045
for (CommandTreeNode subcommand : node.children().values()) {
5146
registerSubCommand(root,
@@ -54,6 +49,13 @@ public LiteralCommandNode<T> buildLiteral(
5449
executor);
5550
}
5651

52+
if (hasHiddenCommand(node)) {
53+
BrigadierCompat.setClientNode(
54+
root,
55+
buildClientLiteral(node, label, suggestionProvider, executor)
56+
);
57+
}
58+
5759
return root;
5860
}
5961

@@ -70,15 +72,83 @@ private void registerSubCommand(
7072

7173
LiteralCommandNode<T> subcommandNode = builder.build();
7274
root.addChild(subcommandNode);
75+
registerNodeData(node, subcommandNode, suggestionProvider, executor);
7376

74-
if (node.isLeaf()) {
75-
registerParams(node, subcommandNode, suggestionProvider, executor);
76-
} else if (blade.configuration().registerDefaultHelpArguments() && !node.children().isEmpty()) {
77-
registerHelpParams(node, subcommandNode, suggestionProvider, executor);
77+
for (CommandTreeNode child : node.children().values()) {
78+
registerSubCommand(subcommandNode, child, suggestionProvider, executor);
79+
}
80+
}
81+
82+
@NotNull
83+
private LiteralCommandNode<T> buildClientLiteral(@NotNull CommandTreeNode node,
84+
@NotNull String label,
85+
@NotNull SuggestionProvider<T> suggestionProvider,
86+
@NotNull Command<T> executor) {
87+
boolean visibleLeaf = node.command() != null && !node.command().hidden();
88+
89+
LiteralArgumentBuilder<T> builder = LiteralArgumentBuilder.<T>literal(label)
90+
.requires(createClientVisibilityPredicate(node));
91+
92+
if (visibleLeaf) {
93+
builder.executes(executor);
94+
}
95+
96+
LiteralCommandNode<T> root = builder.build();
97+
98+
if (visibleLeaf) {
99+
registerNodeData(node, root, suggestionProvider, executor);
100+
}
101+
102+
for (CommandTreeNode subcommand : node.children().values()) {
103+
if (!hasVisibleCommand(subcommand)) {
104+
continue;
105+
}
106+
107+
root.addChild(buildClientSubCommand(subcommand, suggestionProvider, executor));
108+
}
109+
110+
return root;
111+
}
112+
113+
@NotNull
114+
private LiteralCommandNode<T> buildClientSubCommand(@NotNull CommandTreeNode node,
115+
@NotNull SuggestionProvider<T> suggestionProvider,
116+
@NotNull Command<T> executor) {
117+
String label = node.label();
118+
boolean visibleLeaf = node.command() != null && !node.command().hidden();
119+
120+
LiteralArgumentBuilder<T> builder = LiteralArgumentBuilder.<T>literal(label)
121+
.requires(createClientVisibilityPredicate(node));
122+
123+
if (visibleLeaf) {
124+
builder.executes(executor);
125+
}
126+
127+
LiteralCommandNode<T> subcommandNode = builder.build();
128+
129+
if (visibleLeaf) {
130+
registerNodeData(node, subcommandNode, suggestionProvider, executor);
78131
}
79132

80133
for (CommandTreeNode child : node.children().values()) {
81-
registerSubCommand(subcommandNode, child, suggestionProvider, executor);
134+
if (!hasVisibleCommand(child)) {
135+
continue;
136+
}
137+
138+
subcommandNode.addChild(buildClientSubCommand(child, suggestionProvider, executor));
139+
}
140+
141+
return subcommandNode;
142+
}
143+
144+
private void registerNodeData(@NotNull CommandTreeNode node,
145+
@NotNull CommandNode<T> commandNode,
146+
@NotNull SuggestionProvider<T> suggestionProvider,
147+
@NotNull Command<T> brigadierCommand) {
148+
if (node.isLeaf()) {
149+
registerParams(node, commandNode, suggestionProvider, brigadierCommand);
150+
} else if (blade.configuration().registerDefaultHelpArguments() && !node.children().isEmpty()) {
151+
registerHelpParams(node, commandNode, suggestionProvider, brigadierCommand);
82152
}
83153
}
84154

@@ -144,8 +214,7 @@ private void registerParams(@NotNull CommandTreeNode node,
144214
@NotNull
145215
private Predicate<T> createPermissionPredicate(@NotNull CommandTreeNode node) {
146216
return sender -> {
147-
Sender<?> wrappedSender = wrapper.apply(converter.apply(sender));
148-
Context context = new Context(blade, wrappedSender, "", new String[0]);
217+
Context context = createContext(sender);
149218

150219
if (node.command() != null) {
151220
return node.command().hasPermission(context);
@@ -159,6 +228,17 @@ private Predicate<T> createPermissionPredicate(@NotNull CommandTreeNode node) {
159228
};
160229
}
161230

231+
@NotNull
232+
private Predicate<T> createClientVisibilityPredicate(@NotNull CommandTreeNode node) {
233+
return sender -> hasVisibleAccessibleCommand(node, createContext(sender));
234+
}
235+
236+
@NotNull
237+
private Context createContext(@NotNull T sender) {
238+
Sender<?> wrappedSender = wrapper.apply(converter.apply(sender));
239+
return new Context(blade, wrappedSender, "", new String[0]);
240+
}
241+
162242
private boolean hasAccessibleCommand(@NotNull CommandTreeNode node,
163243
@NotNull Context context) {
164244
if (node.isLeaf()) {
@@ -177,6 +257,58 @@ private boolean hasAccessibleCommand(@NotNull CommandTreeNode node,
177257
return false;
178258
}
179259

260+
private boolean hasVisibleCommand(@NotNull CommandTreeNode node) {
261+
if (node.isLeaf()) {
262+
BladeCommand cmd = node.command();
263+
if (cmd != null && !cmd.hidden()) {
264+
return true;
265+
}
266+
}
267+
268+
for (CommandTreeNode child : node.children().values()) {
269+
if (hasVisibleCommand(child)) {
270+
return true;
271+
}
272+
}
273+
274+
return false;
275+
}
276+
277+
private boolean hasHiddenCommand(@NotNull CommandTreeNode node) {
278+
if (node.isLeaf()) {
279+
BladeCommand cmd = node.command();
280+
if (cmd != null && cmd.hidden()) {
281+
return true;
282+
}
283+
}
284+
285+
for (CommandTreeNode child : node.children().values()) {
286+
if (hasHiddenCommand(child)) {
287+
return true;
288+
}
289+
}
290+
291+
return false;
292+
}
293+
294+
private boolean hasVisibleAccessibleCommand(@NotNull CommandTreeNode node,
295+
@NotNull Context context) {
296+
if (node.isLeaf()) {
297+
BladeCommand cmd = node.command();
298+
if (cmd != null && !cmd.hidden() && cmd.hasPermission(context)) {
299+
return true;
300+
}
301+
}
302+
303+
for (CommandTreeNode child : node.children().values()) {
304+
if (hasVisibleAccessibleCommand(child, context)) {
305+
return true;
306+
}
307+
}
308+
309+
return false;
310+
}
311+
180312
@NotNull
181313
private ArgumentType<Object> mapBrigadierArgument(@NotNull BladeCommand command,
182314
@NotNull BladeParameter parameter) {
@@ -262,5 +394,4 @@ private ArgumentType<Object> mapBrigadierArgument(@NotNull BladeCommand command,
262394
//noinspection unchecked
263395
return (ArgumentType<Object>) type;
264396
}
265-
266397
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package me.vaperion.blade.brigadier;
2+
3+
import com.mojang.brigadier.tree.CommandNode;
4+
import com.mojang.brigadier.tree.LiteralCommandNode;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
import java.lang.reflect.Field;
9+
10+
@SuppressWarnings("JavaReflectionMemberAccess")
11+
public final class BrigadierCompat {
12+
13+
private static volatile Field CLIENT_NODE_FIELD;
14+
private static volatile Field UNWRAPPED_CACHED_FIELD;
15+
16+
private static boolean CLIENT_NODE_SUPPORTED = true;
17+
private static boolean UNWRAPPED_CACHED_SUPPORTED = true;
18+
19+
private BrigadierCompat() {
20+
}
21+
22+
public static <T> void setClientNode(@NotNull CommandNode<T> node,
23+
@NotNull LiteralCommandNode<T> clientNode) {
24+
// some platforms like Paper have a separate client node field that lets us send a different brigadier tree to the client
25+
26+
if (!CLIENT_NODE_SUPPORTED) {
27+
return;
28+
}
29+
30+
try {
31+
Field field = clientNodeField();
32+
33+
field.set(node, clientNode);
34+
} catch (Throwable ignored) {
35+
CLIENT_NODE_SUPPORTED = false;
36+
}
37+
}
38+
39+
@Nullable
40+
public static <T> CommandNode<T> getClientNode(@NotNull CommandNode<T> node) {
41+
// some platforms like Paper have a separate client node field that lets us send a different brigadier tree to the client
42+
43+
if (!CLIENT_NODE_SUPPORTED) {
44+
return null;
45+
}
46+
47+
try {
48+
Field field = clientNodeField();
49+
50+
//noinspection unchecked
51+
return (CommandNode<T>) field.get(node);
52+
} catch (Throwable ignored) {
53+
CLIENT_NODE_SUPPORTED = false;
54+
return null;
55+
}
56+
}
57+
58+
@Nullable
59+
public static <T> CommandNode<T> getUnwrappedCached(@NotNull CommandNode<T> node) {
60+
if (!UNWRAPPED_CACHED_SUPPORTED) {
61+
return null;
62+
}
63+
64+
try {
65+
Field field = unwrappedCachedField();
66+
67+
//noinspection unchecked
68+
return (CommandNode<T>) field.get(node);
69+
} catch (Throwable ignored) {
70+
UNWRAPPED_CACHED_SUPPORTED = false;
71+
return null;
72+
}
73+
}
74+
75+
@NotNull
76+
private static Field clientNodeField() throws NoSuchFieldException {
77+
if (CLIENT_NODE_FIELD == null) {
78+
CLIENT_NODE_FIELD = CommandNode.class.getField("clientNode");
79+
CLIENT_NODE_FIELD.setAccessible(true);
80+
}
81+
82+
return CLIENT_NODE_FIELD;
83+
}
84+
85+
@NotNull
86+
private static Field unwrappedCachedField() throws NoSuchFieldException {
87+
if (UNWRAPPED_CACHED_FIELD == null) {
88+
UNWRAPPED_CACHED_FIELD = CommandNode.class.getField("unwrappedCached");
89+
UNWRAPPED_CACHED_FIELD.setAccessible(true);
90+
}
91+
92+
return UNWRAPPED_CACHED_FIELD;
93+
}
94+
}

bukkit/src/main/java/me/vaperion/blade/bukkit/container/BukkitContainer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,11 @@ public boolean execute(@NotNull CommandSender sender,
203203
BladeCommand command = Objects.requireNonNull(node.command());
204204

205205
if (!command.hasPermission(context)) {
206-
sender.sendMessage(ChatColor.RED + command.permissionMessage());
206+
if (command.hidden()) {
207+
sender.sendMessage(UNKNOWN_COMMAND_MESSAGE);
208+
} else {
209+
sender.sendMessage(ChatColor.RED + command.permissionMessage());
210+
}
207211
return true;
208212
}
209213

core/src/main/java/me/vaperion/blade/impl/suggestions/CommandSuggestionProvider.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,16 @@ public void suggest(@NotNull Context context,
7878
@NotNull RichSuggestionsBuilder builder) {
7979
input.ensureTokenized();
8080

81-
if (input.bladeCommand() != null && !input.bladeCommand().hasPermission(context)) {
82-
// No suggestions if the user doesn't have permission for the command
83-
return;
81+
if (input.bladeCommand() != null) {
82+
if (input.bladeCommand().hidden()) {
83+
// Hidden commands should never be suggested.
84+
return;
85+
}
86+
87+
if (!input.bladeCommand().hasPermission(context)) {
88+
// No suggestions if the user doesn't have permission for the command
89+
return;
90+
}
8491
}
8592

8693
String label = input.label()

0 commit comments

Comments
 (0)