Skip to content

Commit

Permalink
Feat. Rule runCommandOnSign finished.
Browse files Browse the repository at this point in the history
  • Loading branch information
Apricityx committed Dec 6, 2024
1 parent 47f1430 commit dba1093
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 7 deletions.
9 changes: 9 additions & 0 deletions src/main/java/tech/mctown/cma/CMALogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public static void toConsole(String message) {
System.out.println(message);
}

public static void debug(String message) {
System.out.println("\033[34m[CMA DEBUG]\033[0m" + "\033[33m" + message + "\033[0m");
}

public static void toGame(String message, ServerPlayerEntity player) {
player.sendMessage(Text.of(message), false);
}
Expand All @@ -26,4 +30,9 @@ public static void toGame(String message, MinecraftServer server) {
public static void toGame(String message, PlayerEntity player) {
player.sendMessage(Text.of(message));
}

public static void error(String s, Exception e) {
System.out.println("\033[31m[CMA ERROR]\033[0m" + "\033[33m" + s + "\033[0m");
e.printStackTrace();
}
}
50 changes: 50 additions & 0 deletions src/main/java/tech/mctown/cma/CMAUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package tech.mctown.cma;

import carpet.CarpetSettings;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting;

import java.util.Map;

public class CMAUtils {
public static void executeCommand(ServerPlayerEntity player, String command) {
if (player == null || command == null || command.trim().isEmpty()) {
throw new IllegalArgumentException("Player or command cannot be null or empty");
}
// 若带有 / 则去掉
if (command.startsWith("/")) {
command = command.substring(1);
}
var server = player.getServer();
if (server == null) {
throw new IllegalStateException("Player is not on a server");
}

ServerCommandSource source = player.getCommandSource();
CommandDispatcher<ServerCommandSource> dispatcher = server.getCommandManager().getDispatcher();

try {
// 解析和执行命令
var parseResults = dispatcher.parse(command, source);
dispatcher.execute(parseResults);
} catch (Exception e) {
Text text = Text.literal(getTranslation("carpet.commandWentWrong"))
.setStyle(Style.EMPTY.withColor(TextColor.fromFormatting(Formatting.RED))).append(Text.literal(" /" + command + ": " + e.getMessage())
.setStyle(Style.EMPTY.withColor(TextColor.fromFormatting(Formatting.RED)).withUnderline(true)));
player.sendMessage(text, false);
CMALogger.debug("Failed to execute command: " + command + e.getMessage());
}
}

public static String getTranslation(String key) {
Map<String, String> lang = CMATranslations.getTranslationFromResourcePath(CarpetSettings.language);
String translationText = lang.get(key);
CMALogger.debug("Translation with key" + key + ": " + translationText);
return lang.get(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package tech.mctown.cma.mixins.runCommandOnSign;

import net.minecraft.block.BlockState;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.block.entity.SignText;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.server.command.CommandOutput;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import tech.mctown.cma.CMALogger;
import tech.mctown.cma.CMASettings;
import tech.mctown.cma.CMAUtils;

import java.util.Arrays;
import java.util.Objects;

@Mixin(net.minecraft.block.AbstractSignBlock.class) // 向告示牌注入代码
public class AbstractSignBlockMixin {
@Inject(method = "onUse", at = @At("HEAD"), cancellable = true)
public void cancelOpenGUI(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit, CallbackInfoReturnable<ActionResult> cir) {
if (CMASettings.runCommandOnSign) {
if (player instanceof ServerPlayerEntity) {
if (world.getBlockEntity(pos) instanceof SignBlockEntity signBlockEntity) {
if (player.getMainHandStack().isOf(Items.AIR) && player.isSneaking()) {
CMALogger.debug("AbstractSignBlockMixin cancelOpenGUI");
boolean isFront = signBlockEntity.isPlayerFacingFront(player);
SignText texts = signBlockEntity.getText(isFront);
Text[] text = texts.getMessages(false);
CMALogger.debug(Arrays.toString(text));
// 若告示牌上的文本以 / 开头,则执行命令
StringBuilder command = new StringBuilder(text[0].getString());
if (command.toString().startsWith("/")) {
for (int i = 1; i < 4; i++) {
if (Objects.equals(text[i].getString(), "")) {
continue;
}
command.append(text[i].getString());
}
CMALogger.debug("Command: " + command);
CMAUtils.executeCommand((ServerPlayerEntity) player, command.toString());
// Objects.requireNonNull(player.getServer()).getCommandManager().executeWithPrefix(createCommandSource(player, world, pos), command.toString());
}
cir.setReturnValue(ActionResult.SUCCESS);
}
}
}
}
}

@Unique
private ServerCommandSource createCommandSource(PlayerEntity player, World world, BlockPos pos) {
String string = player == null ? "Sign" : player.getName().getString();
Text text = (Text) (player == null ? Text.literal("Sign") : player.getDisplayName());
return new ServerCommandSource(CommandOutput.DUMMY, Vec3d.ofCenter(pos), Vec2f.ZERO, (ServerWorld) world, 2, string, text, world.getServer(), player);
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,63 @@
package tech.mctown.cma.mixins.runCommandOnSign;

import net.minecraft.block.AbstractSignBlock;
import net.minecraft.block.SignBlock;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.block.entity.SignText;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.packet.s2c.play.GameMessageS2CPacket;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.filter.FilteredMessage;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import tech.mctown.cma.CMALogger;
import tech.mctown.cma.CMASettings;
import tech.mctown.cma.CMAUtils;

@Mixin(SignBlockEntity.class)
import javax.swing.text.Position;
import java.awt.*;
import java.util.List;


@Mixin(SignBlockEntity.class) // 向告示牌实体注入代码
public abstract class SignBlockEntityMixin {
//
//
// 如果告示牌上的文本以 / 开头,且玩家手持物品为空,且玩家未潜行,则执行命令
// 倘若不满足,则执行原版告示牌的编辑功能
@Inject(method = "tryChangeText", at = @At("HEAD"), cancellable = true)
public void PreventChangeTextWhenEmptyHands(PlayerEntity player, boolean front, List<FilteredMessage> messages, CallbackInfo ci) {
if (CMASettings.runCommandOnSign) {
if (player instanceof ServerPlayerEntity) {
CMALogger.debug("Player is trying to change the text, checking sign text");
ServerWorld world = (ServerWorld) player.getEntityWorld();
BlockPos pos = ((SignBlockEntity) (Object) this).getPos();
if (world.getBlockEntity(pos) instanceof SignBlockEntity signBlockEntity) {
boolean isFront = signBlockEntity.isPlayerFacingFront(player);
SignText texts = signBlockEntity.getText(isFront);
Text[] text = texts.getMessages(false);
CMALogger.debug("Sign text: " + text[0].getString());
if (text[0].getString().startsWith("/")) {
CMALogger.debug("Player is trying to change the text, but the text starts with /");
Text message = Text.literal(CMAUtils.getTranslation("carpet.runCommandOnSignTips"));
player.sendMessage(message, false);
}
}
}
}
}
}
// @Shadow

// protected abstract Text[] getTexts(boolean filtered);
//
// @Inject(method = "onActivate", at = @At("HEAD"))
Expand All @@ -36,4 +77,3 @@ public abstract class SignBlockEntityMixin {
// }
// }
// }
}
4 changes: 3 additions & 1 deletion src/main/resources/assets/cma/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
"carpet.rule.flintAndSteelActivatesObserver.desc": "Observer will be activated when player uses Flint and Steel on it.",
"carpet.rule.commandDumpEntity.desc": "Enable the usage of /dumpentity command.",
"carpet.rule.fakePlayerGamemode.desc": "Op players can spawn bots in different gamemodes.",
"carpet.rule.fakePlayerColor.desc": "Fake players will have different name colors based on their gamemodes."
"carpet.rule.fakePlayerColor.desc": "Fake players will have different name colors based on their gamemodes.",
"carpet.commandWentWrong": "Command failed, error message as follows:",
"carpet.runCommandOnSignTips": "Tips: If you want to run a command on the sign, right-click it while sneaking and don't holding anything in the main hand."
}
5 changes: 4 additions & 1 deletion src/main/resources/assets/cma/lang/zh_cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
"carpet.rule.runCommandOnSign.extra": "命令必须以'/'开头",
"carpet.rule.flintAndSteelActivatesObserver.desc": "打火石可以激活侦测器",
"carpet.rule.commandDumpEntity.desc": "开启dumpentity命令,打印所有的实体信息",
"carpet.rule.fakePlayerGamemode.desc": "OP可以生成不同游戏模式的假人"
"carpet.rule.fakePlayerGamemode.desc": "OP可以生成不同游戏模式的假人",
"carpet.commandWentWrong": "指令出现错误,错误信息如下:",
"carpet.runCommandOnSignTips": "提示:如果你想运行告示牌上的指令,请空手潜行右键点击告示牌",
"carpet.rule.fakePlayerColor.desc": "Fake players will have different name colors based on their gamemodes."
}
5 changes: 3 additions & 2 deletions src/main/resources/cma.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"package": "tech.mctown.cma.mixins",
"compatibilityLevel": "JAVA_21",
"mixins": [
"fakePlayerGamemode.PlayerCommandMixin",
"flintAndSteelActivatesObserver.FlintAndSteelItemMixin",
"runCommandOnSign.SignBlockEntityMixin",
"fakePlayerGamemode.PlayerCommandMixin"
"runCommandOnSign.AbstractSignBlockMixin",
"runCommandOnSign.SignBlockEntityMixin"
],
"server": [],
"client": [],
Expand Down

0 comments on commit dba1093

Please sign in to comment.