From 01944bd5b4debea7d7731c85a57f50ff88753808 Mon Sep 17 00:00:00 2001 From: Disy Date: Sun, 6 Aug 2023 23:24:25 +0800 Subject: [PATCH] feat: add bot command --- build.gradle | 18 + gradle.properties | 4 +- .../carpetslsaddition/PlayerAccess.java | 4 + .../zly2006/carpetslsaddition/ServerMain.java | 74 +--- .../carpetslsaddition/command/BotCommand.java | 319 ++++++++++++++++++ .../carpetslsaddition/command/HatCommand.java | 30 ++ .../carpetslsaddition/command/SitCommand.java | 37 ++ .../mixin/entity/MixinFallingBlockEntity.java | 2 - .../mixin/entity/MixinPlayerEntity.java | 17 + .../MixinChainRestrictedNeighborUpdater.java | 24 +- src/main/resources/slsa.accesswidener | 1 + src/main/resources/slsaddition.mixins.json | 6 +- 12 files changed, 460 insertions(+), 76 deletions(-) create mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java create mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/command/HatCommand.java create mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/command/SitCommand.java diff --git a/build.gradle b/build.gradle index 2c39f09..4ab4dfc 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,19 @@ repositories { maven { url 'https://www.cursemaven.com' } + maven { + url = "https://maven.fabricmc.net/" + } + maven { + url = "https://oss.sonatype.org/content/repositories/snapshots" + } + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + } + maven { + url "https://maven.shedaniel.me" + } } dependencies { @@ -27,10 +40,15 @@ dependencies { minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + // luck perm + modRuntimeOnly "maven.modrinth:luckperms:v5.4.83-fabric" // carpet modImplementation "carpet:fabric-carpet:${project.carpet_core_version}" + // Fabric Permission API + include(modImplementation('me.lucko:fabric-permissions-api:0.2-SNAPSHOT')) + // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" } diff --git a/gradle.properties b/gradle.properties index d35ef97..2935ecf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,10 +9,10 @@ org.gradle.jvmargs=-Xmx1G # Mod Properties - mod_version = 0.6.0 + mod_version = 0.6.1 maven_group = com.github.zly2006 archives_base_name = carpet-sls-addition # Dependencies fabric_version=0.83.1+1.20.1 - carpet_core_version=1.20-pre6-1.4.111+v230525 + carpet_core_version=1.20-1.4.112+v230608 diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/PlayerAccess.java b/src/main/java/com/github/zly2006/carpetslsaddition/PlayerAccess.java index 4bdcd1d..cc5ec96 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/PlayerAccess.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/PlayerAccess.java @@ -1,5 +1,9 @@ package com.github.zly2006.carpetslsaddition; +import net.minecraft.text.Text; + public interface PlayerAccess { boolean holdingObsidianPickaxe(); + + void setDisplayName(Text name); } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java index 8256c18..91c8ff7 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java @@ -2,8 +2,10 @@ package com.github.zly2006.carpetslsaddition; import carpet.CarpetExtension; import carpet.CarpetServer; +import com.github.zly2006.carpetslsaddition.command.BotCommand; +import com.github.zly2006.carpetslsaddition.command.HatCommand; +import com.github.zly2006.carpetslsaddition.command.SitCommand; import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; -import com.github.zly2006.carpetslsaddition.util.SitEntity; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -13,28 +15,13 @@ import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.Version; import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.entity.EquipmentSlot; -import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.recipe.Ingredient; -import net.minecraft.recipe.ShapedRecipe; -import net.minecraft.recipe.book.CraftingRecipeCategory; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; -import net.minecraft.util.collection.DefaultedList; -import net.minecraft.world.World; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.io.InputStream; -import java.util.List; import java.util.Map; public class ServerMain implements ModInitializer, CarpetExtension { @@ -58,6 +45,7 @@ public class ServerMain implements ModInitializer, CarpetExtension { public void onInitialize() { INSTANCE = this; CarpetServer.manageExtension(this); + PcaSyncProtocol.init(); } @Override @@ -65,24 +53,12 @@ public class ServerMain implements ModInitializer, CarpetExtension { ServerMain.server = server; CarpetServer.settingsManager.parseSettingsClass(SLSCarpetSettings.class); ((SettingsManagerAccessor) CarpetServer.settingsManager).loadSettings(); - if (false) { - ItemStack stack = new ItemStack(Items.STONE_PICKAXE); - stack.setCustomName(Text.empty().formatted(Formatting.LIGHT_PURPLE).append("黑曜石镐")); - stack.getOrCreateSubNbt(MOD_ID).putString(ITEM_NAME, OBSIDIAN_PICKAXE); - DefaultedList ingredients = DefaultedList.ofSize(9, Ingredient.EMPTY); - ingredients.set(0, Ingredient.ofItems(Items.OBSIDIAN)); - ingredients.set(1, Ingredient.ofItems(Items.OBSIDIAN)); - ingredients.set(2, Ingredient.ofItems(Items.OBSIDIAN)); - ingredients.set(4, Ingredient.ofItems(Items.STICK)); - ingredients.set(7, Ingredient.ofItems(Items.STICK)); - server.getRecipeManager().setRecipes(List.of(new ShapedRecipe(new Identifier(MOD_ID, "recipe.obsidian_pickaxe"), MOD_ID, CraftingRecipeCategory.EQUIPMENT, 3, 3, ingredients, stack))); - } } @Override public Map canHasTranslations(String lang) { Map translation = Maps.newHashMap(); - PcaSyncProtocol.init(); + String jsonFile; try { try (InputStream stream = ServerMain.class.getResourceAsStream("/assets/slsaddition/lang/%s.json".formatted(lang))) { @@ -109,42 +85,8 @@ public class ServerMain implements ModInitializer, CarpetExtension { @Override public void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess commandBuildContext) { - dispatcher.register(CommandManager.literal("hat") - .requires(ServerCommandSource::isExecutedByPlayer) - .requires((commandSource) -> SLSCarpetSettings.canUseHatCommand) - .executes(context -> { - ServerPlayerEntity player = context.getSource().getPlayer(); - assert player != null; - ItemStack stack = player.getMainHandStack(); - ItemStack head = player.getEquippedStack(EquipmentSlot.HEAD); - player.equipStack(EquipmentSlot.HEAD, stack); - player.getInventory().setStack(player.getInventory().selectedSlot, head); - player.currentScreenHandler.syncState(); - - return 1; - })); - - dispatcher.register(CommandManager.literal("sit") - .requires(ServerCommandSource::isExecutedByPlayer) - .requires((commandSource) -> SLSCarpetSettings.canUseSitCommand) - .executes(context -> { - ServerPlayerEntity player = context.getSource().getPlayer(); - assert player != null; - - if (player.getVehicle() != null || !player.isOnGround()) { // 防止错误的坐下行为 - return 1; - } - - World world = player.getWorld(); - - ArmorStandEntity armorStandEntity = new ArmorStandEntity(world, player.getX(), player.getY(), player.getZ()); - ((SitEntity) armorStandEntity).setSitEntity(true); - world.spawnEntity(armorStandEntity); - player.setSneaking(false); - player.startRiding(armorStandEntity); - - return 1; - }) - ); + HatCommand.register(dispatcher); + SitCommand.register(dispatcher); + BotCommand.register(dispatcher); } } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java b/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java new file mode 100644 index 0000000..58587a4 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java @@ -0,0 +1,319 @@ +package com.github.zly2006.carpetslsaddition.command; + +import carpet.CarpetSettings; +import carpet.fakes.ServerPlayerInterface; +import carpet.helpers.EntityPlayerActionPack; +import carpet.patches.EntityPlayerMPFake; +import carpet.utils.Messenger; +import com.github.zly2006.carpetslsaddition.PlayerAccess; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.mojang.authlib.GameProfile; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import me.lucko.fabric.api.permissions.v0.Permissions; +import net.minecraft.command.argument.DimensionArgumentType; +import net.minecraft.command.argument.GameModeArgumentType; +import net.minecraft.command.argument.RotationArgumentType; +import net.minecraft.command.argument.Vec3ArgumentType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; +import net.minecraft.registry.RegistryKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PlayerManager; +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.util.Formatting; +import net.minecraft.util.Uuids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec2f; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.GameMode; +import net.minecraft.world.World; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import static net.minecraft.command.CommandSource.suggestMatching; +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +public class BotCommand { + public static void register(CommandDispatcher dispatcher) + { + LiteralArgumentBuilder command = literal("bot") + .requires(source -> source.hasPermissionLevel(4) || Permissions.check(source, "slsaddition.command.bot")) + .then(argument("player", StringArgumentType.word()) + .suggests((c, b) -> suggestMatching(getPlayerSuggestions(c.getSource()), b)) + .then(literal("stop").executes(manipulation(EntityPlayerActionPack::stopAll))) + .then(makeActionCommand("use", EntityPlayerActionPack.ActionType.USE)) + .then(makeActionCommand("jump", EntityPlayerActionPack.ActionType.JUMP)) + .then(makeActionCommand("attack", EntityPlayerActionPack.ActionType.ATTACK)) + .then(makeActionCommand("drop", EntityPlayerActionPack.ActionType.DROP_ITEM)) + .then(makeDropCommand("drop", false)) + .then(makeActionCommand("dropStack", EntityPlayerActionPack.ActionType.DROP_STACK)) + .then(makeDropCommand("dropStack", true)) + .then(makeActionCommand("swapHands", EntityPlayerActionPack.ActionType.SWAP_HANDS)) + .then(literal("hotbar") + .then(argument("slot", IntegerArgumentType.integer(1, 9)) + .executes(c -> manipulate(c, ap -> ap.setSlot(IntegerArgumentType.getInteger(c, "slot")))))) + .then(literal("kill").executes(BotCommand::kill)) + .then(literal("mount").executes(manipulation(ap -> ap.mount(true))) + .then(literal("anything").executes(manipulation(ap -> ap.mount(false))))) + .then(literal("dismount").executes(manipulation(EntityPlayerActionPack::dismount))) + .then(literal("sneak").executes(manipulation(ap -> ap.setSneaking(true)))) + .then(literal("unsneak").executes(manipulation(ap -> ap.setSneaking(false)))) + .then(literal("sprint").executes(manipulation(ap -> ap.setSprinting(true)))) + .then(literal("unsprint").executes(manipulation(ap -> ap.setSprinting(false)))) + .then(literal("look") + .then(literal("north").executes(manipulation(ap -> ap.look(Direction.NORTH)))) + .then(literal("south").executes(manipulation(ap -> ap.look(Direction.SOUTH)))) + .then(literal("east").executes(manipulation(ap -> ap.look(Direction.EAST)))) + .then(literal("west").executes(manipulation(ap -> ap.look(Direction.WEST)))) + .then(literal("up").executes(manipulation(ap -> ap.look(Direction.UP)))) + .then(literal("down").executes(manipulation(ap -> ap.look(Direction.DOWN)))) + .then(literal("at").then(argument("position", Vec3ArgumentType.vec3()) + .executes(c -> manipulate(c, ap -> ap.lookAt(Vec3ArgumentType.getVec3(c, "position")))))) + .then(argument("direction", RotationArgumentType.rotation()) + .executes(c -> manipulate(c, ap -> ap.look(RotationArgumentType.getRotation(c, "direction").toAbsoluteRotation(c.getSource()))))) + ).then(literal("turn") + .then(literal("left").executes(manipulation(ap -> ap.turn(-90, 0)))) + .then(literal("right").executes(manipulation(ap -> ap.turn(90, 0)))) + .then(literal("back").executes(manipulation(ap -> ap.turn(180, 0)))) + .then(argument("rotation", RotationArgumentType.rotation()) + .executes(c -> manipulate(c, ap -> ap.turn(RotationArgumentType.getRotation(c, "rotation").toAbsoluteRotation(c.getSource()))))) + ).then(literal("move").executes(manipulation(EntityPlayerActionPack::stopMovement)) + .then(literal("forward").executes(manipulation(ap -> ap.setForward(1)))) + .then(literal("backward").executes(manipulation(ap -> ap.setForward(-1)))) + .then(literal("left").executes(manipulation(ap -> ap.setStrafing(1)))) + .then(literal("right").executes(manipulation(ap -> ap.setStrafing(-1)))) + ).then(literal("spawn").executes(BotCommand::spawn) + .then(literal("in").requires((player) -> player.hasPermissionLevel(2)) + .then(argument("gamemode", GameModeArgumentType.gameMode()) + .executes(BotCommand::spawn))) + .then(literal("at").then(argument("position", Vec3ArgumentType.vec3()).executes(BotCommand::spawn) + .then(literal("facing").then(argument("direction", RotationArgumentType.rotation()).executes(BotCommand::spawn) + .then(literal("in").then(argument("dimension", DimensionArgumentType.dimension()).executes(BotCommand::spawn) + .then(literal("in").requires((player) -> player.hasPermissionLevel(2)) + .then(argument("gamemode", GameModeArgumentType.gameMode()) + .executes(BotCommand::spawn) + ))) + ))) + )) + ) + ); + dispatcher.register(command); + } + + private static LiteralArgumentBuilder makeActionCommand(String actionName, EntityPlayerActionPack.ActionType type) + { + return literal(actionName) + .executes(manipulation(ap -> ap.start(type, EntityPlayerActionPack.Action.once()))) + .then(literal("once").executes(manipulation(ap -> ap.start(type, EntityPlayerActionPack.Action.once())))) + .then(literal("continuous").executes(manipulation(ap -> ap.start(type, EntityPlayerActionPack.Action.continuous())))) + .then(literal("interval").then(argument("ticks", IntegerArgumentType.integer(1)) + .executes(c -> manipulate(c, ap -> ap.start(type, EntityPlayerActionPack.Action.interval(IntegerArgumentType.getInteger(c, "ticks"))))))); + } + + private static LiteralArgumentBuilder makeDropCommand(String actionName, boolean dropAll) + { + return literal(actionName) + .then(literal("all").executes(manipulation(ap -> ap.drop(-2, dropAll)))) + .then(literal("mainhand").executes(manipulation(ap -> ap.drop(-1, dropAll)))) + .then(literal("offhand").executes(manipulation(ap -> ap.drop(40, dropAll)))) + .then(argument("slot", IntegerArgumentType.integer(0, 40)). + executes(c -> manipulate(c, ap -> ap.drop(IntegerArgumentType.getInteger(c, "slot"), dropAll)))); + } + + private static Collection getPlayerSuggestions(ServerCommandSource source) + { + Set players = new LinkedHashSet<>(List.of("Steve", "Alex")); + players.addAll(source.getPlayerNames()); + return players; + } + + private static ServerPlayerEntity getPlayer(CommandContext context) + { + String playerName = StringArgumentType.getString(context, "player"); + MinecraftServer server = context.getSource().getServer(); + return server.getPlayerManager().getPlayer(playerName); + } + + private static boolean cantManipulate(CommandContext context) + { + PlayerEntity player = getPlayer(context); + ServerCommandSource source = context.getSource(); + if (player == null) + { + Messenger.m(source, "r Can only manipulate existing players"); + return true; + } + PlayerEntity sender = source.getPlayer(); + if (sender == null) + { + return false; + } + + if (!source.getServer().getPlayerManager().isOperator(sender.getGameProfile())) + { + if (sender != player && !(player instanceof EntityPlayerMPFake)) + { + Messenger.m(source, "r Non OP players can't control other real players"); + return true; + } + } + return false; + } + + private static boolean cantReMove(CommandContext context) + { + if (cantManipulate(context)) return true; + PlayerEntity player = getPlayer(context); + if (player instanceof EntityPlayerMPFake) return false; + Messenger.m(context.getSource(), "r Only fake players can be moved or killed"); + return true; + } + + private static boolean cantSpawn(CommandContext context) + { + String playerName = StringArgumentType.getString(context, "player"); + MinecraftServer server = context.getSource().getServer(); + PlayerManager manager = server.getPlayerManager(); + + if (manager.getPlayer(playerName) != null) + { + Messenger.m(context.getSource(), "r Player ", "rb " + playerName, "r is already logged on"); + return true; + } + GameProfile profile = server.getUserCache().findByName(playerName).orElse(null); + if (profile == null) + { + if (!CarpetSettings.allowSpawningOfflinePlayers) + { + Messenger.m(context.getSource(), "r Player "+playerName+" is either banned by Mojang, or auth servers are down. " + + "Banned players can only be summoned in Singleplayer and in servers in off-line mode."); + return true; + } else { + profile = new GameProfile(Uuids.getOfflinePlayerUuid(playerName), playerName); + } + } + if (manager.getUserBanList().contains(profile)) + { + Messenger.m(context.getSource(), "r Player ", "rb " + playerName, "r is banned on this server"); + return true; + } + if (manager.isWhitelistEnabled() && manager.isWhitelisted(profile) && !context.getSource().hasPermissionLevel(2)) + { + Messenger.m(context.getSource(), "r Whitelisted players can only be spawned by operators"); + return true; + } + return false; + } + + private static int kill(CommandContext context) + { + if (cantReMove(context)) return 0; + getPlayer(context).kill(); + return 1; + } + + @FunctionalInterface + interface SupplierWithCSE + { + T get() throws CommandSyntaxException; + } + + private static T getArgOrDefault(BotCommand.SupplierWithCSE getter, T defaultValue) throws CommandSyntaxException + { + try + { + return getter.get(); + } + catch (IllegalArgumentException e) + { + return defaultValue; + } + } + + private static int spawn(CommandContext context) throws CommandSyntaxException + { + if (cantSpawn(context)) return 0; + + ServerCommandSource source = context.getSource(); + Vec3d pos = getArgOrDefault( + () -> Vec3ArgumentType.getVec3(context, "position"), + source.getPosition() + ); + Vec2f facing = getArgOrDefault( + () -> RotationArgumentType.getRotation(context, "direction").toAbsoluteRotation(source), + source.getRotation() + ); + RegistryKey dimType = getArgOrDefault( + () -> DimensionArgumentType.getDimensionArgument(context, "dimension").getRegistryKey(), + source.getWorld().getRegistryKey() + ); + + String playerName = "bot_" + StringArgumentType.getString(context, "player"); + if (playerName.length() > maxNameLength(source.getServer())) + { + Messenger.m(source, "rb Player name: " + playerName + " is too long"); + return 0; + } + + if (!World.isValid(BlockPos.ofFloored(pos))) + { + Messenger.m(source, "rb Player " + playerName + " cannot be placed outside of the world"); + return 0; + } + ServerPlayerEntity player = EntityPlayerMPFake.createFake(playerName, source.getServer(), pos, facing.y, facing.x, dimType, GameMode.SURVIVAL, false); + if (player == null) + { + Messenger.m(source, "rb Player " + playerName + " doesn't exist and cannot spawn in online mode. " + + "Turn the server offline to spawn non-existing players"); + return 0; + } + else { + ((PlayerAccess) player).setDisplayName(Text.empty().append(Text.literal("[%s] ".formatted(source.getName())).setStyle(Style.EMPTY.withColor(Formatting.GRAY))).append(Text.literal(playerName).setStyle(Style.EMPTY))); + ServerMain.server.getPlayerManager().broadcast(Text.empty() + .append(Text.literal("假人").setStyle(Style.EMPTY.withColor(Formatting.GREEN))) + .append(Text.literal(playerName).setStyle(Style.EMPTY.withColor(Formatting.GOLD).withBold(true))) + .append(Text.literal("由玩家").setStyle(Style.EMPTY.withColor(Formatting.GREEN))) + .append(source.getDisplayName()) + .append(Text.literal("召唤!").setStyle(Style.EMPTY.withColor(Formatting.GREEN))), false); + + ServerMain.server.getPlayerManager().sendToAll(new PlayerListS2CPacket( + PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, + player + )); + } + return 1; + } + + private static int maxNameLength(MinecraftServer server) + { + return server.getServerPort() >= 0 ? PlayerEntity.field_30643 : 40; + } + + private static int manipulate(CommandContext context, Consumer action) + { + if (cantManipulate(context)) return 0; + ServerPlayerEntity player = getPlayer(context); + action.accept(((ServerPlayerInterface) player).getActionPack()); + return 1; + } + + private static Command manipulation(Consumer action) + { + return c -> manipulate(c, action); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/command/HatCommand.java b/src/main/java/com/github/zly2006/carpetslsaddition/command/HatCommand.java new file mode 100644 index 0000000..909ef1e --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/command/HatCommand.java @@ -0,0 +1,30 @@ +package com.github.zly2006.carpetslsaddition.command; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.ItemStack; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; + +public class HatCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(CommandManager.literal("hat") + .requires(ServerCommandSource::isExecutedByPlayer) + .requires((commandSource) -> SLSCarpetSettings.canUseHatCommand) + .executes(context -> { + ServerPlayerEntity player = context.getSource().getPlayer(); + assert player != null; + ItemStack stack = player.getMainHandStack(); + ItemStack head = player.getEquippedStack(EquipmentSlot.HEAD); + player.equipStack(EquipmentSlot.HEAD, stack); + player.getInventory().setStack(player.getInventory().selectedSlot, head); + player.currentScreenHandler.syncState(); + + return 1; + } + ) + ); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/command/SitCommand.java b/src/main/java/com/github/zly2006/carpetslsaddition/command/SitCommand.java new file mode 100644 index 0000000..b003ddd --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/command/SitCommand.java @@ -0,0 +1,37 @@ +package com.github.zly2006.carpetslsaddition.command; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.util.SitEntity; +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.world.World; + +public class SitCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(CommandManager.literal("sit") + .requires(ServerCommandSource::isExecutedByPlayer) + .requires((commandSource) -> SLSCarpetSettings.canUseSitCommand) + .executes(context -> { + ServerPlayerEntity player = context.getSource().getPlayer(); + assert player != null; + + if (player.getVehicle() != null || !player.isOnGround()) { // 防止错误的坐下行为 + return 1; + } + + World world = player.getWorld(); + + ArmorStandEntity armorStandEntity = new ArmorStandEntity(world, player.getX(), player.getY(), player.getZ()); + ((SitEntity) armorStandEntity).setSitEntity(true); + world.spawnEntity(armorStandEntity); + player.setSneaking(false); + player.startRiding(armorStandEntity); + + return 1; + }) + ); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java index 06dd2de..db7bfe4 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java @@ -6,10 +6,8 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.FallingBlockEntity; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.damage.DamageSource; -import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.world.World; 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; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java index 1effdbb..e50b235 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java @@ -8,6 +8,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.stat.Stat; +import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -22,6 +23,10 @@ public abstract class MixinPlayerEntity implements PlayerAccess { @Shadow public abstract void resetStat(Stat stat); + @Shadow public abstract Text getName(); + + private Text displayName = null; + @Override public boolean holdingObsidianPickaxe() { ItemStack itemStack = getEquippedStack(EquipmentSlot.MAINHAND); @@ -29,6 +34,18 @@ public abstract class MixinPlayerEntity implements PlayerAccess { return compound != null && compound.contains(ITEM_NAME) && compound.getString(ITEM_NAME).equals(OBSIDIAN_PICKAXE); } + @Override + public void setDisplayName(Text name) { + this.displayName = name; + } + + @Inject(method = "getDisplayName", at = @At("HEAD"), cancellable = true) + public void injectGetDisplayName(CallbackInfoReturnable cir) { + if (this.displayName != null) { + cir.setReturnValue(displayName); + } + } + @Inject(method = "getBlockBreakingSpeed", at = @At("HEAD"), cancellable = true) private void onGetBlockBreakingSpeed(BlockState block, CallbackInfoReturnable cir) { diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinChainRestrictedNeighborUpdater.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinChainRestrictedNeighborUpdater.java index d6e1689..4c14094 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinChainRestrictedNeighborUpdater.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinChainRestrictedNeighborUpdater.java @@ -1,6 +1,7 @@ package com.github.zly2006.carpetslsaddition.mixin.world; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.block.ChainRestrictedNeighborUpdater; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -8,8 +9,10 @@ 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.LocalCapture; import java.util.ArrayDeque; +import java.util.List; @Mixin(ChainRestrictedNeighborUpdater.class) public class MixinChainRestrictedNeighborUpdater { @@ -18,12 +21,25 @@ public class MixinChainRestrictedNeighborUpdater { @Final private ArrayDeque queue; - @Inject(method = "runQueuedUpdates", at = @At(value = "INVOKE", target = "Ljava/util/List;clear()V", ordinal = 0, shift = At.Shift.BEFORE)) - public void injectRunQueuedUpdates(CallbackInfo ci) { + @Shadow + @Final + private List pending; + + @Shadow + private int depth; + + @Inject(method = "enqueue", at = @At(value = "INVOKE", target = "Ljava/util/ArrayDeque;push(Ljava/lang/Object;)V"), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT) + public void injectEnqueue(BlockPos pos, ChainRestrictedNeighborUpdater.Entry entry, CallbackInfo ci, boolean bl) { int maxSize = SLSCarpetSettings.maxUpdateQueueSize; if (maxSize > 0) { - if (this.queue.size() > maxSize) { - throw new OutOfMemoryError("Create by Carpet-SLS-Addition: The update queue exceeded the maximum allowed allocation of heap space set by SLS Addition!"); + if (this.depth + 1 > maxSize) { + this.queue.clear(); + this.pending.clear(); + this.depth = 0; + bl = true; + Throwable e = new OutOfMemoryError("Create by Carpet-SLS-Addition: The update queue exceeded the maximum allowed allocation of heap space set by SLS Addition!"); + e.printStackTrace(); + ci.cancel(); } } } diff --git a/src/main/resources/slsa.accesswidener b/src/main/resources/slsa.accesswidener index 3fde517..f049378 100644 --- a/src/main/resources/slsa.accesswidener +++ b/src/main/resources/slsa.accesswidener @@ -1 +1,2 @@ accessWidener v1 named +accessible class net/minecraft/world/block/ChainRestrictedNeighborUpdater$Entry diff --git a/src/main/resources/slsaddition.mixins.json b/src/main/resources/slsaddition.mixins.json index 8fbe329..9762a92 100644 --- a/src/main/resources/slsaddition.mixins.json +++ b/src/main/resources/slsaddition.mixins.json @@ -4,6 +4,10 @@ "package": "com.github.zly2006.carpetslsaddition.mixin", "compatibilityLevel": "JAVA_17", "server": [ + ], + "client": [ + ], + "mixins": [ "block.MixinBlock", "carpet.MixinFakePlayer", "carpet.MixinSettingsManager", @@ -37,8 +41,6 @@ "world.MixinChainRestrictedNeighborUpdater", "world.MixinSpawnHelper" ], - "client": [ - ], "injectors": { "defaultRequire": 1 }