Skip to content

Commit

Permalink
feat: update to cloud v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Citymonstret committed Dec 26, 2023
1 parent 060417a commit cdeece1
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 194 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ configurations {
}

repositories {
mavenLocal()
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://maven.neoforged.net/releases/")
Expand All @@ -44,7 +45,7 @@ dependencies {
mappings(loom.officialMojangMappings())
neoForge("net.neoforged:neoforge:20.4.43-beta")

api(platform("cloud.commandframework:cloud-bom:1.8.4"))
api(platform("cloud.commandframework:cloud-bom:2.0.0-SNAPSHOT"))
api("cloud.commandframework:cloud-core")
api("cloud.commandframework:cloud-brigadier")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,17 @@
package cloud.commandframework.neoforge;

import cloud.commandframework.Command;
import cloud.commandframework.CommandTree;
import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.internal.CommandNode;
import cloud.commandframework.permission.AndPermission;
import cloud.commandframework.permission.CommandPermission;
import cloud.commandframework.permission.OrPermission;
import cloud.commandframework.permission.Permission;
import cloud.commandframework.permission.PredicatePermission;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
Expand All @@ -50,6 +48,8 @@
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
import org.checkerframework.checker.nullness.qual.Nullable;

import static cloud.commandframework.arguments.standard.StringParser.greedyStringParser;

@Mod("cloud")
public final class CloudNeoForgeEntrypoint {
private static boolean serverStartingCalled;
Expand Down Expand Up @@ -82,7 +82,7 @@ private static void registerPermissions(final PermissionGatherEvent.Nodes event)

private static void registerPermissionsForManager(final PermissionGatherEvent.Nodes event, final NeoForgeCommandManager<?> manager) {
final Set<String> permissions = new HashSet<>();
collectPermissions(permissions, manager.commandTree().getRootNodes());
collectPermissions(permissions, manager.commandTree().rootNodes());
permissions.stream()
.filter(permissionString -> event.getNodes().stream().noneMatch(node -> node.getNodeName().equals(permissionString)))
.map(permissionString -> {
Expand All @@ -99,29 +99,27 @@ private static void registerPermissionsForManager(final PermissionGatherEvent.No

private static <C> void collectPermissions(
final Set<String> permissions,
final Collection<CommandTree.Node<CommandArgument<C, ?>>> nodes
final Collection<CommandNode<C>> nodes
) {
for (final CommandTree.Node<CommandArgument<C, ?>> node : nodes) {
final @Nullable Command<C> owningCommand = node.getValue().getOwningCommand();
for (final CommandNode<C> node : nodes) {
final @Nullable Command<C> owningCommand = node.component().owningCommand();
if (owningCommand != null) {
recurseCommandPermission(permissions, owningCommand.getCommandPermission());
recurseCommandPermission(permissions, owningCommand.commandPermission());
}
collectPermissions(permissions, node.getChildren());
collectPermissions(permissions, node.children());
}
}

private static void recurseCommandPermission(final Set<String> permissions, final CommandPermission permission) {
private static void recurseCommandPermission(final Set<String> permissions, final Permission permission) {
if (permission instanceof PredicatePermission<?> || permission == Permission.empty()) {
return;
}
if (permission instanceof OrPermission || permission instanceof AndPermission) {
for (final CommandPermission child : permission.getPermissions()) {
for (final Permission child : permission.permissions()) {
recurseCommandPermission(permissions, child);
}
} else if (permission instanceof Permission p) {
permissions.add(p.getPermission());
} else {
throw new IllegalStateException();
permissions.add(permission.permissionString());
}
}

Expand All @@ -134,17 +132,17 @@ private static void testClientManager() {
manager.brigadierManager().setNativeNumberSuggestions(false);
manager.command(manager.commandBuilder("cloud_client")
.literal("forge")
.argument(StringArgument.greedy("string"))
.handler(ctx -> ctx.getSender().sendSystemMessage(Component.literal(ctx.get("string")))));
.required("string", greedyStringParser())
.handler(ctx -> ctx.sender().sendSystemMessage(Component.literal(ctx.get("string")))));
}

private static void testServerManager() {
final NeoForgeServerCommandManager<CommandSourceStack> manager = NeoForgeServerCommandManager.createNative(CommandExecutionCoordinator.simpleCoordinator());
manager.brigadierManager().setNativeNumberSuggestions(false);
manager.command(manager.commandBuilder("cloud")
.literal("forge")
.argument(StringArgument.greedy("string"))
.required("string", greedyStringParser())
.permission("cloud.hello")
.handler(ctx -> ctx.getSender().sendSystemMessage(Component.literal(ctx.get("string")))));
.handler(ctx -> ctx.sender().sendSystemMessage(Component.literal(ctx.get("string")))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
package cloud.commandframework.neoforge;

import cloud.commandframework.keys.CloudKey;
import cloud.commandframework.keys.SimpleCloudKey;
import io.leangen.geantyref.TypeToken;
import net.minecraft.commands.CommandSourceStack;

Expand All @@ -33,7 +32,7 @@ public final class NeoForgeCommandContextKeys {
private NeoForgeCommandContextKeys() {
}

public static final CloudKey<CommandSourceStack> NATIVE_COMMAND_SOURCE = SimpleCloudKey.of(
public static final CloudKey<CommandSourceStack> NATIVE_COMMAND_SOURCE = CloudKey.of(
"cloud:forge_command_source",
TypeToken.get(CommandSourceStack.class)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,55 @@

import cloud.commandframework.CommandManager;
import cloud.commandframework.CommandTree;
import cloud.commandframework.arguments.suggestion.SuggestionFactory;
import cloud.commandframework.brigadier.BrigadierManagerHolder;
import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.brigadier.suggestion.TooltipSuggestion;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.*;
import cloud.commandframework.exceptions.handling.ExceptionContext;
import cloud.commandframework.exceptions.handling.ExceptionHandler;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.execution.FilteringCommandSuggestionProcessor;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.logging.LogUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.*;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.slf4j.Logger;

@DefaultQualifier(NonNull.class)
public abstract class NeoForgeCommandManager<C>
extends CommandManager<C> implements BrigadierManagerHolder<C> {

static final Set<NeoForgeCommandManager<?>> INSTANCES = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));

private static final Logger LOGGER = LogUtils.getLogger();
private static final Component NEWLINE = Component.literal("\n");
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS =
"I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error.";
private static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command. Type \"/help\" for help.";

private final Function<CommandSourceStack, C> commandSourceMapper;
private final Function<C, CommandSourceStack> backwardsCommandSourceMapper;
private final CloudBrigadierManager<C, CommandSourceStack> brigadierManager;
private final SuggestionFactory<C, ? extends TooltipSuggestion> suggestionFactory;

protected NeoForgeCommandManager(
final Function<CommandTree<C>, CommandExecutionCoordinator<C>> commandExecutionCoordinator,
Expand All @@ -61,16 +86,18 @@ protected NeoForgeCommandManager(
INSTANCES.add(this);
this.commandSourceMapper = commandSourceMapper;
this.backwardsCommandSourceMapper = backwardsCommandSourceMapper;
this.suggestionFactory = super.suggestionFactory().mapped(TooltipSuggestion::tooltipSuggestion);
this.brigadierManager = new CloudBrigadierManager<>(this, () -> new CommandContext<>(
this.commandSourceMapper.apply(dummyCommandSourceProvider.get()),
this
));
), this.suggestionFactory);
this.brigadierManager.backwardsBrigadierSenderMapper(this.backwardsCommandSourceMapper);
this.brigadierManager.brigadierSenderMapper(this.commandSourceMapper);
this.registerCommandPreProcessor(new NeoForgeCommandPreprocessor<>(this));
this.commandSuggestionProcessor(new FilteringCommandSuggestionProcessor<>(
FilteringCommandSuggestionProcessor.Filter.<C>startsWith(true).andTrimBeforeLastSpace()
));
this.registerDefaultExceptionHandlers();
registrationHandler.initialize(this);
}

Expand All @@ -97,7 +124,99 @@ public CloudBrigadierManager<C, CommandSourceStack> brigadierManager() {
return this.brigadierManager;
}

@Override
public @NonNull SuggestionFactory<C, ? extends TooltipSuggestion> suggestionFactory() {
return this.suggestionFactory;
}

final void registrationCalled() {
this.lockRegistration();
}

private void registerDefaultExceptionHandlers() {
final BiConsumer<CommandSourceStack, Component> sendError = CommandSourceStack::sendFailure;
final Function<CommandSourceStack, String> getName = CommandSourceStack::getTextName;

this.registerHandler(Throwable.class, (source, sender, throwable) -> {
sendError.accept(source, this.decorateHoverStacktrace(
Component.literal(MESSAGE_INTERNAL_ERROR),
throwable,
sender
));
LOGGER.warn("Error occurred while executing command for user {}:", getName.apply(source), throwable);
});
this.registerHandler(CommandExecutionException.class, (source, sender, throwable) -> {
sendError.accept(source, this.decorateHoverStacktrace(
Component.literal(MESSAGE_INTERNAL_ERROR),
throwable.getCause(),
sender
));
LOGGER.warn(
"Error occurred while executing command for user {}:",
getName.apply(source),
throwable.getCause()
);
});
this.registerHandler(ArgumentParseException.class, (source, sender, throwable) -> {
if (throwable.getCause() instanceof CommandSyntaxException) {
sendError.accept(source, Component.literal("Invalid Command Argument: ")
.append(Component.literal("")
.append(ComponentUtils
.fromMessage(((CommandSyntaxException) throwable.getCause()).getRawMessage()))
.withStyle(ChatFormatting.GRAY)));
} else {
sendError.accept(source, Component.literal("Invalid Command Argument: ")
.append(Component.literal(throwable.getCause().getMessage())
.withStyle(ChatFormatting.GRAY)));
}
});
this.registerHandler(NoSuchCommandException.class, (source, sender, throwable) ->
sendError.accept(source, Component.literal(MESSAGE_UNKNOWN_COMMAND)));
this.registerHandler(NoPermissionException.class, (source, sender, throwable) ->
sendError.accept(source, Component.literal(MESSAGE_NO_PERMS)));
this.registerHandler(InvalidCommandSenderException.class, (source, sender, throwable) ->
sendError.accept(source, Component.literal(throwable.getMessage())));
this.registerHandler(InvalidSyntaxException.class, (source, sender, throwable) ->
sendError.accept(source, Component.literal("Invalid Command Syntax. Correct command syntax is: ")
.append(Component.literal(String.format("/%s", throwable.getCorrectSyntax()))
.withStyle(style -> style.withColor(ChatFormatting.GRAY)))));
}

private <T extends Throwable> void registerHandler(final Class<T> exceptionType, final NeoForgeExceptionHandler<C, T> handler) {
this.exceptionController().registerHandler(exceptionType, handler);
}

private MutableComponent decorateHoverStacktrace(final MutableComponent input, final Throwable cause, final C sender) {
if (!this.hasPermission(sender, "cloud.hover-stacktrace")) {
return input;
}

final StringWriter writer = new StringWriter();
cause.printStackTrace(new PrintWriter(writer));
final String stackTrace = writer.toString().replace("\t", " ");
return input.withStyle(style -> style
.withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
Component.literal(stackTrace)
.append(NEWLINE)
.append(Component.literal(" Click to copy")
.withStyle(s2 -> s2.withColor(ChatFormatting.GRAY).withItalic(true)))
))
.withClickEvent(new ClickEvent(
ClickEvent.Action.COPY_TO_CLIPBOARD,
stackTrace
)));
}

@FunctionalInterface
private interface NeoForgeExceptionHandler<C, T extends Throwable> extends ExceptionHandler<C, T> {

@Override
default void handle(final ExceptionContext<C, T> context) throws Throwable {
final CommandSourceStack commandSourceStack = context.context().get(NeoForgeCommandContextKeys.NATIVE_COMMAND_SOURCE);
this.handle(commandSourceStack, context.context().sender(), context.exception());
}

void handle(CommandSourceStack source, C sender, T throwable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ final class NeoForgeCommandPreprocessor<C> implements CommandPreprocessor<C> {

@Override
public void accept(final CommandPreprocessingContext<C> context) {
context.getCommandContext().store(
context.commandContext().store(
NeoForgeCommandContextKeys.NATIVE_COMMAND_SOURCE,
this.manager.backwardsCommandSourceMapper().apply(context.getCommandContext().getSender())
this.manager.backwardsCommandSourceMapper().apply(context.commandContext().sender())
);
}
}
Loading

0 comments on commit cdeece1

Please sign in to comment.