feat: 调整了bot的实现方式

This commit is contained in:
Disy authored and zly2006 committed 2024-07-11 12:14:02 +08:00
1 parent 07596c15c7
commit 2063933634
10 files changed
+90 -86

No files matched your search

@@ -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;
@@ -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<ServerPlayerEntity> playerQueue = new ArrayBlockingQueue<>(128);
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
{
LiteralArgumentBuilder<ServerCommandSource> 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<World> 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<EntityPlayerMPFake> fakePlayerClass = (Class<EntityPlayerMPFake>)Class.forName("carpet.patches.EntityPlayerMPFake");
Constructor<EntityPlayerMPFake> 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;
@@ -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<GameProfile> 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) {}
}
}
@@ -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;
}
@@ -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);
}
@@ -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",
@@ -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": "空潜影盒可堆叠,但是不会改变容器中潜影盒的堆叠行为",
+3 -1
View File
@@ -1,3 +1,5 @@
accessWidener v1 named
accessible class net/minecraft/world/block/ChainRestrictedNeighborUpdater$Entry
accessible class net/minecraft/entity/mob/EndermanEntity$PickUpBlockGoal
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;
@@ -12,7 +12,6 @@
"mixins": [
"block.BlockPatternTestTransformInvoker",
"block.MixinRedstoneWireBlock",
"carpet.MixinFakePlayer",
"carpet.MixinSettingsManager",
"entity.MixinArmorStandEntity",
"entity.MixinEnderDragonFight",