mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-28 07:20:28 +01:00
Implement rich commands for Cloud on standalone
This commit is contained in:
parent
edd62eb39e
commit
df8bd9b583
2 changed files with 116 additions and 0 deletions
|
@ -27,6 +27,13 @@ package org.geysermc.geyser.command;
|
|||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandOverloadData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandParam;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
|
@ -51,14 +58,26 @@ import org.geysermc.geyser.command.defaults.StopCommand;
|
|||
import org.geysermc.geyser.command.defaults.VersionCommand;
|
||||
import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl;
|
||||
import org.geysermc.geyser.extension.command.GeyserExtensionCommand;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.incendo.cloud.Command.Builder;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.internal.CommandNode;
|
||||
import org.incendo.cloud.parser.standard.EnumParser;
|
||||
import org.incendo.cloud.parser.standard.IntegerParser;
|
||||
import org.incendo.cloud.parser.standard.LiteralParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.geysermc.geyser.command.GeyserCommand.DEFAULT_ROOT_COMMAND;
|
||||
|
||||
|
@ -299,4 +318,96 @@ public class CommandRegistry implements EventRegistrar {
|
|||
public void runCommand(@NonNull GeyserCommandSource source, @NonNull String command) {
|
||||
cloud.commandExecutor().executeCommand(source, command);
|
||||
}
|
||||
|
||||
public void export(GeyserSession session, List<CommandData> bedrockCommands) {
|
||||
cloud.commandTree().rootNode().children().forEach(commandTree -> {
|
||||
var command = commandTree.command();
|
||||
// Command null happens if you register an extension command with custom Cloud parameters...
|
||||
if (command == null || session.hasPermission(command.commandPermission().permissionString())) {
|
||||
var rootComponent = commandTree.component();
|
||||
String name = rootComponent.name();
|
||||
|
||||
LinkedHashMap<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
|
||||
for (String s : rootComponent.aliases()) {
|
||||
values.put(s, EnumSet.of(CommandEnumConstraint.ALLOW_ALIASES));
|
||||
}
|
||||
CommandEnumData aliases = new CommandEnumData(name + "Aliases", values, false);
|
||||
|
||||
List<CommandOverloadData> data = new ArrayList<>();
|
||||
for (var node : commandTree.children()) {
|
||||
List<List<CommandParamData>> params = new ArrayList<>();
|
||||
createParamData(node, params);
|
||||
params.forEach(param -> data.add(new CommandOverloadData(false, param.toArray(CommandParamData[]::new))));
|
||||
}
|
||||
|
||||
CommandData bedrockCommand = new CommandData(name, rootComponent.description().textDescription(),
|
||||
Set.of(CommandData.Flag.NOT_CHEAT), CommandPermission.ANY, aliases,
|
||||
Collections.emptyList(), data.toArray(new CommandOverloadData[0]));
|
||||
bedrockCommands.add(bedrockCommand);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createParamData(CommandNode<GeyserCommandSource> node, List<List<CommandParamData>> bedrockData) {
|
||||
CommandParamData data = new CommandParamData();
|
||||
var component = node.component();
|
||||
data.setName(component.name());
|
||||
data.setOptional(component.optional());
|
||||
var suggestionProvider = component.suggestionProvider();
|
||||
if (suggestionProvider instanceof LiteralParser<GeyserCommandSource> parser) {
|
||||
Map<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
|
||||
for (String alias : parser.aliases()) {
|
||||
values.put(alias, Set.of());
|
||||
}
|
||||
|
||||
data.setEnumData(new CommandEnumData(component.name(), values, false));
|
||||
} else if (suggestionProvider instanceof IntegerParser<GeyserCommandSource>) {
|
||||
data.setType(CommandParam.INT);
|
||||
} else if (suggestionProvider instanceof EnumParser<?,?> parser) {
|
||||
LinkedHashMap<String, Set<CommandEnumConstraint>> map = new LinkedHashMap<>();
|
||||
for (Enum<?> e : parser.acceptedValues()) {
|
||||
map.put(e.name().toLowerCase(Locale.ROOT), Set.of());
|
||||
}
|
||||
|
||||
data.setEnumData(new CommandEnumData(component.name().toLowerCase(Locale.ROOT), map, false));
|
||||
} else {
|
||||
data.setType(CommandParam.STRING);
|
||||
}
|
||||
|
||||
// This, realistically, is not going to be used without extensions using internals and implementing complicated commands.
|
||||
// It essentially does the same behavior as JavaCommandsTranslator#isCompatible.
|
||||
// But, selfishly, I would like to use it, and in the future it's possible extensions can register commands
|
||||
// using Cloud, and in that case this becomes relevant!
|
||||
if (bedrockData.isEmpty()) {
|
||||
List<CommandParamData> list = new ArrayList<>();
|
||||
list.add(data);
|
||||
bedrockData.add(list);
|
||||
} else {
|
||||
int size = bedrockData.size(); // Preserve original list size in case new entries get added.
|
||||
for (int i = 0; i < size; i++) {
|
||||
List<CommandParamData> cpdList = bedrockData.get(i);
|
||||
if (cpdList.size() <= 1) { // No commands or parent will be root.
|
||||
cpdList.add(data);
|
||||
} else {
|
||||
String parentName = node.parent().component().name(); // Should never be null.
|
||||
if (!cpdList.get(cpdList.size() - 1).getName().equals(parentName)) { // We need to copy the list as this is a new branch.
|
||||
for (int j = cpdList.size() - 2; j >= 0; j--) {
|
||||
if (cpdList.get(j).getName().equals(parentName)) {
|
||||
List<CommandParamData> newList = new ArrayList<>(cpdList.subList(0, j + 1));
|
||||
newList.add(data);
|
||||
bedrockData.add(newList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cpdList.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var child : node.children()) {
|
||||
createParamData(child, bedrockData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.cloudburstmc.protocol.bedrock.data.command.*;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.AvailableCommandsPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.java.ServerDefineCommandsEvent;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.item.enchantment.Enchantment;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
|
@ -213,6 +214,10 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
|||
commandData.add(createFakeHelpCommand());
|
||||
}
|
||||
|
||||
if (session.getGeyser().platformType() == PlatformType.STANDALONE) {
|
||||
session.getGeyser().commandRegistry().export(session, commandData);
|
||||
}
|
||||
|
||||
// Add our commands to the AvailableCommandsPacket for the bedrock client
|
||||
AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket();
|
||||
availableCommandsPacket.getCommands().addAll(commandData);
|
||||
|
|
Loading…
Reference in a new issue