From 2063933634db46b81b91eecca2d44ba0d019b525 Mon Sep 17 00:00:00 2001 From: Disy Date: Sat, 22 Jun 2024 12:31:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E4=BA=86bot=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +- .../carpetslsaddition/SLSCarpetSettings.java | 3 - .../carpetslsaddition/command/BotCommand.java | 111 +++++++++++++----- .../mixin/carpet/MixinFakePlayer.java | 38 ------ .../mixin/entity/MixinPlayerEntity.java | 7 +- .../util/access/PlayerAccess.java | 4 +- .../assets/slsaddition/lang/en_us.json | 2 - .../assets/slsaddition/lang/zh_cn.json | 2 - src/main/resources/slsa.accesswidener | 4 +- src/main/resources/slsaddition.mixins.json | 1 - 10 files changed, 90 insertions(+), 86 deletions(-) delete mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java diff --git a/build.gradle b/build.gradle index fb98474..9b62e77 100644 --- a/build.gradle +++ b/build.gradle @@ -41,14 +41,14 @@ dependencies { 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" + modRuntimeOnly "maven.modrinth:luckperms:v5.4.133-fabric" // carpet modImplementation "carpet:fabric-carpet:${project.carpet_core_version}" // modImplementation(files("./lib/fabric-carpet-1.20-1.4.112+v230608.jar")) // Fabric Permission API - include(modImplementation('me.lucko:fabric-permissions-api:0.2-SNAPSHOT')) + include(modImplementation('me.lucko:fabric-permissions-api:0.3.1')) include(modImplementation('com.github.Fallen-Breath:conditional-mixin:v0.4.0')) // Fabric API. This is technically optional, but you probably want it anyway. diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java b/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java index fdfa63a..a721697 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java @@ -23,9 +23,6 @@ public class SLSCarpetSettings { @Rule(categories = {CARPET_ID, RuleCategory.OPTIMIZATION}) public static boolean noBatSpawning = false; - @Rule(categories = {CARPET_ID, RuleCategory.FEATURE}) - public static boolean offlineFakePlayers = false; - @Rule(categories = {CARPET_ID, RuleCategory.FEATURE}) public static boolean canUseHatCommand = false; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java b/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java index 298e4e4..f7233ca 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/command/BotCommand.java @@ -4,9 +4,10 @@ import carpet.CarpetSettings; import carpet.fakes.ServerPlayerInterface; import carpet.helpers.EntityPlayerActionPack; import carpet.patches.EntityPlayerMPFake; +import carpet.patches.FakeClientConnection; import carpet.utils.Messenger; -import com.github.zly2006.carpetslsaddition.util.access.PlayerAccess; import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.util.access.PlayerAccess; import com.mojang.authlib.GameProfile; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; @@ -20,17 +21,24 @@ 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.attribute.EntityAttributes; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.NetworkSide; +import net.minecraft.network.packet.c2s.common.SyncedClientOptions; +import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; +import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; import net.minecraft.registry.RegistryKey; -import net.minecraft.server.Main; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ConnectedClientData; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import net.minecraft.util.UserCache; import net.minecraft.util.Uuids; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -39,12 +47,12 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.World; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static net.minecraft.command.CommandSource.suggestMatching; @@ -52,7 +60,6 @@ import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; public class BotCommand { - public static final ArrayBlockingQueue playerQueue = new ArrayBlockingQueue<>(128); public static void register(CommandDispatcher dispatcher) { LiteralArgumentBuilder command = literal("bot") @@ -279,41 +286,89 @@ public class BotCommand { Messenger.m(source, "rb Player " + playerName + " cannot be placed outside of the world"); return 0; } - boolean success = EntityPlayerMPFake.createFake(playerName, source.getServer(), pos, facing.y, facing.x, dimType, GameMode.SURVIVAL, false); - if (!success) + EntityPlayerMPFake bot = createBot(playerName, source.getServer(), pos, facing.y, facing.x, dimType); + + if (bot == 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 { - ServerPlayerEntity player; - try { - player = playerQueue.poll(500, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - return 1; - } - if (player == null) return 1; + ((PlayerAccess) bot).carpet_SLS_Addition$setDisplayName(Text.empty().append(Text.literal("[%s] ".formatted(source.getName())).setStyle(Style.EMPTY.withColor(Formatting.AQUA))).append(Text.literal(playerName).setStyle(Style.EMPTY))); - ((PlayerAccess) player).setDisplayName(Text.empty().append(Text.literal("[%s] ".formatted(source.getName())).setStyle(Style.EMPTY.withColor(Formatting.AQUA))).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().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 - )); - } + ServerMain.server.getPlayerManager().sendToAll(new PlayerListS2CPacket( + PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, + bot + )); return 1; } + @SuppressWarnings("unchecked") + private static EntityPlayerMPFake createBot(String username, MinecraftServer server, Vec3d pos, double yaw, double pitch, RegistryKey dimensionId) { + ServerWorld worldIn = server.getWorld(dimensionId); + UserCache.setUseRemote(false); + GameProfile gameprofile; + try { + gameprofile = server.getUserCache().findByName(username).orElse(null); //findByName .orElse(null) + } + finally { + UserCache.setUseRemote(server.isDedicated() && server.isOnlineMode()); + } + if (gameprofile == null) + { + if (!CarpetSettings.allowSpawningOfflinePlayers) + { + return null; + } else { + gameprofile = new GameProfile(Uuids.getOfflinePlayerUuid(username), username); + } + } + + // 孩子不懂,用反射写着玩的,报错了记得随Carpet一起升级一下 + + try { + Class fakePlayerClass = (Class)Class.forName("carpet.patches.EntityPlayerMPFake"); + Constructor constructor = fakePlayerClass.getDeclaredConstructor( + MinecraftServer.class, + ServerWorld.class, + GameProfile.class, + SyncedClientOptions.class, + boolean.class + ); + constructor.setAccessible(true); + EntityPlayerMPFake bot = constructor.newInstance(server, worldIn, gameprofile, SyncedClientOptions.createDefault(), false); + + bot.fixStartingPosition = () -> bot.refreshPositionAndAngles(pos.x, pos.y, pos.z, (float) yaw, (float) pitch); + server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), bot, new ConnectedClientData(gameprofile, 0, bot.getClientOptions(), false)); + + bot.teleport(worldIn, pos.x, pos.y, pos.z, (float) yaw, (float) pitch); + bot.setHealth(20.0F); + bot.unsetRemoved(); + bot.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT).setBaseValue(0.6F); + bot.interactionManager.changeGameMode(GameMode.SURVIVAL); + + server.getPlayerManager().sendToDimension(new EntitySetHeadYawS2CPacket(bot, (byte) (bot.headYaw * 256 / 360)), dimensionId);//bot.dimension); + server.getPlayerManager().sendToDimension(new EntityPositionS2CPacket(bot), dimensionId); + + bot.getDataTracker().set(PlayerEntity.PLAYER_MODEL_PARTS, (byte) 0x7f); // show all model layers (incl. capes) + bot.getAbilities().flying = false; + + return bot; + } + catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("An error has occurred when trying spawn a new bot with reflection", e); + } + } + private static int maxNameLength(MinecraftServer server) { return 40; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java deleted file mode 100644 index 8e8d99f..0000000 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.zly2006.carpetslsaddition.mixin.carpet; - -import carpet.patches.EntityPlayerMPFake; -import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; -import com.github.zly2006.carpetslsaddition.command.BotCommand; -import com.mojang.authlib.GameProfile; -import net.minecraft.util.UserCache; -import net.minecraft.util.Uuids; -import net.minecraft.util.math.Vec3d; -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.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -@Mixin(value = EntityPlayerMPFake.class) -public class MixinFakePlayer { - @Redirect(method = "createFake", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/UserCache;findByName(Ljava/lang/String;)Ljava/util/Optional;")) - private static Optional onCreate(UserCache instance, String name) { - if (SLSCarpetSettings.offlineFakePlayers) { - UUID uuid = Uuids.getOfflinePlayerUuid(name); - return Optional.of(new GameProfile(uuid, name)); - } - return instance.findByName(name); - } - - @Inject(method = "lambda$createFake$1", at = @At("TAIL")) - private static void onAcceptFakePlayer(EntityPlayerMPFake instance, Vec3d pos, double yaw, double pitch, CallbackInfo ci) { - try { - BotCommand.playerQueue.offer(instance, 500, TimeUnit.MILLISECONDS); - } - catch (InterruptedException ignored) {} - } -} 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 5dcef0f..42eaa74 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 @@ -1,12 +1,9 @@ package com.github.zly2006.carpetslsaddition.mixin.entity; import com.github.zly2006.carpetslsaddition.util.access.PlayerAccess; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; import net.minecraft.entity.EquipmentSlot; 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; @@ -17,8 +14,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import static com.github.zly2006.carpetslsaddition.ServerMain.*; - @Mixin(PlayerEntity.class) public abstract class MixinPlayerEntity implements PlayerAccess { @Shadow public abstract ItemStack getEquippedStack(EquipmentSlot slot); @@ -31,7 +26,7 @@ public abstract class MixinPlayerEntity implements PlayerAccess { private Text displayName = null; @Override - public void setDisplayName(Text name) { + public void carpet_SLS_Addition$setDisplayName(Text name) { this.displayName = name; } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/access/PlayerAccess.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/access/PlayerAccess.java index 3021139..a36b3cb 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/util/access/PlayerAccess.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/util/access/PlayerAccess.java @@ -3,7 +3,5 @@ package com.github.zly2006.carpetslsaddition.util.access; import net.minecraft.text.Text; public interface PlayerAccess { - boolean holdingObsidianPickaxe(); - - void setDisplayName(Text name); + void carpet_SLS_Addition$setDisplayName(Text name); } diff --git a/src/main/resources/assets/slsaddition/lang/en_us.json b/src/main/resources/assets/slsaddition/lang/en_us.json index 7e14c38..69434fa 100644 --- a/src/main/resources/assets/slsaddition/lang/en_us.json +++ b/src/main/resources/assets/slsaddition/lang/en_us.json @@ -8,7 +8,6 @@ "carpet.rule.skipTicksForJoblessVillager.name": "Skip ticks for jobless villager", "carpet.rule.creativeNoInfinitePickup.name": "Creative mode cannot infinite pickup", "carpet.rule.noBatSpawning.name": "No bat spawning", - "carpet.rule.offlineFakePlayers.name": "Use offline mode for fake players", "carpet.rule.pcaSyncProtocol.name": "Enable PCA Sync Protocol", "carpet.rule.pcaSyncPlayerEntity.name": "Set PCA Sync Protocol's entity scope", "carpet.rule.emptyShulkerBoxStack.name": "Empty shulker boxes can be stacked", @@ -28,7 +27,6 @@ "carpet.rule.skipTicksForJoblessVillager.desc": "For jobless/nitwit villagers, after performing a set number of tick operations, skip the 1 tick operation.", "carpet.rule.creativeNoInfinitePickup.desc": "If my inventory is full, don't pick up items in creative mode.", "carpet.rule.noBatSpawning.desc": "Prevents bats from spawning.", - "carpet.rule.offlineFakePlayers.desc": "Using offline uuid and skin so that fake players wont cause extra network traffic.", "carpet.rule.pcaSyncProtocol.desc": "Using PCA protocol to support sync entity and blockEntity from server", "carpet.rule.pcaSyncPlayerEntity.desc": "Which player entity can be sync", "carpet.rule.emptyShulkerBoxStack.desc": "Empty shulker boxes stack", diff --git a/src/main/resources/assets/slsaddition/lang/zh_cn.json b/src/main/resources/assets/slsaddition/lang/zh_cn.json index 5a4dd65..85987b4 100644 --- a/src/main/resources/assets/slsaddition/lang/zh_cn.json +++ b/src/main/resources/assets/slsaddition/lang/zh_cn.json @@ -8,7 +8,6 @@ "carpet.rule.skipTicksForJoblessVillager.name": "无业村民跳刻", "carpet.rule.creativeNoInfinitePickup.name": "创造模式满背包无法拾取", "carpet.rule.noBatSpawning.name": "禁止蝙蝠生成", - "carpet.rule.offlineFakePlayers.name": "假人离线模式", "carpet.rule.pcaSyncProtocol.name": "启动兼容PCA的同步协议", "carpet.rule.pcaSyncPlayerEntity.name": "PCA同步协议作用范围", "carpet.rule.emptyShulkerBoxStack.name": "空潜影盒背包可堆叠", @@ -28,7 +27,6 @@ "carpet.rule.skipTicksForJoblessVillager.desc": "对于无业/傻子村民,执行设定数量的tick运算后,跳过1tick的运算", "carpet.rule.creativeNoInfinitePickup.desc": "创造模式下,玩家不能无限制地拾取物品", "carpet.rule.noBatSpawning.desc": "禁止任何区域蝙蝠生成", - "carpet.rule.offlineFakePlayers.desc": "由Carpet生成的假玩家,将使用离线皮肤和UUID", "carpet.rule.pcaSyncProtocol.desc": "借助PCA同步协议,支持同步 BlockEntity 和 Entity 到客户端", "carpet.rule.pcaSyncPlayerEntity.desc": "决定哪些玩家的数据将会被同步", "carpet.rule.emptyShulkerBoxStack.desc": "空潜影盒可堆叠,但是不会改变容器中潜影盒的堆叠行为", diff --git a/src/main/resources/slsa.accesswidener b/src/main/resources/slsa.accesswidener index b567afa..1c5bd2f 100644 --- a/src/main/resources/slsa.accesswidener +++ b/src/main/resources/slsa.accesswidener @@ -1,3 +1,5 @@ accessWidener v1 named accessible class net/minecraft/world/block/ChainRestrictedNeighborUpdater$Entry -accessible class net/minecraft/entity/mob/EndermanEntity$PickUpBlockGoal \ No newline at end of file +accessible class net/minecraft/entity/mob/EndermanEntity$PickUpBlockGoal +accessible method net/minecraft/entity/Entity unsetRemoved ()V +accessible field net/minecraft/entity/player/PlayerEntity PLAYER_MODEL_PARTS Lnet/minecraft/entity/data/TrackedData; \ No newline at end of file diff --git a/src/main/resources/slsaddition.mixins.json b/src/main/resources/slsaddition.mixins.json index 369f403..0049d50 100644 --- a/src/main/resources/slsaddition.mixins.json +++ b/src/main/resources/slsaddition.mixins.json @@ -12,7 +12,6 @@ "mixins": [ "block.BlockPatternTestTransformInvoker", "block.MixinRedstoneWireBlock", - "carpet.MixinFakePlayer", "carpet.MixinSettingsManager", "entity.MixinArmorStandEntity", "entity.MixinEnderDragonFight",