Skip to content

Commit

Permalink
feat: better gamemode command
Browse files Browse the repository at this point in the history
  • Loading branch information
Citymonstret committed Feb 1, 2024
1 parent bf55616 commit 092783e
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public final class CaptionKeys {
* Variables: {@code <gamemode>}.
*/
public static final Caption UTILITY_COMMAND_GAMEMODE_UPDATED = of("utility.command.gamemode.updated");
/**
* Variables: {@code <target>}, {@code <gamemode>}.
*/
public static final Caption UTILITY_COMMAND_GAMEMODE_UPDATED_OTHER = of("utility.command.gamemode.updated.other");

private static @NonNull Caption of(final @NonNull String key) {
return Caption.of(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.bean.CommandBean;
import org.incendo.cloud.key.CloudKey;

/**
* An extension of {@link CommandBean} which does extra pre-processing of the commands.
Expand All @@ -36,9 +37,19 @@ public abstract class KitchenSinkCommandBean extends CommandBean<KitchenSinkComm
protected final Command.@NonNull Builder<? extends KitchenSinkCommandSender> configure(
final Command.@NonNull Builder<KitchenSinkCommandSender> builder
) {
return this.configureKitchenSinkCommand(builder);
return this.configureKitchenSinkCommand(builder)
.meta(CloudKey.of("bukkit_description", String.class), this.stringDescription());
}

/**
* Returns a simple string description of this command.
*
* <p>This is primarily used in the platform-native help menus.</p>
*
* @return command description
*/
public abstract String stringDescription();

/**
* Configures the command and returns the updated builder.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,18 @@ public GameModeCommand(final @NonNull Formatter formatter) {
return CommandProperties.of("gamemode", "gm");
}

@Override
public String stringDescription() {
return "Set your own game mode";
}

@Override
protected Command.@NonNull Builder<? extends KitchenSinkCommandSender> configureKitchenSinkCommand(
final Command.@NonNull Builder<KitchenSinkCommandSender> builder
) {
// TODO(City): Make the command take in an optional player argument that defaults
// to the executing player (if the sender is a player).
return builder.required("gameMode", gameModeParser())
return builder
.permission("kitchensink.command.utility.gamemode")
.required("gameMode", gameModeParser())
.senderType(KitchenSinkPlayer.class)
.handler((FutureCommandExecutionHandler<KitchenSinkPlayer>) context -> {
final GameMode gameMode = context.get("gameMode");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//
// MIT License
//
// Copyright (c) 2024 Incendo
//
// 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 org.incendo.kitchensink.command.commands;

import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.bean.CommandProperties;
import org.incendo.cloud.caption.CaptionVariable;
import org.incendo.kitchensink.caption.CaptionKeys;
import org.incendo.kitchensink.caption.Formatter;
import org.incendo.kitchensink.command.KitchenSinkCommandBean;
import org.incendo.kitchensink.command.KitchenSinkCommandSender;
import org.incendo.kitchensink.entity.PlayerRepository;
import org.incendo.kitchensink.entity.player.GameMode;
import org.incendo.kitchensink.entity.player.KitchenSinkPlayer;

import static org.incendo.kitchensink.command.parser.GameModeParser.gameModeParser;
import static org.incendo.kitchensink.command.parser.PlayerParser.playerParser;

@Singleton
public final class GameModeOtherCommand extends KitchenSinkCommandBean {

private final Formatter formatter;
private final PlayerRepository<?, ?> playerRepository;

/**
* Creates a new instance.
*
* @param formatter caption formatter
* @param playerRepository repository for players
*/
@Inject
public GameModeOtherCommand(final @NonNull Formatter formatter, final @NonNull PlayerRepository<?, ?> playerRepository) {
this.formatter = Objects.requireNonNull(formatter, "formatter");
this.playerRepository = Objects.requireNonNull(playerRepository, "playerRepository");
}

@Override
protected @NonNull CommandProperties properties() {
return CommandProperties.of("gamemode", "gm");
}

@Override
public String stringDescription() {
return "Set a player's game mode";
}

@Override
protected Command.@NonNull Builder<? extends KitchenSinkCommandSender> configureKitchenSinkCommand(
final Command.@NonNull Builder<KitchenSinkCommandSender> builder
) {
return builder
.permission("kitchensink.command.utility.gamemode.other")
.required("gameMode", gameModeParser())
.required("target", playerParser(this.playerRepository))
.handler((FutureCommandExecutionHandler<KitchenSinkCommandSender>) context -> {
final GameMode gameMode = context.get("gameMode");
final KitchenSinkPlayer target = context.get("target");

return target.gameMode(gameMode).whenComplete(($, error) -> {
context.sender().sendMessage(
this.formatter.format(
context.sender(),
CaptionKeys.UTILITY_COMMAND_GAMEMODE_UPDATED_OTHER,
CaptionVariable.of("target", target.name()),
CaptionVariable.of("gamemode", gameMode.key()) // TODO(City): Use RichVariable
)
);
});
});
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//
// MIT License
//
// Copyright (c) 2024 Incendo
//
// 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 org.incendo.kitchensink.command.parser;

import java.util.Objects;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.caption.Caption;
import org.incendo.cloud.caption.CaptionVariable;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.exception.parsing.ParserException;
import org.incendo.cloud.parser.ArgumentParseResult;
import org.incendo.cloud.parser.ArgumentParser;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.suggestion.BlockingSuggestionProvider;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.kitchensink.command.KitchenSinkCommandSender;
import org.incendo.kitchensink.entity.KitchenSinkEntity;
import org.incendo.kitchensink.entity.PlayerRepository;
import org.incendo.kitchensink.entity.player.KitchenSinkPlayer;

/**
* Parses online players.
*/
public final class PlayerParser implements ArgumentParser<KitchenSinkCommandSender, KitchenSinkPlayer>,
BlockingSuggestionProvider<KitchenSinkCommandSender> {

/**
* Creates a new player parser.
*
* @param playerRepository player repository
* @return the parser
*/
public static @NonNull ParserDescriptor<KitchenSinkCommandSender, KitchenSinkPlayer> playerParser(
final @NonNull PlayerRepository<?, ?> playerRepository
) {
return ParserDescriptor.of(new PlayerParser(playerRepository), KitchenSinkPlayer.class);
}

private final PlayerRepository<?, ?> playerRepository;

private PlayerParser(final @NonNull PlayerRepository<?, ?> playerRepository) {
this.playerRepository = Objects.requireNonNull(playerRepository, "playerRepository");
}

@Override
public @NonNull ArgumentParseResult<@NonNull KitchenSinkPlayer> parse(
final @NonNull CommandContext<@NonNull KitchenSinkCommandSender> commandContext,
final @NonNull CommandInput commandInput
) {
// Try to parse a UUID. If that fails, we instead try to parse a player.
final String input = commandInput.readString();

KitchenSinkPlayer player = null;

try {
final UUID uuid = UUID.fromString(input);
player = this.playerRepository.findById(uuid).orElse(null);
} catch (final IllegalArgumentException ignored) {
}

for (final KitchenSinkPlayer candidate : this.playerRepository.players()) {
if (candidate.name().equalsIgnoreCase(input)) {
player = candidate;
break;
}
}

if (player != null) {
return ArgumentParseResult.success(player);
}
return ArgumentParseResult.failure(new PlayerParseException(input, commandContext));
}

@Override
public @NonNull Iterable<@NonNull Suggestion> suggestions(
final @NonNull CommandContext<KitchenSinkCommandSender> context,
final @NonNull CommandInput input
) {
return this.playerRepository.players().stream().map(KitchenSinkEntity::name).map(Suggestion::simple).toList();
}


/**
* Player parse exception
*/
public static final class PlayerParseException extends ParserException {

private final String input;

/**
* Construct a new Player parse exception.
*
* @param input string input
* @param context command context
*/
public PlayerParseException(
final @NonNull String input,
final @NonNull CommandContext<?> context
) {
super(
PlayerParser.class,
context,
Caption.of("argument.parse.failure.player"),
CaptionVariable.of("input", input)
);
this.input = input;
}

/**
* Returns the supplied input-
*
* @return string value
*/
public @NonNull String input() {
return this.input;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
//
package org.incendo.kitchensink.entity;

import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.NonNull;
Expand Down Expand Up @@ -59,4 +60,11 @@ public interface PlayerRepository<T extends KitchenSinkPlayer, U> {
* @return created player
*/
@NonNull T create(@NonNull U platformPlayer);

/**
* Returns all players.
*
* @return all players
*/
@NonNull Collection<@NonNull T> players();
}
Loading

0 comments on commit 092783e

Please sign in to comment.