Make commands fully use cloud, command api deprecations

This commit is contained in:
Konicai 2023-06-27 04:39:00 -04:00
parent a830fb76fb
commit 049e29ed42
No known key found for this signature in database
GPG Key ID: 710D09287708C823
19 changed files with 343 additions and 302 deletions

View File

@ -76,25 +76,18 @@ public interface Command {
*
* @return if this command is designated to be used only by server operators.
*/
boolean isSuggestedOpOnly();
@Deprecated(forRemoval = true)
default boolean isSuggestedOpOnly() {
return false;
}
/**
* Gets if this command is executable on console.
*
* @return if this command is executable on console
*/
boolean isExecutableOnConsole();
/**
* Gets the subcommands associated with this
* command. Mainly used within the Geyser Standalone
* GUI to know what subcommands are supported.
*
* @return the subcommands associated with this command
*/
@NonNull
default List<String> subCommands() {
return Collections.emptyList();
default boolean isExecutableOnConsole() {
return true;
}
/**
@ -106,6 +99,19 @@ public interface Command {
return false;
}
/**
* Gets the subcommands associated with this
* command. Mainly used within the Geyser Standalone
* GUI to know what subcommands are supported.
*
* @return the subcommands associated with this command
*/
@Deprecated(forRemoval = true)
@NonNull
default List<String> subCommands() {
return Collections.emptyList();
}
/**
* Creates a new {@link Command.Builder} used to construct commands.
*
@ -180,14 +186,6 @@ public interface Command {
*/
Builder<T> executableOnConsole(boolean executableOnConsole);
/**
* Sets the subcommands.
*
* @param subCommands the subcommands
* @return the builder
*/
Builder<T> subCommands(@NonNull List<String> subCommands);
/**
* Sets if this command is bedrock only.
*
@ -196,6 +194,17 @@ public interface Command {
*/
Builder<T> bedrockOnly(boolean bedrockOnly);
/**
* Sets the subcommands.
*
* @param subCommands the subcommands
* @return the builder
*/
@Deprecated(forRemoval = true)
default Builder<T> subCommands(@NonNull List<String> subCommands) {
return this;
}
/**
* Sets the {@link CommandExecutor} for this command.
*

View File

@ -25,23 +25,32 @@
package org.geysermc.geyser.command;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.meta.CommandMeta;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.session.GeyserSession;
import org.jetbrains.annotations.Contract;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
@Accessors(fluent = true)
@Getter
@RequiredArgsConstructor
public abstract class GeyserCommand implements Command {
public abstract class GeyserCommand implements org.geysermc.geyser.api.command.Command {
/**
* CommandMeta to indicate that the command is only available to bedrock players. default of false.
*/
public static final CommandMeta.Key<Boolean> BEDROCK_ONLY = CommandMeta.Key.of(Boolean.class, "bedrock-only", meta -> false);
/**
* CommandMeta to indicate that the command is only available to players. default of false.
*/
public static final CommandMeta.Key<Boolean> PLAYER_ONLY = CommandMeta.Key.of(Boolean.class, "player-only", meta -> false);
protected final String name;
/**
@ -50,78 +59,23 @@ public abstract class GeyserCommand implements Command {
protected final String description;
protected final String permission;
@Setter
private List<String> aliases = Collections.emptyList();
public abstract void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args);
/**
* If false, hides the command from being shown on the Geyser Standalone GUI.
*
* @return true if the command can be run on the server console
*/
@Override
public boolean isExecutableOnConsole() {
return true;
}
/**
* Used in the GUI to know what subcommands can be run
*
* @return a list of all possible subcommands, or empty if none.
*/
@NonNull
@Override
public List<String> subCommands() {
return Collections.emptyList();
}
/**
* Shortcut to {@link #subCommands()} ()}{@code .isEmpty()}.
*
* @return true if there are subcommand present for this command.
*/
public boolean hasSubCommands() {
return !this.subCommands().isEmpty();
}
public void setAliases(List<String> aliases) {
this.aliases = aliases;
}
/**
* Used for permission defaults on server implementations.
*
* @return if this command is designated to be used only by server operators.
*/
@Override
public boolean isSuggestedOpOnly() {
return false;
}
public String rootCommand() {
return "geyser";
}
public void register(CommandManager<GeyserCommandSource> manager) {
// todo: commands all builtin commands should directly use cloud
// also, there needs to be customized messages for isExecutableOnConsole and isBedrockOnly, etc. Review the old CommandExecutors.
manager.command(manager.commandBuilder(rootCommand())
@Contract(value = "_ -> new", pure = true)
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return manager.commandBuilder(rootCommand())
.literal(name, aliases.toArray(new String[0]))
.permission(source -> {
if (source.isConsole()) {
return isExecutableOnConsole();
}
if (isBedrockOnly() && source.connection().isEmpty()) {
return false; // bedrock only but not a bedrock player
}
.meta(BEDROCK_ONLY, isBedrockOnly())
.meta(PLAYER_ONLY, !isExecutableOnConsole())
.permission(permission);
}
return source.hasPermission(permission);
})
.argument(StringArgument.optional("args", StringArgument.StringMode.GREEDY))
.handler(context -> {
GeyserCommandSource source = context.getSender();
execute(source.connection().orElse(null), source, context.getOrDefault("args", "").split(" "));
})
);
public void register(CommandManager<GeyserCommandSource> manager) {
manager.command(builder(manager));
}
}

View File

@ -26,10 +26,10 @@
package org.geysermc.geyser.command;
import cloud.commandframework.CommandManager;
import cloud.commandframework.arguments.standard.StringArgument;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
@ -73,6 +73,8 @@ public class GeyserCommandManager {
this.geyser = geyser;
this.cloud = cloud;
cloud.registerCommandPostProcessor(new SenderTypeProcessor());
registerBuiltInCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help", "geyser", this.commands));
registerBuiltInCommand(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list"));
registerBuiltInCommand(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload"));
@ -178,7 +180,6 @@ public class GeyserCommandManager {
private List<String> aliases;
private boolean suggestedOpOnly = false;
private boolean executableOnConsole = true;
private List<String> subCommands;
private boolean bedrockOnly;
private CommandExecutor<T> executor;
@ -219,11 +220,6 @@ public class GeyserCommandManager {
return this;
}
public CommandBuilder<T> subCommands(@NonNull List<String> subCommands) {
this.subCommands = subCommands;
return this;
}
public CommandBuilder<T> bedrockOnly(boolean bedrockOnly) {
this.bedrockOnly = bedrockOnly;
return this;
@ -248,20 +244,29 @@ public class GeyserCommandManager {
@SuppressWarnings("unchecked")
@Override
public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) {
Class<? extends T> sourceType = CommandBuilder.this.sourceType;
CommandExecutor<T> executor = CommandBuilder.this.executor;
if (sourceType.isInstance(session)) {
executor.execute((T) session, this, args);
return;
}
public cloud.commandframework.Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.argument(StringArgument.optional("args", StringArgument.StringMode.GREEDY))
.handler(context -> {
GeyserCommandSource source = context.getSender();
String[] args = context.getOrDefault("args", "").split(" ");
if (sourceType.isInstance(sender)) {
executor.execute((T) sender, this, args);
return;
}
Class<? extends T> sourceType = CommandBuilder.this.sourceType;
CommandExecutor<T> executor = CommandBuilder.this.executor;
GeyserImpl.getInstance().getLogger().debug("Ignoring command " + this.name + " due to no suitable sender.");
if (sourceType.isInstance(source)) {
executor.execute((T) source, this, args);
return;
}
GeyserSession session = source.connection().orElse(null);
if (sourceType.isInstance(session)) {
executor.execute((T) session, this, args);
return;
}
GeyserImpl.getInstance().getLogger().debug("Ignoring command " + this.name + " due to no suitable sender.");
});
}
@NonNull
@ -275,10 +280,9 @@ public class GeyserCommandManager {
return CommandBuilder.this.suggestedOpOnly;
}
@NonNull
@Override
public List<String> subCommands() {
return CommandBuilder.this.subCommands == null ? Collections.emptyList() : CommandBuilder.this.subCommands;
public boolean isExecutableOnConsole() {
return CommandBuilder.this.executableOnConsole;
}
@Override
@ -286,11 +290,6 @@ public class GeyserCommandManager {
return CommandBuilder.this.bedrockOnly;
}
@Override
public boolean isExecutableOnConsole() {
return CommandBuilder.this.executableOnConsole;
}
@Override
public String rootCommand() {
return extension().rootCommand();

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* 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.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.command;
import cloud.commandframework.execution.postprocessor.CommandPostprocessingContext;
import cloud.commandframework.execution.postprocessor.CommandPostprocessor;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.services.types.ConsumerService;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
public class SenderTypeProcessor implements CommandPostprocessor<GeyserCommandSource> {
@Override
public void accept(@NonNull CommandPostprocessingContext<GeyserCommandSource> processContext) {
CommandMeta meta = processContext.getCommand().getCommandMeta();
GeyserCommandSource source = processContext.getCommandContext().getSender();
if (meta.getOrDefault(GeyserCommand.BEDROCK_ONLY, false)) {
if (source.connection().isEmpty()) {
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", source.locale()));
ConsumerService.interrupt();
}
} else if (meta.getOrDefault(GeyserCommand.PLAYER_ONLY, false)) {
// it should be fine to use else-if here, because if the command is bedrock-only,
// performing the bedrock player check is also inherently a player check
if (source.playerUuid().isEmpty()) {
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.player_only", source.locale()));
ConsumerService.interrupt();
}
}
}
}

View File

@ -25,9 +25,10 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.MinecraftLocale;
public class AdvancedTooltipsCommand extends GeyserCommand {
@ -36,13 +37,14 @@ public class AdvancedTooltipsCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (session != null) {
String onOrOff = session.isAdvancedTooltips() ? "off" : "on";
session.setAdvancedTooltips(!session.isAdvancedTooltips());
session.sendMessage("§l§e" + MinecraftLocale.getLocaleString("debug.prefix", session.locale()) + " §r" + MinecraftLocale.getLocaleString("debug.advanced_tooltips." + onOrOff, session.locale()));
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
}
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context -> context.getSender().connection().ifPresent(session -> {
String onOrOff = session.isAdvancedTooltips() ? "off" : "on";
session.setAdvancedTooltips(!session.isAdvancedTooltips());
session.sendMessage("§l§e" + MinecraftLocale.getLocaleString("debug.prefix", session.locale()) + " §r" + MinecraftLocale.getLocaleString("debug.advanced_tooltips." + onOrOff, session.locale()));
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
}));
}
@Override

View File

@ -25,9 +25,10 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
public class AdvancementsCommand extends GeyserCommand {
public AdvancementsCommand(String name, String description, String permission) {
@ -35,10 +36,11 @@ public class AdvancementsCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (session != null) {
session.getAdvancementsCache().buildAndShowMenuForm();
}
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context ->
context.getSender().connection().ifPresent(session ->
session.getAdvancementsCache().buildAndShowMenuForm()));
}
@Override

View File

@ -25,20 +25,25 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.context.CommandContext;
import com.fasterxml.jackson.databind.JsonNode;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.LoopbackUtil;
import org.geysermc.geyser.util.WebUtils;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class ConnectionTestCommand extends GeyserCommand {
private static final String ADDRESS = "address";
private static final String PORT = "port";
private final GeyserImpl geyser;
public ConnectionTestCommand(GeyserImpl geyser, String name, String description, String permission) {
@ -47,64 +52,57 @@ public class ConnectionTestCommand extends GeyserCommand {
}
@Override
public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) {
// Only allow the console to create dumps on Geyser Standalone
if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
return;
}
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.argument(StringArgument.of(ADDRESS))
.argument(IntegerArgument.<GeyserCommandSource>builder(PORT)
.asOptionalWithDefault(19132)
.withMax(65535).withMin(0)
.build())
.handler(this::execute);
}
if (args.length == 0) {
sender.sendMessage("Provide the Bedrock server IP you are trying to connect with. Example: `test.geysermc.org:19132`");
return;
}
// Still allow people to not supply a port and fallback to 19132
String[] fullAddress = args[0].split(":", 2);
int port;
if (fullAddress.length == 2) {
port = Integer.parseInt(fullAddress[1]);
} else {
port = 19132;
}
private void execute(CommandContext<GeyserCommandSource> context) {
GeyserCommandSource source = context.getSender();
String address = context.get(ADDRESS);
int port = context.get(PORT);
// Issue: do the ports not line up?
if (port != geyser.getConfig().getBedrock().port()) {
sender.sendMessage("The port you supplied (" + port + ") does not match the port supplied in Geyser's configuration ("
source.sendMessage("The port you supplied (" + port + ") does not match the port supplied in Geyser's configuration ("
+ geyser.getConfig().getBedrock().port() + "). You can change it under `bedrock` `port`.");
}
// Issue: is the `bedrock` `address` in the config different?
if (!geyser.getConfig().getBedrock().address().equals("0.0.0.0")) {
sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
source.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
}
// Issue: did someone turn on enable-proxy-protocol and they didn't mean it?
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
sender.sendMessage("You have the `enable-proxy-protocol` setting enabled. " +
source.sendMessage("You have the `enable-proxy-protocol` setting enabled. " +
"Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled.");
}
CompletableFuture.runAsync(() -> {
try {
// Issue: SRV record?
String ip = fullAddress[0];
String[] record = WebUtils.findSrvRecord(geyser, ip);
if (record != null && !ip.equals(record[3]) && !record[2].equals(String.valueOf(port))) {
sender.sendMessage("Bedrock Edition does not support SRV records. Try connecting to your server using the address " + record[3] + " and the port " + record[2]
String[] record = WebUtils.findSrvRecord(geyser, address);
if (record != null && !address.equals(record[3]) && !record[2].equals(String.valueOf(port))) {
source.sendMessage("Bedrock Edition does not support SRV records. Try connecting to your server using the address " + record[3] + " and the port " + record[2]
+ ". If that fails, re-run this command with that address and port.");
return;
}
// Issue: does Loopback need applying?
if (LoopbackUtil.needsLoopback(GeyserImpl.getInstance().getLogger())) {
sender.sendMessage("Loopback is not applied on this computer! You will have issues connecting from the same computer. " +
source.sendMessage("Loopback is not applied on this computer! You will have issues connecting from the same computer. " +
"See here for steps on how to resolve: " + "https://wiki.geysermc.org/geyser/fixing-unable-to-connect-to-world/#using-geyser-on-the-same-computer");
}
// mcsrvstatus will likely be replaced in the future with our own service where we can also test
// around the OVH workaround without worrying about caching
JsonNode output = WebUtils.getJson("https://api.mcsrvstat.us/bedrock/2/" + args[0]);
JsonNode output = WebUtils.getJson("https://api.mcsrvstat.us/bedrock/2/" + address);
long cacheTime = output.get("debug").get("cachetime").asLong();
String when;
@ -115,15 +113,15 @@ public class ConnectionTestCommand extends GeyserCommand {
}
if (output.get("online").asBoolean()) {
sender.sendMessage("Your server is likely online as of " + when + "!");
sendLinks(sender);
source.sendMessage("Your server is likely online as of " + when + "!");
sendLinks(source);
return;
}
sender.sendMessage("Your server is likely unreachable from outside the network as of " + when + ".");
sendLinks(sender);
source.sendMessage("Your server is likely unreachable from outside the network as of " + when + ".");
sendLinks(source);
} catch (Exception e) {
sender.sendMessage("Error while trying to check your connection!");
source.sendMessage("Error while trying to check your connection!");
geyser.getLogger().error("Error while trying to check your connection!", e);
}
});

View File

@ -25,17 +25,18 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import cloud.commandframework.arguments.standard.StringArrayArgument;
import cloud.commandframework.context.CommandContext;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.dump.DumpInfo;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.AsteriskSerializer;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
@ -43,11 +44,13 @@ import org.geysermc.geyser.util.WebUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class DumpCommand extends GeyserCommand {
private static final String ARGUMENTS = "args";
private static final List<String> SUGGESTIONS = List.of("full", "offline", "logs");
private final GeyserImpl geyser;
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String DUMP_URL = "https://dump.geysermc.org/";
@ -59,12 +62,20 @@ public class DumpCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
// Only allow the console to create dumps on Geyser Standalone
if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
return;
}
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.argument(createArgument())
.handler(this::execute);
}
private StringArrayArgument<GeyserCommandSource> createArgument() {
// todo suggestions might be broken
return StringArrayArgument.optional(ARGUMENTS, (context, input) -> SUGGESTIONS);
}
public void execute(CommandContext<GeyserCommandSource> context) {
GeyserCommandSource source = context.getSender();
String[] args = context.getOrDefault(ARGUMENTS, new String[0]);
boolean showSensitive = false;
boolean offlineDump = false;
@ -81,7 +92,7 @@ public class DumpCommand extends GeyserCommand {
AsteriskSerializer.showSensitive = showSensitive;
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale()));
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", source.locale()));
String dumpData;
try {
if (offlineDump) {
@ -93,7 +104,7 @@ public class DumpCommand extends GeyserCommand {
dumpData = MAPPER.writeValueAsString(new DumpInfo(addLog));
}
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.locale()));
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", source.locale()));
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
return;
}
@ -101,21 +112,21 @@ public class DumpCommand extends GeyserCommand {
String uploadedDumpUrl = "";
if (offlineDump) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.locale()));
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", source.locale()));
try {
FileOutputStream outputStream = new FileOutputStream(GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile());
outputStream.write(dumpData.getBytes());
outputStream.close();
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.write_error", sender.locale()));
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.write_error", source.locale()));
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.write_error_short"), e);
return;
}
uploadedDumpUrl = "dump.json";
} else {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale()));
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", source.locale()));
String response;
JsonNode responseNode;
@ -123,31 +134,25 @@ public class DumpCommand extends GeyserCommand {
response = WebUtils.post(DUMP_URL + "documents", dumpData);
responseNode = MAPPER.readTree(response);
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.locale()));
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", source.locale()));
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
return;
}
if (!responseNode.has("key")) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", source.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
return;
}
uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText();
}
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
if (!sender.isConsole()) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.commands.dump.created", sender.name(), uploadedDumpUrl));
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", source.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
if (!source.isConsole()) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.commands.dump.created", source.name(), uploadedDumpUrl));
}
}
@NonNull
@Override
public List<String> subCommands() {
return Arrays.asList("offline", "full", "logs");
}
@Override
public boolean isSuggestedOpOnly() {
return true;

View File

@ -25,14 +25,15 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import cloud.commandframework.context.CommandContext;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.List;
@ -47,16 +48,23 @@ public class ExtensionsCommand extends GeyserCommand {
}
@Override
public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) {
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(this::execute);
}
public void execute(CommandContext<GeyserCommandSource> context) {
GeyserCommandSource source = context.getSender();
// TODO: Pagination
int page = 1;
int maxPage = 1;
String header = GeyserLocale.getPlayerLocaleString("geyser.commands.extensions.header", sender.locale(), page, maxPage);
sender.sendMessage(header);
String header = GeyserLocale.getPlayerLocaleString("geyser.commands.extensions.header", source.locale(), page, maxPage);
source.sendMessage(header);
this.geyser.extensionManager().extensions().stream().sorted(Comparator.comparing(Extension::name)).forEach(extension -> {
String extensionName = (extension.isEnabled() ? ChatColor.GREEN : ChatColor.RED) + extension.name();
sender.sendMessage("- " + extensionName + ChatColor.RESET + " v" + extension.description().version() + formatAuthors(extension.description().authors()));
source.sendMessage("- " + extensionName + ChatColor.RESET + " v" + extension.description().version() + formatAuthors(extension.description().authors()));
});
}

View File

@ -25,61 +25,55 @@
package org.geysermc.geyser.command.defaults;
import org.geysermc.geyser.api.util.PlatformType;
import cloud.commandframework.CommandManager;
import cloud.commandframework.context.CommandContext;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
public class HelpCommand extends GeyserCommand {
private final GeyserImpl geyser;
private final String baseCommand;
private final Map<String, Command> commands;
private final Collection<Command> commands;
public HelpCommand(GeyserImpl geyser, String name, String description, String permission,
String baseCommand, Map<String, Command> commands) {
super(name, description, permission);
this.geyser = geyser;
this.baseCommand = baseCommand;
this.commands = commands;
this.commands = commands.values();
this.setAliases(Collections.singletonList("?"));
this.aliases(Collections.singletonList("?"));
}
/**
* Sends the help menu to a command sender. Will not show certain commands depending on the command sender and session.
*
* @param session The Geyser session of the command sender, if it is a bedrock player. If null, bedrock-only commands will be hidden.
* @param sender The CommandSender to send the help message to.
* @param args Not used.
*/
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
public cloud.commandframework.Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(this::execute);
}
private void execute(CommandContext<GeyserCommandSource> context) {
GeyserCommandSource source = context.getSender();
boolean bedrockPlayer = source.connection().isPresent();
// todo: pagination
int page = 1;
int maxPage = 1;
String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.locale(), page, maxPage);
sender.sendMessage(header);
String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", source.locale(), page, maxPage);
source.sendMessage(header);
// todo: commands looks like this doesn't guard against alias keys
this.commands.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
Command cmd = entry.getValue();
// Standalone hack-in since it doesn't have a concept of permissions
if (geyser.getPlatformType() == PlatformType.STANDALONE || sender.hasPermission(cmd.permission())) {
// Only list commands the player can actually run
if (cmd.isBedrockOnly() && session == null) {
return;
}
sender.sendMessage(ChatColor.YELLOW + "/" + baseCommand + " " + entry.getKey() + ChatColor.WHITE + ": " +
GeyserLocale.getPlayerLocaleString(cmd.description(), sender.locale()));
}
});
this.commands.stream()
.distinct() // remove aliases
.sorted(Comparator.comparing(Command::name))
.filter(cmd -> source.hasPermission(cmd.permission()))
.filter(cmd -> !cmd.isBedrockOnly() || bedrockPlayer) // remove bedrock only commands if not a bedrock player
.forEach(cmd -> source.sendMessage(ChatColor.YELLOW + "/" + baseCommand + " " + cmd.name() + ChatColor.WHITE + ": " +
GeyserLocale.getPlayerLocaleString(cmd.description(), source.locale())));
}
}

View File

@ -25,6 +25,8 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
@ -44,12 +46,17 @@ public class ListCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", sender.locale(),
geyser.getSessionManager().size(),
geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::bedrockUsername).collect(Collectors.joining(" ")));
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context -> {
GeyserCommandSource source = context.getSender();
sender.sendMessage(message);
String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", source.locale(),
geyser.getSessionManager().size(),
geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::bedrockUsername).collect(Collectors.joining(" ")));
source.sendMessage(message);
});
}
@Override

View File

@ -25,6 +25,8 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
@ -37,12 +39,9 @@ public class OffhandCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (session == null) {
return;
}
session.requestOffhandSwap();
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context -> context.getSender().connection().ifPresent(GeyserSession::requestOffhandSwap));
}
@Override

View File

@ -25,11 +25,11 @@
package org.geysermc.geyser.command.defaults;
import org.geysermc.geyser.api.util.PlatformType;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
public class ReloadCommand extends GeyserCommand {
@ -42,17 +42,15 @@ public class ReloadCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) {
return;
}
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context -> {
GeyserCommandSource source = context.getSender();
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.reload.message", source.locale()));
String message = GeyserLocale.getPlayerLocaleString("geyser.commands.reload.message", sender.locale());
sender.sendMessage(message);
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
geyser.reload();
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
geyser.reload();
});
}
@Override

View File

@ -25,10 +25,11 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.SettingsUtils;
public class SettingsCommand extends GeyserCommand {
@ -37,10 +38,11 @@ public class SettingsCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (session != null) {
session.sendForm(SettingsUtils.buildForm(session));
}
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context ->
context.getSender().connection().ifPresent(session ->
session.sendForm(SettingsUtils.buildForm(session))));
}
@Override

View File

@ -25,12 +25,13 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import com.github.steveice10.mc.protocol.data.game.ClientCommand;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
public class StatisticsCommand extends GeyserCommand {
@ -39,12 +40,13 @@ public class StatisticsCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (session == null) return;
session.setWaitingForStatistics(true);
ServerboundClientCommandPacket ServerboundClientCommandPacket = new ServerboundClientCommandPacket(ClientCommand.STATS);
session.sendDownstreamPacket(ServerboundClientCommandPacket);
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context -> context.getSender().connection().ifPresent(session -> {
session.setWaitingForStatistics(true);
ServerboundClientCommandPacket packet = new ServerboundClientCommandPacket(ClientCommand.STATS);
session.sendDownstreamPacket(packet);
}));
}
@Override

View File

@ -25,12 +25,11 @@
package org.geysermc.geyser.command.defaults;
import org.geysermc.geyser.api.util.PlatformType;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
import java.util.Collections;
@ -42,17 +41,13 @@ public class StopCommand extends GeyserCommand {
super(name, description, permission);
this.geyser = geyser;
this.setAliases(Collections.singletonList("shutdown"));
this.aliases(Collections.singletonList("shutdown"));
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
return;
}
geyser.getBootstrap().onDisable();
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(context -> geyser.getBootstrap().onDisable());
}
@Override

View File

@ -25,13 +25,15 @@
package org.geysermc.geyser.command.defaults;
import cloud.commandframework.Command;
import cloud.commandframework.CommandManager;
import cloud.commandframework.context.CommandContext;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.WebUtils;
@ -52,7 +54,14 @@ public class VersionCommand extends GeyserCommand {
}
@Override
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
public Command.Builder<GeyserCommandSource> builder(CommandManager<GeyserCommandSource> manager) {
return super.builder(manager)
.handler(this::execute);
}
public void execute(CommandContext<GeyserCommandSource> context) {
GeyserCommandSource source = context.getSender();
String bedrockVersions;
List<BedrockCodec> supportedCodecs = GameProtocol.SUPPORTED_BEDROCK_CODECS;
if (supportedCodecs.size() > 1) {
@ -68,12 +77,12 @@ public class VersionCommand extends GeyserCommand {
javaVersions = supportedJavaVersions.get(0);
}
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", sender.locale(),
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", source.locale(),
GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions));
// Disable update checking in dev mode and for players in Geyser Standalone
if (GeyserImpl.getInstance().isProductionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
if (GeyserImpl.getInstance().isProductionEnvironment() && !(!source.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", source.locale()));
try {
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" +
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
@ -81,17 +90,17 @@ public class VersionCommand extends GeyserCommand {
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
int buildNum = this.geyser.buildNumber();
if (latestBuildNum == buildNum) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", source.locale()));
} else {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated",
sender.locale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/"));
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated",
source.locale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/"));
}
} else {
throw new AssertionError("buildNumber missing");
}
} catch (IOException e) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", source.locale()));
}
}
}

View File

@ -1403,7 +1403,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Override
public String name() {
return null;
return playerEntity.getUsername();
}
@Override
@ -1429,11 +1429,23 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return Optional.of(playerEntity.getUuid());
}
@Override
public Optional<GeyserSession> connection() {
return Optional.of(this);
}
@Override
public String locale() {
return clientData.getLanguageCode();
}
@Override
public boolean hasPermission(String permission) {
// for Geyser-Standalone, standalone's permission system will handle it.
// for server platforms, the session will be mapped to a server command sender, and the server's api will be used.
return geyser.commandManager().cloud().hasPermission(this, permission);
}
/**
* Sends a chat message to the Java server.
*/
@ -1685,18 +1697,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
upstream.sendPacket(gameRulesChangedPacket);
}
/**
* Checks if the given session's player has a permission
*
* @param permission The permission node to check
* @return true if the player has the requested permission, false if not
*/
@Override
public boolean hasPermission(String permission) {
// let the cloud implementation handle permission checking
return geyser.commandManager().cloud().hasPermission(this, permission);
}
private static final Ability[] USED_ABILITIES = Ability.values();
/**

@ -1 +1 @@
Subproject commit fda08c186de979ff89b01d2b66f48daa5e218d01
Subproject commit e9c9e9bbcfe037f1be85bf323784a583f313c3d1