Skip to content

Commit

Permalink
add get-exception command and /execute parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
UpcraftLP committed Sep 24, 2021
1 parent 4518e44 commit 70779e1
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Mesh
* Copyright (C) 2019-2021 UpcraftLP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; If not, see <https://www.gnu.org/licenses>.
*/
package dev.upcraft.mesh.mixin.impl.command;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.ParseResults;
import dev.upcraft.mesh.util.command.CommandHelper;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.function.CommandFunction;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(CommandFunction.CommandElement.class)
public abstract class MixinCommandElement {

@Redirect(method = "execute(Lnet/minecraft/server/function/CommandFunctionManager;Lnet/minecraft/server/command/ServerCommandSource;)I", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/CommandDispatcher;execute(Lcom/mojang/brigadier/ParseResults;)I"))
private int extractCommandException(CommandDispatcher<ServerCommandSource> commandDispatcher, ParseResults<ServerCommandSource> parse) throws Exception {
int value = 0;
Exception ex = null;
String command = parse.getReader().getString();
try {
value = commandDispatcher.execute(parse);
} catch (Exception e) {
ex = e;
throw e;
} finally {
CommandHelper.recordCommand(command, value, ex);
}
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Mesh
* Copyright (C) 2019-2021 UpcraftLP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; If not, see <https://www.gnu.org/licenses>.
*/
package dev.upcraft.mesh.mixin.impl.command;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.StringReader;
import dev.upcraft.mesh.util.command.CommandHelper;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(CommandManager.class)
public abstract class MixinCommandManager {

@Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/CommandDispatcher;execute(Lcom/mojang/brigadier/StringReader;Ljava/lang/Object;)I"))
private int extractCommandException(CommandDispatcher<ServerCommandSource> commandDispatcher, StringReader input, Object commandSourceObj, ServerCommandSource source, String command) throws Exception {
int value = 0;
Exception ex = null;
try {
value = commandDispatcher.execute(input, source);
}
catch (Exception e) {
ex = e;
throw e;
}
finally {
CommandHelper.recordCommand(command, value, ex);
}
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Mesh
* Copyright (C) 2019-2021 UpcraftLP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; If not, see <https://www.gnu.org/licenses>.
*/
package dev.upcraft.mesh.mixin.impl.command;

import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.tree.CommandNode;
import dev.upcraft.mesh.util.command.CommandHelper;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ExecuteCommand;
import net.minecraft.server.command.ServerCommandSource;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.Locale;

@Mixin(ExecuteCommand.class)
public abstract class MixinExecuteCommand {

@Shadow
private static ArgumentBuilder<ServerCommandSource, ?> addConditionLogic(CommandNode<ServerCommandSource> root, ArgumentBuilder<ServerCommandSource, ?> builder, boolean positive, ExecuteCommand.Condition condition) {
return null;
}

@Shadow @Final private static SimpleCommandExceptionType CONDITIONAL_FAIL_EXCEPTION;

@Inject(method = "addConditionArguments", at = @At("HEAD"))
private static void injectExceptionCondition(CommandNode<ServerCommandSource> root, LiteralArgumentBuilder<ServerCommandSource> argumentBuilder, boolean positive, CallbackInfoReturnable<ArgumentBuilder<ServerCommandSource, ?>> cir) {
argumentBuilder.then(CommandManager.literal("exception").then(addConditionLogic(root, CommandManager.argument("exception_message", StringArgumentType.string()), positive, context -> {
String msg = StringArgumentType.getString(context, "exception_message");
if(msg.isBlank()) {
throw CONDITIONAL_FAIL_EXCEPTION.create();
}
return CommandHelper.getLastCommandException().filter(ex -> ex.getMessage().toLowerCase(Locale.ROOT).contains(msg)).isPresent();
})));
}
}
52 changes: 52 additions & 0 deletions src/main/java/dev/upcraft/mesh/util/command/CommandHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@
package dev.upcraft.mesh.util.command;

import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
import net.minecraft.command.CommandException;
import net.minecraft.text.MutableText;
import net.minecraft.text.TranslatableText;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;

public class CommandHelper {

Expand All @@ -37,4 +45,48 @@ public static <S> LiteralArgumentBuilder<S> createRedirect(LiteralArgumentBuilde
target.getChildren().forEach(builder::then);
return builder;
}

public static MutableText getCopyToClipboardText() {
return new TranslatableText("chat.copy");
}

/**
* @since 0.14.0
*/
public static MutableText getClickToCopyToClipboardText() {
return new TranslatableText("chat.copy.click");
}

/**
* retrieves the most recent recorded exception thrown while executing commands
* @see CommandException
* @see CommandSyntaxException
*
* @since 0.14.0
*/
public static Optional<Exception> getLastCommandException() {
return Optional.ofNullable(lastCommandException);
}

@Nullable
private static Exception lastCommandException = null;

public static boolean clearException() {
boolean result = lastCommandException != null;
lastCommandException = null;
return result;
}

/**
* records a command that was used, its result, and the exception that was thrown, if any
* @implNote if the exception is not {@code null}, the result parameter becomes meaningless and should always be {@code 0}
*
* @since 0.14.0
*/
public static void recordCommand(String command, int result, @Nullable Exception e) {
// TODO do more processing on commands
if(e != null) {
lastCommandException = e;
}
}
}
5 changes: 3 additions & 2 deletions src/main/java/dev/upcraft/mesh/util/command/MeshCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ public class MeshCommand {
public static void init() {
CommandRegistrationCallback.EVENT.register((commandDispatcher, dedicatedServer) -> {
LiteralArgumentBuilder<ServerCommandSource> builder = CommandManager.literal(Mesh.MODID).requires(source -> source.hasPermissionLevel(4));
builder = TpDimCommand.append(builder);
builder = HandCommand.append(builder);
builder = DebugCommand.append(builder);
builder = DumpRecipesCommand.append(builder);
builder = DumpTagsCommand.append(builder);
builder = GetExceptionCommand.append(builder);
builder = HandCommand.append(builder);
builder = StructureFilterCommand.append(builder);
builder = TpDimCommand.append(builder);
commandDispatcher.register(builder);
});
ArgumentTypes.register(new Identifier(Mesh.MODID, "block_tag").toString(), BlockTagArgumentType.class, new ConstantArgumentSerializer<>(BlockTagArgumentType::tag));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Mesh
* Copyright (C) 2019-2021 UpcraftLP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; If not, see <https://www.gnu.org/licenses>.
*/
package dev.upcraft.mesh.util.command.mesh;

import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import dev.upcraft.mesh.util.command.CommandHelper;
import net.minecraft.command.CommandException;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.*;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Optional;

public class GetExceptionCommand {

public static LiteralArgumentBuilder<ServerCommandSource> append(LiteralArgumentBuilder<ServerCommandSource> $) {
return $.then(CommandManager.literal("exception").requires(source -> source.hasPermissionLevel(2)).then(CommandManager.literal("get").executes(ctx -> getException(ctx, false)).then(CommandManager.argument("clearException", BoolArgumentType.bool()).executes(ctx -> {
boolean clear = BoolArgumentType.getBool(ctx, "clearException");
return getException(ctx, clear);
}))).then(CommandManager.literal("clear").executes(ctx -> {
ctx.getSource().sendFeedback(new TranslatableText("command.mesh.get_exception.clear"), true);
return CommandHelper.clearException() ? 1 : 0;
})));
}

private static int getException(CommandContext<ServerCommandSource> ctx, boolean clear) {
Optional<Exception> opt = CommandHelper.getLastCommandException();
if(opt.isEmpty()) {
ctx.getSource().sendError(new TranslatableText("command.mesh.get_exception.empty"));
return 0;
}
else {
Exception ex = opt.get();
MutableText text;
if(ex instanceof CommandException cme) {
text = cme.getTextMessage() instanceof MutableText mtxt ? mtxt : cme.getTextMessage().shallowCopy();
}
else if(ex instanceof CommandSyntaxException cse) {
Text exceptionMessage = Texts.toText(cse.getRawMessage());
text = exceptionMessage instanceof MutableText mtxt ? mtxt : exceptionMessage.shallowCopy();
}
else {
String exMessage = ex.getMessage();
if(exMessage.isBlank()) {
exMessage = "Unknown Exception";
}
text = new LiteralText(exMessage);
}

StringWriter sw = new StringWriter();
try (PrintWriter printer = new PrintWriter(sw)) {
ex.printStackTrace(printer);
}

text.styled(it -> it.withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, sw.toString())).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, CommandHelper.getClickToCopyToClipboardText())));

ctx.getSource().sendFeedback(text, false);

if(clear) {
CommandHelper.clearException();
}
return 1;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import dev.upcraft.mesh.api.command.MeshCommandExceptions;
import dev.upcraft.mesh.api.command.argument.BlockTagArgumentType;
import dev.upcraft.mesh.api.util.MeshResourceListenerKeys;
import dev.upcraft.mesh.util.command.CommandHelper;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.fabricmc.fabric.api.resource.ResourceReloadListenerKeys;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
Expand Down Expand Up @@ -105,7 +106,7 @@ private static int toCommandSender(CommandContext<ServerCommandSource> ctx, List
}
for (int i = 1; i <= values.size(); i++) {
Identifier id = values.get(i - 1);
Text message = new TranslatableText("command.mesh.filter_structures.list_item", i, new LiteralText(id.toString()).formatted(Formatting.WHITE)).styled(it -> it.withFormatting(Formatting.GRAY).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, id.toString())).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TranslatableText("chat.copy.click"))));
Text message = new TranslatableText("command.mesh.filter_structures.list_item", i, new LiteralText(id.toString()).formatted(Formatting.WHITE)).styled(it -> it.withFormatting(Formatting.GRAY).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, id.toString())).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, CommandHelper.getClickToCopyToClipboardText())));
ctx.getSource().sendFeedback(message, false);
}
return values.size();
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/assets/mesh/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"command.mesh.filter_structures.fail": "No structures found matching your criteria!",
"command.mesh.filter_structures.list_item": "#%s: %s",
"command.mesh.filter_structures.file": "Successfully saved %s structure names at %s!",
"command.mesh.get_exception.empty": "No recorded exception found!",
"command.mesh.get_exception.clear": "Exception cache cleared!",
"command.mesh.argument.block_tag.unknown": "Unknown block tag: %s",
"command.mesh.argument.enum.invalid": "No such element in %s: %s",
"command.mesh.argument.multiblock.invalid": "Unknown multiblock: %s",
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/mesh.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ accessible field net/minecraft/util/registry/Registry DEFAULT_ENTRIES Ljava/util

accessible field net/minecraft/client/render/block/entity/EndPortalBlockEntityRenderer field_21732 Ljava/util/List;
accessible field net/minecraft/client/render/entity/model/PlayerEntityModel cloak Lnet/minecraft/client/model/ModelPart;

extendable class net/minecraft/server/command/ExecuteCommand$Condition
3 changes: 3 additions & 0 deletions src/main/resources/mixins.mesh.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"package": "dev.upcraft.mesh.mixin.impl",
"plugin": "dev.upcraft.mesh.mixin.MeshMixinConfig",
"mixins": [
"command.MixinCommandElement",
"command.MixinCommandManager",
"command.MixinExecuteCommand",
"crafting.MixinPotionSlot",
"crafting.MixinRecipeManager",
"event.MixinLivingEntity",
Expand Down

0 comments on commit 70779e1

Please sign in to comment.