From 57ab9a7f3d3385c984eaeb4e055e1b45fa2a1413 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:18:30 +0100 Subject: [PATCH] Implement experimental mimic command --- .../server/ServerCommandManager.java | 64 +++++++++++++++++-- .../server/brigadier/ArgumentTypeHelper.java | 35 ++++------ .../server/protocol/BotConnection.java | 10 +-- .../protocol/bot/SessionDataManager.java | 36 ++++++++--- .../state/entity/AbstractClientPlayer.java | 2 +- .../protocol/bot/state/entity/Entity.java | 41 +++++++----- .../bot/state/entity/EntityFactory.java | 2 +- .../bot/state/entity/LivingEntity.java | 25 ++++++++ .../server/util/EntityMovement.java | 11 +++- 9 files changed, 163 insertions(+), 63 deletions(-) diff --git a/server/src/main/java/com/soulfiremc/server/ServerCommandManager.java b/server/src/main/java/com/soulfiremc/server/ServerCommandManager.java index 12d6d94a0..814cecbff 100644 --- a/server/src/main/java/com/soulfiremc/server/ServerCommandManager.java +++ b/server/src/main/java/com/soulfiremc/server/ServerCommandManager.java @@ -221,12 +221,12 @@ public void postConstruct() { c, bot -> { var entityId = ArgumentTypeHelper.parseEntityId(bot, entityName); - if (entityId == -1) { + if (entityId.isEmpty()) { c.getSource().sendWarn("Invalid entity specified!"); return Command.SINGLE_SUCCESS; } - var entity = bot.dataManager().entityTrackerState().getEntity(entityId); + var entity = bot.dataManager().entityTrackerState().getEntity(entityId.getAsInt()); if (entity == null) { c.getSource().sendWarn("Entity not found!"); return Command.SINGLE_SUCCESS; @@ -345,13 +345,13 @@ public void postConstruct() { c, bot -> { var entityId = ArgumentTypeHelper.parseEntityId(bot, entityName); - if (entityId == -1) { + if (entityId.isEmpty()) { c.getSource().sendWarn("Invalid entity specified!"); return Command.SINGLE_SUCCESS; } bot.scheduler().schedule(() -> new FollowEntityController( - entityId, + entityId.getAsInt(), maxRadius ).start(bot)); @@ -406,6 +406,58 @@ public void postConstruct() { return Command.SINGLE_SUCCESS; }); })))))); + dispatcher.register( + literal("mimic") + .then(argument("entity", StringArgumentType.string()) + .executes( + help( + "Makes selected bots mimic the movement of other entities", + c -> { + var entityName = StringArgumentType.getString(c, "entity"); + + return forEveryBot( + c, + bot -> { + var entityId = ArgumentTypeHelper.parseEntityId(bot, entityName); + if (entityId.isEmpty()) { + c.getSource().sendWarn("Invalid entity specified!"); + return Command.SINGLE_SUCCESS; + } + + var entity = bot.dataManager().entityTrackerState().getEntity(entityId.getAsInt()); + if (entity == null) { + c.getSource().sendWarn("Entity not found!"); + return Command.SINGLE_SUCCESS; + } + + var offset = entity.pos().sub(bot.dataManager().localPlayer().pos()); + bot.botControl().registerControllingTask(new ControllingTask() { + @Override + public void tick() { + bot.controlState().resetAll(); + + var localPlayer = bot.dataManager().localPlayer(); + localPlayer.setYRot(entity.yRot()); + localPlayer.setXRot(entity.xRot()); + + localPlayer.setPos(entity.pos().sub(offset)); + localPlayer.setDeltaMovement(entity.deltaMovement()); + } + + @Override + public void stop() { + bot.controlState().resetAll(); + } + + @Override + public boolean isDone() { + return false; + } + }); + + return Command.SINGLE_SUCCESS; + }); + })))); dispatcher.register( literal("stop-task") .executes( @@ -734,12 +786,12 @@ public void postConstruct() { c, bot -> { var entityId = ArgumentTypeHelper.parseEntityId(bot, entityName); - if (entityId == -1) { + if (entityId.isEmpty()) { c.getSource().sendWarn("Invalid entity specified!"); return Command.SINGLE_SUCCESS; } - var entity = bot.dataManager().entityTrackerState().getEntity(entityId); + var entity = bot.dataManager().entityTrackerState().getEntity(entityId.getAsInt()); if (entity == null) { c.getSource().sendWarn("Entity not found!"); return Command.SINGLE_SUCCESS; diff --git a/server/src/main/java/com/soulfiremc/server/brigadier/ArgumentTypeHelper.java b/server/src/main/java/com/soulfiremc/server/brigadier/ArgumentTypeHelper.java index 4895142e0..1cf8c71b6 100644 --- a/server/src/main/java/com/soulfiremc/server/brigadier/ArgumentTypeHelper.java +++ b/server/src/main/java/com/soulfiremc/server/brigadier/ArgumentTypeHelper.java @@ -21,11 +21,11 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.soulfiremc.server.data.EntityType; import com.soulfiremc.server.protocol.BotConnection; -import com.soulfiremc.server.util.SFHelpers; import com.soulfiremc.server.util.UUIDHelper; +import java.util.OptionalInt; + public class ArgumentTypeHelper { private ArgumentTypeHelper() { } @@ -55,36 +55,27 @@ public static DoubleAxisData readAxis(StringReader reader) throws CommandSyntaxE public record DoubleAxisData(boolean relative, double value) { } - public static int parseEntityId(BotConnection bot, String input) { + public static OptionalInt parseEntityId(BotConnection bot, String input) { var dataManager = bot.dataManager(); var parsedUniqueId = UUIDHelper.tryParseUniqueId(input); - var entityId = -1; for (var entity : dataManager.entityTrackerState().getEntities()) { - if (entity.entityType() != EntityType.PLAYER) { - continue; + if (parsedUniqueId.isPresent() && entity.uuid().equals(parsedUniqueId.get())) { + return OptionalInt.of(entity.entityId()); } - var connectedUsers = dataManager.playerListState(); - var entry = connectedUsers.entries().get(entity.uuid()); - if (entry != null - && ((parsedUniqueId.isPresent() && entry.getProfileId().equals(parsedUniqueId.get())) - || (entry.getProfile() != null && entry.getProfile().getName().equalsIgnoreCase(input))) - ) { - entityId = entity.entityId(); - break; + var entityProfile = bot.getEntityProfile(entity.uuid()); + if (entityProfile.isEmpty()) { + continue; } - } - if (entityId == -1) { - var parsedEntityId = SFHelpers.parseInt(input); - if (parsedEntityId.isEmpty()) { - return -1; + var profile = entityProfile.get(); + var gameProfile = profile.getProfile(); + if (gameProfile != null && gameProfile.getName().equals(input)) { + return OptionalInt.of(entity.entityId()); } - - entityId = parsedEntityId.getAsInt(); } - return entityId; + return OptionalInt.empty(); } } diff --git a/server/src/main/java/com/soulfiremc/server/protocol/BotConnection.java b/server/src/main/java/com/soulfiremc/server/protocol/BotConnection.java index f28a3525e..980186964 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/BotConnection.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/BotConnection.java @@ -50,6 +50,7 @@ import org.slf4j.MDC; import java.util.List; +import java.util.Optional; import java.util.Queue; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -253,12 +254,7 @@ public void sendPacket(Packet packet) { session.send(packet); } - public PlayerListEntry getEntityProfile(UUID uuid) { - var profile = dataManager.playerListState().entries().get(uuid); - if (profile == null) { - throw new IllegalStateException("Profile not found for " + uuid); - } - - return profile; + public Optional getEntityProfile(UUID uuid) { + return Optional.ofNullable(dataManager.playerListState().entries().get(uuid)); } } diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/SessionDataManager.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/SessionDataManager.java index 5cc72deb2..fb4116cf2 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/SessionDataManager.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/SessionDataManager.java @@ -33,10 +33,7 @@ import com.soulfiremc.server.protocol.bot.model.ChunkKey; import com.soulfiremc.server.protocol.bot.model.ServerPlayData; import com.soulfiremc.server.protocol.bot.state.*; -import com.soulfiremc.server.protocol.bot.state.entity.EntityFactory; -import com.soulfiremc.server.protocol.bot.state.entity.ExperienceOrbEntity; -import com.soulfiremc.server.protocol.bot.state.entity.LivingEntity; -import com.soulfiremc.server.protocol.bot.state.entity.LocalPlayer; +import com.soulfiremc.server.protocol.bot.state.entity.*; import com.soulfiremc.server.protocol.bot.state.registry.Biome; import com.soulfiremc.server.protocol.bot.state.registry.DimensionType; import com.soulfiremc.server.protocol.bot.state.registry.SFChatType; @@ -70,6 +67,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.chunk.palette.PaletteType; import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeModifier; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PositionElement; import org.geysermc.mcprotocollib.protocol.data.game.level.notify.LimitedCraftingValue; import org.geysermc.mcprotocollib.protocol.data.game.level.notify.RainStrengthValue; import org.geysermc.mcprotocollib.protocol.data.game.level.notify.RespawnScreenValue; @@ -152,6 +150,24 @@ public SessionDataManager(BotConnection connection) { this.connection = connection; } + private static void setValuesFromPositionPacket(EntityMovement newMovement, List set, Entity entity, boolean canLerp) { + var lv = EntityMovement.ofEntityUsingLerpTarget(entity); + var absolutePos = EntityMovement.calculateAbsolute(lv, newMovement, set); + var teleport = lv.pos().distanceSquared(absolutePos.pos()) > 4096.0; + if (canLerp && !teleport) { + entity.lerpTo(absolutePos.pos().getX(), absolutePos.pos().getY(), absolutePos.pos().getZ(), absolutePos.yRot(), absolutePos.xRot(), 3); + entity.setDeltaMovement(absolutePos.deltaMovement()); + } else { + entity.setPos(absolutePos.pos()); + entity.setDeltaMovement(absolutePos.deltaMovement()); + entity.setYRot(absolutePos.yRot()); + entity.setXRot(absolutePos.xRot()); + var oldPos = new EntityMovement(entity.oldPosition(), Vector3d.ZERO, entity.yRotO, entity.xRotO); + var movedOldPos = EntityMovement.calculateAbsolute(oldPos, newMovement, set); + entity.setOldPosAndRot(movedOldPos.pos(), movedOldPos.yRot(), movedOldPos.xRot()); + } + } + private static String toPlainText(Component component) { return SoulFireServer.PLAIN_MESSAGE_SERIALIZER.serialize(component); } @@ -364,12 +380,12 @@ public void onTickingStep(ClientboundTickingStepPacket packet) { @EventHandler public void onPosition(ClientboundPlayerPositionPacket packet) { - localPlayer.setFrom(new EntityMovement( + setValuesFromPositionPacket(new EntityMovement( packet.getPosition(), packet.getDeltaMovement(), packet.getYRot(), packet.getXRot() - ), packet.getRelatives()); + ), packet.getRelatives(), localPlayer, false); var position = localPlayer.blockPos(); if (!joinedWorld) { @@ -1092,12 +1108,16 @@ public void onEntityTeleport(ClientboundTeleportEntityPacket packet) { return; } - state.setFrom(new EntityMovement( + var horizontalAbsolute = packet.getRelatives().contains(PositionElement.X) + || packet.getRelatives().contains(PositionElement.Y) + || packet.getRelatives().contains(PositionElement.Z); + var canLerp = !state.isControlledByLocalInstance() || horizontalAbsolute; + setValuesFromPositionPacket(new EntityMovement( packet.getPosition(), packet.getDeltaMovement(), packet.getYRot(), packet.getXRot() - ), packet.getRelatives()); + ), packet.getRelatives(), state, canLerp); state.setOnGround(packet.isOnGround()); } diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/AbstractClientPlayer.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/AbstractClientPlayer.java index 7b4bfc91d..e3dea729a 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/AbstractClientPlayer.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/AbstractClientPlayer.java @@ -48,7 +48,7 @@ public boolean isCreative() { private PlayerListEntry getPlayerListEntry() { if (playerListEntry == null) { - playerListEntry = connection.getEntityProfile(uuid); + playerListEntry = connection.getEntityProfile(uuid).orElseThrow(); } return playerListEntry; diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/Entity.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/Entity.java index c0b95b700..f2cb76c40 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/Entity.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/Entity.java @@ -25,7 +25,6 @@ import com.soulfiremc.server.protocol.bot.state.EntityEffectState; import com.soulfiremc.server.protocol.bot.state.EntityMetadataState; import com.soulfiremc.server.protocol.bot.state.Level; -import com.soulfiremc.server.util.EntityMovement; import com.soulfiremc.server.util.MathHelper; import com.soulfiremc.server.util.SectionUtils; import com.soulfiremc.server.util.VectorHelper; @@ -56,7 +55,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.object.ObjectData; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PositionElement; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket; import org.jetbrains.annotations.Nullable; @@ -167,21 +165,6 @@ public void fromAddEntityPacket(ClientboundAddEntityPacket packet) { uuid(packet.getUuid()); } - public EntityMovement toMovement() { - return new EntityMovement(pos, deltaMovement, yRot, xRot); - } - - public void setFrom(EntityMovement packetMovement, List relatives) { - var entityMovement = EntityMovement.toAbsolute(toMovement(), packetMovement, relatives); - setPos(entityMovement.pos()); - setDeltaMovement(entityMovement.deltaMovement()); - setRot(entityMovement.yRot(), entityMovement.xRot()); - - var oldMovement = new EntityMovement(oldPosition(), Vector3d.ZERO, yRotO, xRotO); - var absoluteOldMovement = EntityMovement.toAbsolute(oldMovement, packetMovement, relatives); - setOldPosAndRot(absoluteOldMovement.pos(), absoluteOldMovement.yRot(), absoluteOldMovement.xRot()); - } - public void syncPacketPositionCodec(double x, double y, double z) { this.packetPositionCodec.base(Vector3d.from(x, y, z)); } @@ -198,6 +181,10 @@ public void moveTo(double x, double y, double z, float yRot, float xRot) { this.reapplyPosition(); } + public Vector3d getKnownMovement() { + return this.getDeltaMovement(); + } + public double x() { return pos.getX(); } @@ -210,6 +197,26 @@ public double z() { return pos.getZ(); } + public double lerpTargetX() { + return this.x(); + } + + public double lerpTargetY() { + return this.y(); + } + + public double lerpTargetZ() { + return this.z(); + } + + public float lerpTargetXRot() { + return this.xRot(); + } + + public float lerpTargetYRot() { + return this.yRot(); + } + public EntityDimensions getDimensions(Pose pose) { return entityType.dimensions(); } diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/EntityFactory.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/EntityFactory.java index ad9515c46..67d1ee296 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/EntityFactory.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/EntityFactory.java @@ -30,7 +30,7 @@ public class EntityFactory { public static Entity createEntity(BotConnection connection, EntityType entityType, Level level, UUID uuid) { if (entityType.playerEntity()) { - return new RemotePlayer(connection, level, connection.getEntityProfile(uuid).getProfile()); + return new RemotePlayer(connection, level, connection.getEntityProfile(uuid).orElseThrow().getProfile()); } else if (entityType.livingEntity()) { // TODO: Implement entity inventories return new LivingEntity(entityType, level) { diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/LivingEntity.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/LivingEntity.java index 9bf0b05d5..45132b86d 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/LivingEntity.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/LivingEntity.java @@ -191,6 +191,31 @@ public void lerpTo(double x, double y, double z, float yRot, float xRot, int ste this.lerpSteps = steps; } + @Override + public double lerpTargetX() { + return this.lerpSteps > 0 ? this.lerpX : this.x(); + } + + @Override + public double lerpTargetY() { + return this.lerpSteps > 0 ? this.lerpY : this.y(); + } + + @Override + public double lerpTargetZ() { + return this.lerpSteps > 0 ? this.lerpZ : this.z(); + } + + @Override + public float lerpTargetXRot() { + return this.lerpSteps > 0 ? (float) this.lerpXRot : this.xRot(); + } + + @Override + public float lerpTargetYRot() { + return this.lerpSteps > 0 ? (float) this.lerpYRot : this.yRot(); + } + public void aiStep() { if (this.noJumpDelay > 0) { this.noJumpDelay--; diff --git a/server/src/main/java/com/soulfiremc/server/util/EntityMovement.java b/server/src/main/java/com/soulfiremc/server/util/EntityMovement.java index 109f0aee4..b11226c3d 100644 --- a/server/src/main/java/com/soulfiremc/server/util/EntityMovement.java +++ b/server/src/main/java/com/soulfiremc/server/util/EntityMovement.java @@ -17,13 +17,22 @@ */ package com.soulfiremc.server.util; +import com.soulfiremc.server.protocol.bot.state.entity.Entity; import org.cloudburstmc.math.vector.Vector3d; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PositionElement; import java.util.List; public record EntityMovement(Vector3d pos, Vector3d deltaMovement, float yRot, float xRot) { - public static EntityMovement toAbsolute(EntityMovement current, EntityMovement packet, List relative) { + public static EntityMovement of(Entity arg) { + return new EntityMovement(arg.pos(), arg.getKnownMovement(), arg.yRot(), arg.xRot()); + } + + public static EntityMovement ofEntityUsingLerpTarget(Entity entity) { + return new EntityMovement(Vector3d.from(entity.lerpTargetX(), entity.lerpTargetY(), entity.lerpTargetZ()), entity.getKnownMovement(), entity.yRot(), entity.xRot()); + } + + public static EntityMovement calculateAbsolute(EntityMovement current, EntityMovement packet, List relative) { var x = relative.contains(PositionElement.X) ? current.pos().getX() + packet.pos().getX() : packet.pos().getX(); var y = relative.contains(PositionElement.Y) ? current.pos().getY() + packet.pos().getY() : packet.pos().getY(); var z = relative.contains(PositionElement.Z) ? current.pos().getZ() + packet.pos().getZ() : packet.pos().getZ();