Skip to content

Commit

Permalink
Merge pull request #6 from Apricityx/1.21.1
Browse files Browse the repository at this point in the history
new rule: run command on sign.
  • Loading branch information
Apricityx authored Dec 6, 2024
2 parents 1788786 + dba1093 commit dea4514
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 11 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: build
on: [pull_request, push]

jobs:
build:
strategy:
matrix:
# Use these Java versions
java: [
21, # Current Java LTS
]
runs-on: ubuntu-22.04
steps:
- name: checkout repository
uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v2
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'microsoft'
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ matrix.java == '21' }} # Only upload artifacts built from latest java
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: build/libs/
28 changes: 28 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
on:
release:
types: [published]

name: Create Release

jobs:
release:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup java
uses: actions/setup-java@v1
with:
java-version: 21
- name: Build project
run: |
chmod +x ./gradlew
./gradlew build
- name: Publish mod
uses: Kir-Antipov/mc-publish@v2.1
with:
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}

loader: fabric
21 changes: 21 additions & 0 deletions src/main/java/tech/mctown/cma/CMAEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@

import carpet.CarpetServer;
import carpet.api.settings.SettingsManager;
import com.mojang.brigadier.Command;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.Language;
import tech.mctown.cma.CMALogger;

import java.util.Objects;

import static net.minecraft.server.command.CommandManager.*;

public class CMAEntry implements ModInitializer {
@Override
public void onInitialize() {
CarpetServer.manageExtension(CMAExtension.INSTANCE);
CMALogger.toConsole("CMA Loaded");
// Practice
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> dispatcher.register(literal("foo")
.executes(context -> {
context.getSource().sendFeedback(() -> Text.literal("调用 /foo,不带参数"), false);
CMALogger.toGame("123123", Objects.requireNonNull(context.getSource().getPlayer()));
return 1;
})));
}
}
38 changes: 38 additions & 0 deletions src/main/java/tech/mctown/cma/CMALogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package tech.mctown.cma;

import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;


public class CMALogger {
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);
}

public static void toGame(String message, MinecraftServer server) {
Text textMessage = Text.of(message);
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
player.sendMessage(textMessage, false);
}
}

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);
}
}
12 changes: 12 additions & 0 deletions src/main/java/tech/mctown/cma/mixins/Test/TestMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package tech.mctown.cma.mixins.Test;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;

@Mixin(TestMixin.class)
public abstract class TestMixin {
// @Inject(method = "testMixin", at = @At("HEAD"), cancellable = true)
// private void testMixin(){

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

@Mixin(PlayerCommand.class)
public abstract class PlayerCommandMixin {
@Inject(method = "cantSpawn", at = @At("HEAD"), remap = false)
@Inject(method = "cantSpawn", at = @At("HEAD"), remap = false, cancellable = true)
static private void checkGamemode(CommandContext<ServerCommandSource> context, CallbackInfoReturnable<Boolean> ci) {
try {
StringArgumentType.getString(context, "gamemode");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,40 @@
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FlintAndSteelItem;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
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 java.lang.reflect.Method;

@Mixin(FlintAndSteelItem.class)
public abstract class FlintAndSteelItemMixin {
@Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true)
@Inject(method = "useOnBlock", at = @At("TAIL"), cancellable = true)
// At指定注入代码插入的位置
void activatesObserver(ItemUsageContext context, CallbackInfoReturnable<ActionResult> ci) {
if (CMASettings.flintAndSteelActivatesObserver) {
PlayerEntity playerEntity = context.getPlayer();
if (playerEntity == null) return;
World world = context.getWorld();
BlockPos blockPos = context.getBlockPos();
BlockState blockState = world.getBlockState(blockPos);

if (blockState.isOf(Blocks.OBSERVER) && !playerEntity.isSneaking()) {
ObserverBlock observer = (ObserverBlock) blockState.getBlock();
try {
Method scheduleTickMethod = ObserverBlock.class.getDeclaredMethod("scheduleTick", World.class, BlockPos.class);
Method scheduleTickMethod = ObserverBlock.class.getDeclaredMethod("scheduledTick", BlockState.class, ServerWorld.class, BlockPos.class, Random.class);
scheduleTickMethod.setAccessible(true); // 设置为可访问
scheduleTickMethod.invoke(observer, world, blockPos);
ServerWorld serverWorld = (ServerWorld) world;
scheduleTickMethod.invoke(observer, blockState, serverWorld, blockPos, null);
} catch (Exception e) {
e.printStackTrace(); // 处理异常,打印堆栈
CMALogger.toConsole("Failed to activate observer");
}
ci.setReturnValue(ActionResult.success(world.isClient()));
}
Expand Down
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,20 +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 @@ -34,4 +77,3 @@ public abstract class SignBlockEntityMixin {
// }
// }
// }
}
Loading

0 comments on commit dea4514

Please sign in to comment.