diff --git a/server/src/main/java/com/soulfiremc/server/pathfinding/Costs.java b/server/src/main/java/com/soulfiremc/server/pathfinding/Costs.java index 441c34af6..3fbb2fb63 100644 --- a/server/src/main/java/com/soulfiremc/server/pathfinding/Costs.java +++ b/server/src/main/java/com/soulfiremc/server/pathfinding/Costs.java @@ -143,7 +143,7 @@ public static TickResult getRequiredMiningTicks( // If this value adds up over all ticks to 1, the block is fully mined var damage = getBlockDamagePerTick(tagsState, entity, onGround, itemStack, blockType); - var creativeMode = entity != null && entity.abilities().creativeModeBreak(); + var creativeMode = entity != null && entity.abilities().instabuild(); var willDropUsableBlockItem = correctToolUsed && !creativeMode && BlockTypeHelper.isUsableBlockItem(blockType); // Insta mine @@ -159,7 +159,7 @@ private static float getBlockDamagePerTick(TagsState tagsState, boolean onGround, @Nullable SFItemStack itemStack, BlockType blockType) { - if (entity != null && entity.abilities().creativeModeBreak()) { + if (entity != null && entity.abilities().instabuild()) { // We instantly break any block in creative mode return 1.0F; } diff --git a/server/src/main/java/com/soulfiremc/server/pathfinding/graph/PathConstraint.java b/server/src/main/java/com/soulfiremc/server/pathfinding/graph/PathConstraint.java index 9035596c0..eb1df894d 100644 --- a/server/src/main/java/com/soulfiremc/server/pathfinding/graph/PathConstraint.java +++ b/server/src/main/java/com/soulfiremc/server/pathfinding/graph/PathConstraint.java @@ -40,7 +40,7 @@ public PathConstraint(BotConnection botConnection) { } public boolean doUsableBlocksDecreaseWhenPlaced() { - return entity == null || !entity.abilities().creativeModeBreak(); + return entity == null || !entity.abilities().instabuild(); } public boolean isPlaceable(SFItemStack item) { diff --git a/server/src/main/java/com/soulfiremc/server/plugins/POVServer.java b/server/src/main/java/com/soulfiremc/server/plugins/POVServer.java index 8f260ea33..f1c008b69 100644 --- a/server/src/main/java/com/soulfiremc/server/plugins/POVServer.java +++ b/server/src/main/java/com/soulfiremc/server/plugins/POVServer.java @@ -633,46 +633,48 @@ private void syncBotAndUser() { dataManager.loginData().enforcesSecureChat())); session.send(new ClientboundRespawnPacket(spawnInfo, false, false)); - if (dataManager.difficultyData() != null) { + var difficultyData = dataManager.difficultyData(); + if (difficultyData != null) { session.send( new ClientboundChangeDifficultyPacket( - dataManager.difficultyData().difficulty(), - dataManager.difficultyData().locked())); + difficultyData.difficulty(), + difficultyData.locked())); } - if (dataManager.abilitiesData() != null) { - session.send( - new ClientboundPlayerAbilitiesPacket( - dataManager.abilitiesData().invulnerable(), - dataManager.abilitiesData().flying(), - dataManager.abilitiesData().allowFlying(), - dataManager.abilitiesData().creativeModeBreak(), - dataManager.abilitiesData().flySpeed(), - dataManager.abilitiesData().walkSpeed())); - } + var abilitiesData = dataManager.clientEntity().abilitiesData(); + session.send( + new ClientboundPlayerAbilitiesPacket( + abilitiesData.invulnerable(), + abilitiesData.flying(), + abilitiesData.mayfly(), + abilitiesData.instabuild(), + abilitiesData.flySpeed(), + abilitiesData.walkSpeed())); session.send( new ClientboundGameEventPacket( GameEvent.CHANGE_GAMEMODE, dataManager.gameMode())); - if (dataManager.borderState() != null) { + var borderState = dataManager.borderState(); + if (borderState != null) { session.send( new ClientboundInitializeBorderPacket( - dataManager.borderState().centerX(), - dataManager.borderState().centerZ(), - dataManager.borderState().oldSize(), - dataManager.borderState().newSize(), - dataManager.borderState().lerpTime(), - dataManager.borderState().newAbsoluteMaxSize(), - dataManager.borderState().warningBlocks(), - dataManager.borderState().warningTime())); + borderState.centerX(), + borderState.centerZ(), + borderState.oldSize(), + borderState.newSize(), + borderState.lerpTime(), + borderState.newAbsoluteMaxSize(), + borderState.warningBlocks(), + borderState.warningTime())); } - if (dataManager.defaultSpawnData() != null) { + var defaultSpawnData = dataManager.defaultSpawnData(); + if (defaultSpawnData != null) { session.send( new ClientboundSetDefaultSpawnPositionPacket( - dataManager.defaultSpawnData().position(), - dataManager.defaultSpawnData().angle())); + defaultSpawnData.position(), + defaultSpawnData.angle())); } if (dataManager.weatherState() != null) { @@ -694,20 +696,22 @@ private void syncBotAndUser() { dataManager.weatherState().thunderStrength()))); } - if (dataManager.healthData() != null) { + var healthData = dataManager.healthData(); + if (healthData != null) { session.send( new ClientboundSetHealthPacket( - dataManager.healthData().health(), - dataManager.healthData().food(), - dataManager.healthData().saturation())); + healthData.health(), + healthData.food(), + healthData.saturation())); } - if (dataManager.experienceData() != null) { + var experienceData = dataManager.experienceData(); + if (experienceData != null) { session.send( new ClientboundSetExperiencePacket( - dataManager.experienceData().experience(), - dataManager.experienceData().level(), - dataManager.experienceData().totalExperience())); + experienceData.experience(), + experienceData.level(), + experienceData.totalExperience())); } // Give initial coordinates to the client diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/BotControlAPI.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/BotControlAPI.java index a41dd69b6..d33cdca71 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/BotControlAPI.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/BotControlAPI.java @@ -71,13 +71,13 @@ public void tick() { } public boolean toggleFlight() { - var abilitiesData = dataManager.abilitiesData(); - if (abilitiesData != null && !abilitiesData.allowFlying()) { + var abilitiesData = dataManager.clientEntity().abilitiesData(); + if (abilitiesData != null && !abilitiesData.mayfly()) { throw new IllegalStateException("You can't fly! (Server said so)"); } - var newFly = !dataManager.controlState().flying(); - dataManager.controlState().flying(newFly); + var newFly = !dataManager.clientEntity().abilitiesData().flying(); + dataManager.clientEntity().abilitiesData().flying(newFly); // Let the server know we are flying connection.sendPacket(new ServerboundPlayerAbilitiesPacket(newFly)); 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 4b33f0c48..fb9637db2 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 @@ -31,7 +31,6 @@ import com.soulfiremc.server.protocol.bot.container.SFItemStack; import com.soulfiremc.server.protocol.bot.container.WindowContainer; import com.soulfiremc.server.protocol.bot.model.*; -import com.soulfiremc.server.protocol.bot.movement.ControlState; import com.soulfiremc.server.protocol.bot.state.*; import com.soulfiremc.server.protocol.bot.state.entity.ClientEntity; import com.soulfiremc.server.protocol.bot.state.entity.ExperienceOrbEntity; @@ -146,7 +145,6 @@ public final class SessionDataManager { private @Nullable GlobalPos lastDeathPos; private int portalCooldown = -1; private @Nullable DifficultyData difficultyData; - private @Nullable AbilitiesData abilitiesData; private @Nullable DefaultSpawnData defaultSpawnData; private @Nullable ExperienceData experienceData; private @Nullable ChunkKey centerChunk; @@ -502,24 +500,13 @@ public void onSetDifficulty(ClientboundChangeDifficultyPacket packet) { @EventHandler public void onAbilities(ClientboundPlayerAbilitiesPacket packet) { - abilitiesData = - new AbilitiesData( - packet.isInvincible(), - packet.isFlying(), - packet.isCanFly(), - packet.isCreative(), - packet.getFlySpeed(), - packet.getWalkSpeed()); - - var attributeState = clientEntity.attributeState(); - attributeState - .getOrCreateAttribute(AttributeType.MOVEMENT_SPEED) - .baseValue(abilitiesData.walkSpeed()); - attributeState - .getOrCreateAttribute(AttributeType.FLYING_SPEED) - .baseValue(abilitiesData.flySpeed()); - - controlState.flying(abilitiesData.flying()); + var abilitiesData = clientEntity.abilitiesData(); + abilitiesData.flying = packet.isFlying(); + abilitiesData.instabuild = packet.isCreative(); + abilitiesData.invulnerable = packet.isInvincible(); + abilitiesData.mayfly = packet.isCanFly(); + abilitiesData.flySpeed(packet.getFlySpeed()); + abilitiesData.walkSpeed(packet.getWalkSpeed()); } @EventHandler diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/model/AbilitiesData.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/model/AbilitiesData.java index 4ff876e17..a8d096788 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/model/AbilitiesData.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/model/AbilitiesData.java @@ -17,10 +17,23 @@ */ package com.soulfiremc.server.protocol.bot.model; -public record AbilitiesData( - boolean invulnerable, - boolean flying, - boolean allowFlying, - boolean creativeModeBreak, - float flySpeed, - float walkSpeed) {} +import lombok.*; + +@Setter +@Getter +@ToString +@EqualsAndHashCode +@AllArgsConstructor +public final class AbilitiesData { + public boolean invulnerable; + public boolean flying; + public boolean mayfly; + public boolean instabuild; + public boolean mayBuild; + private float flySpeed; + private float walkSpeed; + + public AbilitiesData() { + this(false, false, false, false, true, 0.05F, 0.1F); + } +} diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BotMovementManager.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BotMovementManager.java deleted file mode 100644 index 3fc2e69a9..000000000 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BotMovementManager.java +++ /dev/null @@ -1,792 +0,0 @@ -/* - * SoulFire - * Copyright (C) 2024 AlexProgrammerDE - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.soulfiremc.server.protocol.bot.movement; - -import com.soulfiremc.server.data.AttributeType; -import com.soulfiremc.server.data.BlockState; -import com.soulfiremc.server.data.BlockTags; -import com.soulfiremc.server.data.BlockType; -import com.soulfiremc.server.protocol.bot.SessionDataManager; -import com.soulfiremc.server.protocol.bot.state.Level; -import com.soulfiremc.server.protocol.bot.state.TagsState; -import com.soulfiremc.server.protocol.bot.state.entity.ClientEntity; -import com.soulfiremc.server.protocol.bot.state.entity.Entity; -import com.soulfiremc.server.util.MathHelper; -import com.soulfiremc.server.util.mcstructs.AABB; -import it.unimi.dsi.fastutil.Pair; -import lombok.Getter; -import org.cloudburstmc.math.vector.Vector3d; -import org.cloudburstmc.math.vector.Vector3i; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.BiConsumer; - -/** - * Java port of prismarine-physics. - */ -public class BotMovementManager { - private static final List WATER_TYPES = List.of(BlockType.WATER); - private static final List LAVA_TYPES = List.of(BlockType.WATER); - private static final List WATER_LIKE_TYPES = - List.of( - BlockType.WATER, - BlockType.SEAGRASS, - BlockType.TALL_SEAGRASS, - BlockType.KELP, - BlockType.KELP_PLANT, - BlockType.BUBBLE_COLUMN); - private final ClientEntity clientEntity; - @Getter - private final PlayerMovementState movementState; - private final PhysicsData physics; - private final TagsState tagsState; - private final ControlState controlState; - - public BotMovementManager( - SessionDataManager dataManager, - PlayerMovementState movementState, - ClientEntity clientEntity) { - this.clientEntity = clientEntity; - this.movementState = movementState; - this.physics = clientEntity.physics(); - this.tagsState = dataManager.tagsState(); - this.controlState = dataManager.controlState(); - } - - private static void consumeIntersectedBlocks( - Level world, AABB queryBB, BiConsumer consumer) { - var startX = MathHelper.floorDouble(queryBB.minX - 1.0E-7) - 1; - var endX = MathHelper.floorDouble(queryBB.maxX + 1.0E-7) + 1; - var startY = MathHelper.floorDouble(queryBB.minY - 1.0E-7) - 1; - var endY = MathHelper.floorDouble(queryBB.maxY + 1.0E-7) + 1; - var startZ = MathHelper.floorDouble(queryBB.minZ - 1.0E-7) - 1; - var endZ = MathHelper.floorDouble(queryBB.maxZ + 1.0E-7) + 1; - - for (var x = startX; x <= endX; x++) { - for (var y = startY; y <= endY; y++) { - for (var z = startZ; z <= endZ; z++) { - var cursor = Vector3i.from(x, y, z); - var block = world.getBlockState(cursor); - - for (var collisionBox : block.getCollisionBoxes(cursor)) { - if (collisionBox.intersects(queryBB)) { - consumer.accept(block, cursor); - break; - } - } - } - } - } - } - - private static double horizontalDistanceSqr(Vector3d vec) { - return vec.getX() * vec.getX() + vec.getZ() * vec.getZ(); - } - - public static Vector3d collideBoundingBox(Level world, Vector3d targetVec, AABB queryBB) { - return collideWith( - targetVec, queryBB, world.getCollisionBoxes(queryBB.expandTowards(targetVec))); - } - - private static Vector3d collideWith( - Vector3d direction, AABB boundingBox, List collisionBoxes) { - var dx = direction.getX(); - var dy = direction.getY(); - var dz = direction.getZ(); - - // TODO: Fix movement - - return Vector3d.from(dx, dy, dz); - } - - private static LookingVectorData getLookingVector(Entity clientEntity) { - // given a yRot xRot, we need the looking vector - - // yRot is right handed rotation about y (up) starting from -z (north) - // xRot is -90 looking down, 90 looking up, 0 looking at horizon - // lets get its coordinate system. - // var x' = -z (north) - // var y' = -x (west) - // var z' = y (up) - - // the non normalized looking vector in x', y', z' space is - // x' is cos(yRot) - // y' is sin(yRot) - // z' is tan(xRot) - - // substituting back in x, y, z, we get the looking vector in the normal x, y, z space - // -z = cos(yRot) => z = -cos(yRot) - // -x = sin(yRot) => x = -sin(yRot) - // y = tan(xRot) - - // normalizing the vectors, we divide each by |sqrt(x*x + y*y + z*z)| - // x*x + z*z = sin^2 + cos^2 = 1 - // so |sqrt(xx+yy+zz)| = |sqrt(1+tan^2(xRot))| - // = |sqrt(1+sin^2(xRot)/cos^2(xRot))| - // = |sqrt((cos^2+sin^2)/cos^2(xRot))| - // = |sqrt(1/cos^2(xRot))| - // = |+/- 1/cos(xRot)| - // = 1/cos(xRot) since xRot in [-90, 90] - - // the looking vector is therefore - // x = -sin(yRot) * cos(xRot) - // y = tan(xRot) * cos(xRot) = sin(xRot) - // z = -cos(yRot) * cos(xRot) - - var yRot = clientEntity.yRot(); - var xRot = clientEntity.xRot(); - var sinYRot = Math.sin(yRot); - var cosYRot = Math.cos(yRot); - var sinXRot = Math.sin(xRot); - var cosXRot = Math.cos(xRot); - var lookX = -sinYRot * cosXRot; - var lookZ = -cosYRot * cosXRot; - var lookDir = new MutableVector3d(lookX, sinXRot, lookZ); - return new LookingVectorData( - yRot, xRot, sinYRot, cosYRot, sinXRot, cosXRot, lookX, sinXRot, lookZ, lookDir); - } - - public static boolean isMaterialInBB(Level world, AABB queryBB, List types) { - var minX = MathHelper.floorDouble(queryBB.minX); - var minY = MathHelper.floorDouble(queryBB.minY); - var minZ = MathHelper.floorDouble(queryBB.minZ); - - var maxX = MathHelper.floorDouble(queryBB.maxX); - var maxY = MathHelper.floorDouble(queryBB.maxY); - var maxZ = MathHelper.floorDouble(queryBB.maxZ); - - for (var x = minX; x <= maxX; x++) { - for (var y = minY; y <= maxY; y++) { - for (var z = minZ; z <= maxZ; z++) { - var block = world.getBlockState(Vector3i.from(x, y, z)); - if (types.contains(block.blockType())) { - return true; - } - } - } - } - - return false; - } - - public static void setPositionToBB(AABB bb, MutableVector3d pos) { - pos.x = (bb.minX + bb.maxX) / 2; - pos.y = bb.minY; - pos.z = (bb.minZ + bb.maxZ) / 2; - } - - public boolean isClimbable(Level world, Vector3i pos) { - var blockType = world.getBlockState(pos).blockType(); - return tagsState.isValueInTag(blockType, BlockTags.CLIMBABLE) - || blockType == BlockType.POWDER_SNOW; - } - - public void tick() { - var world = clientEntity.level(); - var vel = movementState.vel; - - { - var playerBB = clientEntity.boundingBox(); - var waterBB = playerBB.deflate(0.001, 0.401, 0.001); - var lavaBB = playerBB.deflate(0.1, 0.4, 0.1); - - movementState.isInWater = isInWaterApplyCurrent(world, waterBB, vel); - movementState.isInLava = isMaterialInBB(world, lavaBB, LAVA_TYPES); - } - - // Reset velocity component if it falls under the threshold - if (Math.abs(vel.x) < physics.negligeableVelocity) { - vel.x = 0; - } - if (Math.abs(vel.y) < physics.negligeableVelocity) { - vel.y = 0; - } - if (Math.abs(vel.z) < physics.negligeableVelocity) { - vel.z = 0; - } - - // Handle inputs - if (controlState.jumping() || movementState.jumpQueued) { - if (movementState.jumpTicks > 0) { - movementState.jumpTicks--; - } - - if (movementState.isInWater || movementState.isInLava) { - vel.y += 0.04; - } else if (movementState.onGround && movementState.jumpTicks == 0) { - var blockBelow = - world.getBlockState(movementState.pos.floored().offset(0, -0.5, 0).toImmutableInt()); - vel.y = - (float) (0.42) - * ((blockBelow.blockType() == BlockType.HONEY_BLOCK) - ? physics.honeyblockJumpSpeed - : 1); - if (movementState.jumpBoost > 0) { - vel.y += 0.1 * movementState.jumpBoost; - } - - if (controlState.sprinting()) { - var yRot = Math.PI - clientEntity.yRot(); - vel.x -= Math.sin(yRot) * 0.2; - vel.z += Math.cos(yRot) * 0.2; - } - - movementState.jumpTicks = physics.autojumpCooldown; - } - } else { - movementState.jumpTicks = 0; // reset autojump cooldown - } - - movementState.jumpQueued = false; - - var forward = 0.0F; - if (controlState.forward()) { - forward++; - } - - if (controlState.backward()) { - forward--; - } - - var strafe = 0.0F; - if (controlState.right()) { - strafe++; - } - - if (controlState.left()) { - strafe--; - } - - strafe *= 0.98F; - forward *= 0.98F; - - if (controlState.sneaking()) { - strafe *= (float) ((double) strafe * physics.sneakSpeed); - forward *= (float) ((double) forward * physics.sneakSpeed); - } - - movementState.elytraFlying = - movementState.elytraFlying - && movementState.elytraEquipped - && !movementState.onGround - && movementState.levitation == 0; - - if (movementState.fireworkRocketDuration > 0) { - if (!movementState.elytraFlying) { - movementState.fireworkRocketDuration = 0; - } else { - var lookingVector = getLookingVector(clientEntity); - var lookDir = lookingVector.lookDir; - vel.x += lookDir.x * 0.1 + (lookDir.x * 1.5 - vel.x) * 0.5; - vel.y += lookDir.y * 0.1 + (lookDir.y * 1.5 - vel.y) * 0.5; - vel.z += lookDir.z * 0.1 + (lookDir.z * 1.5 - vel.z) * 0.5; - --movementState.fireworkRocketDuration; - } - } - - moveEntityWithHeading(world, strafe, forward); - } - - public void moveEntityWithHeading(Level world, float strafe, float forward) { - var vel = movementState.vel; - var pos = movementState.pos; - - var gravityMultiplier = (vel.y <= 0 && movementState.slowFalling > 0) ? physics.slowFalling : 1; - - if (movementState.isInWater || movementState.isInLava) { - // Water / Lava movement - var lastY = pos.y; - var liquidSpeed = physics.liquidSpeed; - var typeSpeed = movementState.isInWater ? physics.waterSpeed : physics.lavaSpeed; - var horizontalInertia = typeSpeed; - - if (movementState.isInWater) { - var waterMovementEfficiency = (float) clientEntity.attributeValue(AttributeType.WATER_MOVEMENT_EFFICIENCY); - - if (!movementState.onGround) { - waterMovementEfficiency *= 0.5F; - } - - if (waterMovementEfficiency > 0) { - horizontalInertia += (0.54600006F - horizontalInertia) * waterMovementEfficiency; - liquidSpeed += (this.getSpeed() - liquidSpeed) * waterMovementEfficiency; - } - - if (movementState.dolphinsGrace > 0) { - horizontalInertia = 0.96F; - } - } - - applyHeading(strafe, forward, liquidSpeed); - - moveEntity(world, vel.x, vel.y, vel.z); - vel.y *= typeSpeed; - vel.y -= - (movementState.isInWater ? physics.waterGravity : physics.lavaGravity) - * gravityMultiplier; - vel.x *= horizontalInertia; - vel.z *= horizontalInertia; - - if (movementState.isCollidedHorizontally - && doesNotCollide(world, pos.offset(vel.x, vel.y + 0.6 - pos.y + lastY, vel.z))) { - vel.y = physics.outOfLiquidImpulse; // jump out of liquid - } - } else if (movementState.elytraFlying) { - var lookingData = getLookingVector(clientEntity); - var xRot = lookingData.xRot; - var sinXRot = lookingData.sinXRot; - var cosXRot = lookingData.cosXRot; - var lookDir = lookingData.lookDir; - - var horizontalSpeed = Math.sqrt(vel.x * vel.x + vel.z * vel.z); - var cosXRotSquared = cosXRot * cosXRot; - vel.y += physics.gravity * gravityMultiplier * (-1.0 + cosXRotSquared * 0.75); - // cosXRot is in [0, 1], so cosXRot > 0.0 is just to protect against - // divide by zero errors - if (vel.y < 0.0 && cosXRot > 0.0) { - var movingDownSpeedModifier = vel.y * (-0.1) * cosXRotSquared; - vel.x += lookDir.x * movingDownSpeedModifier / cosXRot; - vel.y += movingDownSpeedModifier; - vel.z += lookDir.z * movingDownSpeedModifier / cosXRot; - } - - if (xRot < 0.0 && cosXRot > 0.0) { - var lookDownSpeedModifier = horizontalSpeed * (-sinXRot) * 0.04; - vel.x += -lookDir.x * lookDownSpeedModifier / cosXRot; - vel.y += lookDownSpeedModifier * 3.2; - vel.z += -lookDir.z * lookDownSpeedModifier / cosXRot; - } - - if (cosXRot > 0.0) { - vel.x += (lookDir.x / cosXRot * horizontalSpeed - vel.x) * 0.1; - vel.z += (lookDir.z / cosXRot * horizontalSpeed - vel.z) * 0.1; - } - - vel.x *= 0.99; - vel.y *= 0.98; - vel.z *= 0.99; - moveEntity(world, vel.x, vel.y, vel.z); - - if (movementState.onGround) { - movementState.elytraFlying = false; - } - } else { - // Normal movement - var xzMultiplier = 0.91F; - float frictionInfluencedSpeed; - - var blockUnder = world.getBlockState(pos.offset(0, -0.500001F, 0).toImmutableInt()); - if (movementState.onGround) { - var friction = getBlockFriction(blockUnder.blockType()); - xzMultiplier *= friction; - frictionInfluencedSpeed = this.getSpeed() * (0.21600002F / (friction * friction * friction)); - } else { - frictionInfluencedSpeed = getFlyingSpeed(); - } - - applyHeading(strafe, forward, frictionInfluencedSpeed); - - if (isClimbable(world, pos.toImmutableInt())) { - vel.x = MathHelper.doubleClamp(vel.x, -physics.climbMaxSpeed, physics.climbMaxSpeed); - vel.z = MathHelper.doubleClamp(vel.z, -physics.climbMaxSpeed, physics.climbMaxSpeed); - vel.y = Math.max(vel.y, controlState.sneaking() ? 0 : -physics.climbMaxSpeed); - } - - moveEntity(world, vel.x, vel.y, vel.z); - - if ((movementState.isCollidedHorizontally || controlState.jumping()) - && isClimbable(world, pos.toImmutableInt())) { - vel.y = physics.climbSpeed; // climb ladder - } - - // Apply friction and gravity - if (movementState.levitation > 0) { - vel.y += (0.05 * movementState.levitation - vel.y) * 0.2; - } else { - vel.y -= physics.gravity * gravityMultiplier; - } - - vel.x *= xzMultiplier; - vel.y *= physics.airdrag; - vel.z *= xzMultiplier; - } - } - - private float getFlyingSpeed() { - if (controlState.flying()) { - var abilitiesData = clientEntity.abilities(); - var flySpeed = abilitiesData == null ? 0.05F : abilitiesData.flySpeed(); - return controlState.sprinting() ? flySpeed * 2.0F : flySpeed; - } else { - return controlState.sprinting() ? 0.025999999F : 0.02F; - } - } - - public float getSpeed() { - var attribute = movementState.entity().attributeState(); - var playerSpeedAttribute = attribute.getOrCreateAttribute(AttributeType.MOVEMENT_SPEED); - - if (controlState.sprinting()) { - playerSpeedAttribute.modifiers().putIfAbsent(PhysicsData.SPRINTING_MODIFIER_ID, PhysicsData.SPEED_MODIFIER_SPRINTING); - } else { - // Client-side sprinting (don't rely on server-side sprinting) - // setSprinting in LivingEntity.java - playerSpeedAttribute.modifiers().remove(PhysicsData.SPRINTING_MODIFIER_ID); - } - - return (float) clientEntity.attributeValue(AttributeType.MOVEMENT_SPEED); - } - - public void moveEntity(Level world, double dx, double dy, double dz) { - var vel = movementState.vel; - var pos = movementState.pos; - - var playerBB = clientEntity.boundingBox(pos.toImmutable()); - - if (movementState.isInWeb) { - dx *= 0.25; - dy *= 0.05; - dz *= 0.25; - vel.x = 0; - vel.y = 0; - vel.z = 0; - movementState.isInWeb = false; - } - - var oldVelX = dx; - var oldVelY = dy; - var oldVelZ = dz; - - if (controlState.sneaking() && movementState.onGround) { - var step = 0.05; - - // In the 3 loops bellow, y offset should be -1, but that doesnt reproduce vanilla behavior. - for (; dx != 0 && world.getCollisionBoxes(playerBB.move(dx, 0, 0)).isEmpty(); oldVelX = dx) { - if (dx < step && dx >= -step) { - dx = 0; - } else if (dx > 0) { - dx -= step; - } else { - dx += step; - } - } - - for (; dz != 0 && world.getCollisionBoxes(playerBB.move(0, 0, dz)).isEmpty(); oldVelZ = dz) { - if (dz < step && dz >= -step) { - dz = 0; - } else if (dz > 0) { - dz -= step; - } else { - dz += step; - } - } - - while (dx != 0 && dz != 0 && world.getCollisionBoxes(playerBB.move(dx, 0, dz)).isEmpty()) { - if (dx < step && dx >= -step) { - dx = 0; - } else if (dx > 0) { - dx -= step; - } else { - dx += step; - } - - if (dz < step && dz >= -step) { - dz = 0; - } else if (dz > 0) { - dz -= step; - } else { - dz += step; - } - - oldVelX = dx; - oldVelZ = dz; - } - } - - var collisionResult = collide(world, playerBB, Vector3d.from(dx, dy, dz)); - dx = collisionResult.getX(); - dy = collisionResult.getY(); - dz = collisionResult.getZ(); - - var resultingBB = playerBB.move(dx, dy, dz); - - // Update flags - setPositionToBB(resultingBB, pos); - movementState.isCollidedHorizontally = dx != oldVelX || dz != oldVelZ; - movementState.isCollidedVertically = dy != oldVelY; - movementState.onGround = movementState.isCollidedVertically && oldVelY < 0; - - // We collided, so we block the motion - if (dx != oldVelX) { - vel.x = 0; - } - - if (dz != oldVelZ) { - vel.z = 0; - } - - if (dy != oldVelY) { - var blockAtFeet = world.getBlockState(pos.offset(0, -0.2, 0).toImmutableInt()); - if (blockAtFeet.blockType() == BlockType.SLIME_BLOCK && !controlState.sneaking()) { - vel.y = -vel.y; - } else { - vel.y = 0; - } - } - - // Finally, apply block collisions (web, soulsand...) - consumeIntersectedBlocks( - world, - resultingBB.deflate(0.001, 0.001, 0.001), - (block, cursor) -> { - if (block.blockType() == BlockType.COBWEB) { - movementState.isInWeb = true; - } else if (block.blockType() == BlockType.BUBBLE_COLUMN) { - var down = !block.properties().getBoolean("drag"); - var aboveBlock = world.getBlockState(cursor.add(0, 1, 0)); - var bubbleDrag = - aboveBlock.blockType() == BlockType.AIR - ? physics.bubbleColumnSurfaceDrag - : physics.bubbleColumnDrag; - if (down) { - vel.y = Math.max(bubbleDrag.maxDown(), vel.y - bubbleDrag.down()); - } else { - vel.y = Math.min(bubbleDrag.maxUp(), vel.y + bubbleDrag.up()); - } - } - }); - - var blockBelow = - world.getBlockState(movementState.pos.floored().offset(0, -0.5, 0).toImmutableInt()); - if (blockBelow.blockType() == BlockType.SOUL_SAND) { - vel.x *= physics.soulsandSpeed; - vel.z *= physics.soulsandSpeed; - } else if (blockBelow.blockType() == BlockType.HONEY_BLOCK) { - vel.x *= physics.honeyblockSpeed; - vel.z *= physics.honeyblockSpeed; - } - } - - private Vector3d collide(Level world, AABB playerBB, Vector3d targetVec) { - var initialCollisionVec = - targetVec.lengthSquared() == 0.0 - ? targetVec - : collideBoundingBox(world, targetVec, playerBB); - var xChanged = targetVec.getX() != initialCollisionVec.getY(); - var yChanged = targetVec.getY() != initialCollisionVec.getY(); - var zChanged = targetVec.getZ() != initialCollisionVec.getZ(); - var collidedY = movementState.onGround || yChanged && targetVec.getY() < 0; - - // Step on block if height < stepHeight - if (physics.stepHeight > 0 && collidedY && (xChanged || zChanged)) { - var fullStep = - collideBoundingBox( - world, - Vector3d.from(targetVec.getX(), physics.stepHeight, targetVec.getZ()), - playerBB); - var justStep = - collideBoundingBox( - world, - Vector3d.from(0.0, physics.stepHeight, 0.0), - playerBB.expandTowards(targetVec.getX(), 0.0, targetVec.getZ())); - if (justStep.getY() < physics.stepHeight) { - var justMove = - collideBoundingBox( - world, - Vector3d.from(targetVec.getX(), 0.0, targetVec.getZ()), - playerBB.move(justStep)) - .add(justStep); - if (horizontalDistanceSqr(justMove) > horizontalDistanceSqr(fullStep)) { - fullStep = justMove; - } - } - - if (horizontalDistanceSqr(fullStep) > horizontalDistanceSqr(initialCollisionVec)) { - return fullStep.add( - collideBoundingBox( - world, - Vector3d.from(0.0, -fullStep.getY() + targetVec.getY(), 0.0), - playerBB.move(fullStep))); - } - } - - return initialCollisionVec; - } - - public void applyHeading(double strafe, double forward, float speed) { - var distanceSquared = strafe * strafe + forward * forward; - if (distanceSquared < 1.0E-7) { - return; - } - - if (distanceSquared > 1) { - var distance = Math.sqrt(distanceSquared); - strafe /= distance; - forward /= distance; - } - - strafe *= speed; - forward *= speed; - - var yRotRadians = Math.toRadians(clientEntity.yRot()); - var sin = Math.sin(yRotRadians); - var cos = Math.cos(yRotRadians); - - var vel = movementState.vel; - vel.x += strafe * cos - forward * sin; - vel.z += forward * cos + strafe * sin; - } - - public boolean doesNotCollide(Level world, MutableVector3d pos) { - var pBB = clientEntity.boundingBox(pos.toImmutable()); - return world.getCollisionBoxes(pBB).isEmpty() && getWaterInBB(world, pBB).isEmpty(); - } - - public int getLiquidHeightPercent(@Nullable BlockState meta) { - return (getRenderedDepth(meta) + 1) / 9; - } - - public int getRenderedDepth(@Nullable BlockState meta) { - if (meta == null) { - return -1; - } - - if (WATER_LIKE_TYPES.contains(meta.blockType())) { - return 0; - } - - if (meta.properties().getBoolean("waterlogged")) { - return 0; - } - - if (!WATER_TYPES.contains(meta.blockType())) { - return -1; - } - - var level = meta.properties().getInt("level"); - return level >= 8 ? 0 : level; - } - - public Vector3i getFlow(Level world, BlockState meta, Vector3i block) { - var curlevel = getRenderedDepth(meta); - var flow = new MutableVector3d(0, 0, 0); - for (var combination : - new int[][]{new int[]{0, 1}, new int[]{-1, 0}, new int[]{0, -1}, new int[]{1, 0}}) { - var dx = combination[0]; - var dz = combination[1]; - var adjBlockVec = block.add(dx, 0, dz); - var adjBlock = world.getBlockState(adjBlockVec); - var adjLevel = getRenderedDepth(adjBlock); - if (adjLevel < 0) { - if (adjBlock.blockShapeGroup().hasNoCollisions()) { - var adjLevel2Vec = block.add(dx, -1, dz); - var adjLevel2 = getRenderedDepth(world.getBlockState(adjLevel2Vec)); - if (adjLevel2 >= 0) { - var f = adjLevel2 - (curlevel - 8); - flow.x += dx * f; - flow.z += dz * f; - } - } - } else { - var f = adjLevel - curlevel; - flow.x += dx * f; - flow.z += dz * f; - } - } - - if (meta.properties().getInt("level") >= 8) { - for (var combination : - new int[][]{new int[]{0, 1}, new int[]{-1, 0}, new int[]{0, -1}, new int[]{1, 0}}) { - var dx = combination[0]; - var dz = combination[1]; - var adjBlock = world.getBlockState(block.add(dx, 0, dz)); - var adjUpBlock = world.getBlockState(block.add(dx, 1, dz)); - if ((adjBlock.blockShapeGroup().hasNoCollisions()) - || (adjUpBlock.blockShapeGroup().hasNoCollisions())) { - flow.normalize().translate(0, -6, 0); - } - } - } - - return flow.normalize().toImmutableInt(); - } - - public List> getWaterInBB(Level world, AABB bb) { - var waterBlocks = new ArrayList>(); - - consumeIntersectedBlocks( - world, - bb, - (block, cursor) -> { - if (WATER_TYPES.contains(block.blockType()) - || WATER_LIKE_TYPES.contains(block.blockType()) - || block.properties().getBoolean("waterlogged")) { - var waterLevel = cursor.getY() + 1 - getLiquidHeightPercent(block); - if (Math.ceil(bb.maxY) >= waterLevel) { - waterBlocks.add(Pair.of(cursor, block)); - } - } - }); - - return waterBlocks; - } - - public boolean isInWaterApplyCurrent(Level world, AABB bb, MutableVector3d vel) { - var acceleration = new MutableVector3d(0, 0, 0); - var waterBlocks = getWaterInBB(world, bb); - var isInWater = !waterBlocks.isEmpty(); - for (var block : waterBlocks) { - var flow = getFlow(world, block.right(), block.left()); - acceleration.add(flow); - } - - var len = acceleration.norm(); - if (len > 0) { - vel.x += acceleration.x / len * 0.014; - vel.y += acceleration.y / len * 0.014; - vel.z += acceleration.z / len * 0.014; - } - return isInWater; - } - - private float getBlockFriction(BlockType blockType) { - if (blockType == BlockType.SLIME_BLOCK) { - return 0.8F; - } else if (blockType == BlockType.ICE || blockType == BlockType.PACKED_ICE) { - return 0.98F; - } else if (blockType == BlockType.BLUE_ICE) { - return 0.989F; - } else { - return physics.defaultSlipperiness; // Normal block - } - } - - record LookingVectorData( - float yRot, - float xRot, - double sinYRot, - double cosYRot, - double sinXRot, - double cosXRot, - double lookX, - double lookY, - double lookZ, - MutableVector3d lookDir) {} -} diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BubbleColumnDrag.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BubbleColumnDrag.java deleted file mode 100644 index 72b637c68..000000000 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BubbleColumnDrag.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SoulFire - * Copyright (C) 2024 AlexProgrammerDE - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.soulfiremc.server.protocol.bot.movement; - -public record BubbleColumnDrag(double down, double maxDown, double up, double maxUp) {} diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/MutableVector3d.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/MutableVector3d.java deleted file mode 100644 index 1a7154e64..000000000 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/MutableVector3d.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SoulFire - * Copyright (C) 2024 AlexProgrammerDE - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.soulfiremc.server.protocol.bot.movement; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.ToString; -import org.cloudburstmc.math.GenericMath; -import org.cloudburstmc.math.vector.Vector3d; -import org.cloudburstmc.math.vector.Vector3i; - -@Getter -@AllArgsConstructor -@ToString -public class MutableVector3d { - public double x; - public double y; - public double z; - - public Vector3d toImmutable() { - return Vector3d.from(x, y, z); - } - - public Vector3i toImmutableInt() { - return Vector3i.from(x, y, z); - } - - public MutableVector3d offset(double x, double v, double z) { - return new MutableVector3d(this.x + x, this.y + v, this.z + z); - } - - public MutableVector3d floored() { - return new MutableVector3d(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); - } - - public void add(Vector3i vector3i) { - this.x += vector3i.getX(); - this.y += vector3i.getY(); - this.z += vector3i.getZ(); - } - - public void add(Vector3d vector3d) { - this.x += vector3d.getX(); - this.y += vector3d.getY(); - this.z += vector3d.getZ(); - } - - public double norm() { - return Math.sqrt(x * x + y * y + z * z); - } - - public MutableVector3d normalize() { - var norm = norm(); - if (norm != 0) { - x /= norm; - y /= norm; - z /= norm; - } - - return this; - } - - public void translate(int i, int i1, int i2) { - x += i; - y += i1; - z += i2; - } -} diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PhysicsData.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PhysicsData.java deleted file mode 100644 index 301f90d22..000000000 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PhysicsData.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SoulFire - * Copyright (C) 2024 AlexProgrammerDE - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.soulfiremc.server.protocol.bot.movement; - -import com.soulfiremc.server.data.Attribute; -import com.soulfiremc.server.data.ModifierOperation; -import lombok.Data; -import net.kyori.adventure.key.Key; - -@Data -public class PhysicsData { - // From LivingEntity.java - public static final Key SPRINTING_MODIFIER_ID = Key.key("sprinting"); - public static final Attribute.Modifier SPEED_MODIFIER_SPRINTING = new Attribute.Modifier( - SPRINTING_MODIFIER_ID, 0.3F, ModifierOperation.ADD_MULTIPLIED_TOTAL - ); - public double gravity = 0.08; - public float airdrag = 0.98F; - public double sprintSpeed = 0.3F; - public double sneakSpeed = 0.3; - public double stepHeight = 0.6F; - public double negligeableVelocity = 0.003; - public double soulsandSpeed = 0.4; - public double honeyblockSpeed = 0.4; - public double honeyblockJumpSpeed = 0.4; - public double climbMaxSpeed = 0.15; - public double climbSpeed = 0.2; - public float waterSpeed = 0.8F; - public float lavaSpeed = 0.5F; - public float liquidSpeed = 0.02F; - public float defaultSlipperiness = 0.6F; - public double outOfLiquidImpulse = 0.3; - public int autojumpCooldown = 10; - public BubbleColumnDrag bubbleColumnSurfaceDrag = new BubbleColumnDrag(0.03, -0.9, 0.1, 1.8); - public BubbleColumnDrag bubbleColumnDrag = new BubbleColumnDrag(0.03, -0.3, 0.06, 0.7); - public double slowFalling = 0.125; - public double waterGravity = gravity / 16; - public double lavaGravity = gravity / 4; -} diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PlayerMovementState.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PlayerMovementState.java deleted file mode 100644 index e1af0fb79..000000000 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PlayerMovementState.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SoulFire - * Copyright (C) 2024 AlexProgrammerDE - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.soulfiremc.server.protocol.bot.movement; - -import com.soulfiremc.server.data.ItemType; -import com.soulfiremc.server.protocol.bot.container.PlayerInventoryContainer; -import com.soulfiremc.server.protocol.bot.state.entity.ClientEntity; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; - -@Getter -@Setter -@RequiredArgsConstructor -public class PlayerMovementState { - private final ClientEntity entity; - private final PlayerInventoryContainer inventoryContainer; - - // Position - public MutableVector3d pos; - - // Motion - public MutableVector3d vel; - - // Collision - public boolean onGround; - public boolean isCollidedHorizontally; - public boolean isCollidedVertically; - public boolean isInWater; - public boolean isInLava; - public boolean isInWeb; - - // State - public boolean elytraFlying; - public int jumpTicks; - public boolean jumpQueued; - public int fireworkRocketDuration; - - // Effects - public int jumpBoost; - public int speed; - public int slowness; - public int dolphinsGrace; - public int slowFalling; - public int levitation; - - // Inventory - public boolean elytraEquipped; - - public void updateData() { - pos = new MutableVector3d(entity.x(), entity.y(), entity.z()); - vel = new MutableVector3d(entity.motionX(), entity.motionY(), entity.motionZ()); - - var effectState = entity.effectState(); - jumpBoost = effectState.getEffectAmplifier(Effect.JUMP_BOOST); - speed = effectState.getEffectAmplifier(Effect.SPEED); - slowness = effectState.getEffectAmplifier(Effect.SLOWNESS); - dolphinsGrace = effectState.getEffectAmplifier(Effect.DOLPHINS_GRACE); - slowFalling = effectState.getEffectAmplifier(Effect.SLOW_FALLING); - levitation = effectState.getEffectAmplifier(Effect.LEVITATION); - - var chestItem = inventoryContainer.getChestplate().item(); - elytraEquipped = chestItem != null && chestItem.type() == ItemType.ELYTRA; - } - - public void applyData() { - entity.x(pos.x); - entity.y(pos.y); - entity.z(pos.z); - - entity.onGround(onGround); - - entity.motionX(vel.x); - entity.motionY(vel.y); - entity.motionZ(vel.z); - } -} diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/ControlState.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/ControlState.java similarity index 53% rename from server/src/main/java/com/soulfiremc/server/protocol/bot/movement/ControlState.java rename to server/src/main/java/com/soulfiremc/server/protocol/bot/state/ControlState.java index 33103bd42..44349e9df 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/movement/ControlState.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/ControlState.java @@ -1,25 +1,9 @@ -/* - * SoulFire - * Copyright (C) 2024 AlexProgrammerDE - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.soulfiremc.server.protocol.bot.movement; +package com.soulfiremc.server.protocol.bot.state; import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket; @Setter @Getter @@ -61,4 +45,8 @@ public void resetAll() { sneaking = false; flying = false; } + + public ServerboundPlayerInputPacket toServerboundPlayerInputPacket() { + return new ServerboundPlayerInputPacket(forward, backward, left, right, jumping, sneaking, sprinting); + } } diff --git a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/ClientEntity.java b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/ClientEntity.java index 95816dd0a..45c5dbcf6 100644 --- a/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/ClientEntity.java +++ b/server/src/main/java/com/soulfiremc/server/protocol/bot/state/entity/ClientEntity.java @@ -18,13 +18,11 @@ package com.soulfiremc.server.protocol.bot.state.entity; import com.soulfiremc.server.data.EntityType; +import com.soulfiremc.server.data.FluidTags; import com.soulfiremc.server.protocol.BotConnection; import com.soulfiremc.server.protocol.bot.SessionDataManager; import com.soulfiremc.server.protocol.bot.model.AbilitiesData; -import com.soulfiremc.server.protocol.bot.movement.BotMovementManager; -import com.soulfiremc.server.protocol.bot.movement.ControlState; -import com.soulfiremc.server.protocol.bot.movement.PhysicsData; -import com.soulfiremc.server.protocol.bot.movement.PlayerMovementState; +import com.soulfiremc.server.protocol.bot.state.ControlState; import com.soulfiremc.server.protocol.bot.state.Level; import com.soulfiremc.server.util.MathHelper; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; @@ -32,10 +30,9 @@ import lombok.Getter; import lombok.Setter; import org.geysermc.mcprotocollib.protocol.data.game.entity.EntityEvent; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerStatusOnlyPacket; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.*; import java.util.UUID; @@ -46,12 +43,10 @@ @Setter @EqualsAndHashCode(callSuper = true) public class ClientEntity extends Entity { - private final PhysicsData physics = new PhysicsData(); + private final AbilitiesData abilitiesData = new AbilitiesData(); private final BotConnection connection; private final SessionDataManager dataManager; private final ControlState controlState; - private final PlayerMovementState movementState; - private final BotMovementManager botMovementManager; private boolean showReducedDebug; private int opPermissionLevel; private double lastX = 0; @@ -61,7 +56,12 @@ public class ClientEntity extends Entity { private float lastXRot = 0; private boolean lastOnGround = false; private boolean lastHorizontalCollision = false; + private boolean wasShiftKeyDown = false; + private boolean wasSprinting = false; + private boolean noPhysics = false; + private boolean wasUnderwater = false; private int positionReminder = 0; + private ServerboundPlayerInputPacket lastSentInput = new ServerboundPlayerInputPacket(false, false, false, false, false, false, false); public ClientEntity( int entityId, UUID uuid, BotConnection connection, SessionDataManager dataManager, ControlState controlState, @@ -70,31 +70,59 @@ public ClientEntity( this.connection = connection; this.dataManager = dataManager; this.controlState = controlState; - this.movementState = - new PlayerMovementState(this, dataManager.inventoryManager().playerInventory()); - this.botMovementManager = new BotMovementManager(dataManager, movementState, this); } @Override public void tick() { - super.tick(); + playerTick(); - // Collect data for calculations - movementState.updateData(); + this.sendShiftKeyState(); - // Tick physics movement - if (level.isChunkPositionLoaded(this.blockX(), this.blockZ())) { - botMovementManager.tick(); + var keyPressPacket = this.controlState.toServerboundPlayerInputPacket(); + if (!keyPressPacket.equals(this.lastSentInput)) { + this.connection.sendPacket(keyPressPacket); + this.lastSentInput = keyPressPacket; } - // Apply calculated state - movementState.applyData(); - // Send position changes sendPositionChanges(); } + private void playerTick() { + this.noPhysics = this.isSpectator(); + if (this.isSpectator()) { + this.onGround(false); + } + + this.wasUnderwater = this.isEyeInFluid(FluidTags.WATER); + livingEntityTick(); + + var x = MathHelper.clamp(this.x(), -2.9999999E7, 2.9999999E7); + var z = MathHelper.clamp(this.z(), -2.9999999E7, 2.9999999E7); + if (x != this.x() || z != this.z()) { + this.setPosition(x, this.y(), z); + } + } + + private void livingEntityTick() { + super.tick(); + + this.aiStep(); + + if (this.isFallFlying()) { + this.fallFlyTicks++; + } else { + this.fallFlyTicks = 0; + } + + if (this.isSleeping()) { + this.xRot(0.0F); + } + } + public void sendPositionChanges() { + sendIsSprintingIfNeeded(); + // Detect whether anything changed var xDiff = x - lastX; var yDiff = y - lastY; @@ -105,7 +133,7 @@ public void sendPositionChanges() { MathHelper.lengthSquared(xDiff, yDiff, zDiff) > MathHelper.square(2.0E-4) || ++positionReminder >= 20; var sendRot = xRotDiff != 0.0 || yRotDiff != 0.0; - var sendStatus = onGround != lastOnGround || movementState.isCollidedHorizontally != lastHorizontalCollision; + var sendStatus = onGround != lastOnGround || horizontalCollision != lastHorizontalCollision; // Send position packets if changed if (sendPos && sendRot) { @@ -146,9 +174,6 @@ public double eyeHeight() { } public void sendPosRot() { - var onGround = movementState.onGround; - var horizontalCollision = movementState.isCollidedHorizontally; - lastOnGround = onGround; lastHorizontalCollision = horizontalCollision; @@ -165,9 +190,6 @@ public void sendPosRot() { } public void sendPos() { - var onGround = movementState.onGround; - var horizontalCollision = movementState.isCollidedHorizontally; - lastOnGround = onGround; lastHorizontalCollision = horizontalCollision; @@ -180,9 +202,6 @@ public void sendPos() { } public void sendRot() { - var onGround = movementState.onGround; - var horizontalCollision = movementState.isCollidedHorizontally; - lastOnGround = onGround; lastHorizontalCollision = horizontalCollision; @@ -193,25 +212,54 @@ public void sendRot() { } public void sendStatus() { - var onGround = movementState.onGround; - var horizontalCollision = movementState.isCollidedHorizontally; - lastOnGround = onGround; lastHorizontalCollision = horizontalCollision; connection.sendPacket(new ServerboundMovePlayerStatusOnlyPacket(onGround, horizontalCollision)); } + private void sendShiftKeyState() { + var sneaking = controlState.sneaking(); + if (sneaking != this.wasShiftKeyDown) { + this.connection.sendPacket(new ServerboundPlayerCommandPacket(entityId(), sneaking + ? PlayerState.START_SNEAKING + : PlayerState.STOP_SNEAKING)); + this.wasShiftKeyDown = sneaking; + } + } + + private void sendIsSprintingIfNeeded() { + var sprinting = controlState.sprinting(); + if (sprinting != this.wasSprinting) { + this.connection.sendPacket(new ServerboundPlayerCommandPacket(entityId(), sprinting + ? PlayerState.START_SPRINTING + : PlayerState.STOP_SPRINTING)); + this.wasSprinting = sprinting; + } + } + public AbilitiesData abilities() { return dataManager.abilitiesData(); } public void jump() { - movementState.jumpQueued = true; + jumpTriggerTime = 7; } @Override public float height() { return this.controlState.sneaking() ? 1.5F : 1.8F; } + + private boolean isSpectator() { + return false; // TODO + } + + private boolean isSleeping() { + return false; // TODO + } + + public boolean isUnderWater() { + return this.wasUnderwater; + } } 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 a4cd35fe4..f257826e1 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 @@ -36,6 +36,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.EntityEvent; import org.geysermc.mcprotocollib.protocol.data.game.entity.RotationOrigin; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; @Slf4j @@ -46,6 +48,8 @@ public abstract class Entity { private final EntityMetadataState metadataState = new EntityMetadataState(); private final EntityAttributeState attributeState = new EntityAttributeState(); private final EntityEffectState effectState = new EntityEffectState(); + private final Set> fluidOnEyes = new HashSet<>(); + public float fallDistance; private final int entityId; private final UUID uuid; private final EntityType entityType; @@ -60,6 +64,13 @@ public abstract class Entity { protected double motionY; protected double motionZ; protected boolean onGround; + protected int jumpTriggerTime; + protected boolean horizontalCollision; + protected boolean verticalCollision; + protected boolean verticalCollisionBelow; + protected boolean minorHorizontalCollision; + protected boolean isInPowderSnow; + protected boolean wasInPowderSnow; public Entity(int entityId, UUID uuid, EntityType entityType, Level level, @@ -127,6 +138,20 @@ public void setMotion(double motionX, double motionY, double motionZ) { } public void tick() { + this.baseTick(); + } + + public void baseTick() { + this.wasInPowderSnow = this.isInPowderSnow; + this.isInPowderSnow = false; + this.updateInWaterStateAndDoFluidPushing(); + this.updateFluidOnEyes(); + this.updateSwimming(); + + if (this.isInLava()) { + this.fallDistance *= 0.5F; + } + effectState.tick(); } @@ -178,15 +203,6 @@ public Vector3d eyePosition() { return Vector3d.from(x, y + eyeHeight(), z); } - public Vector3d rotationVector() { - var yRotRadians = (float) Math.toRadians(yRot); - var xRotRadians = (float) Math.toRadians(xRot); - var x = -Math.sin(yRotRadians) * Math.cos(xRotRadians); - var y = -Math.sin(xRotRadians); - var z = Math.cos(yRotRadians) * Math.cos(xRotRadians); - return Vector3d.from(x, y, z); - } - public Vector3i blockPos() { return Vector3i.from(blockX(), blockY(), blockZ()); }