From 685af975322016456b5a9ecaaf52604de2c35c12 Mon Sep 17 00:00:00 2001
From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com>
Date: Wed, 6 Nov 2024 08:41:03 +0100
Subject: [PATCH] Initial work on more exact abilities and new movement
---
.../soulfiremc/server/pathfinding/Costs.java | 4 +-
.../pathfinding/graph/PathConstraint.java | 2 +-
.../soulfiremc/server/plugins/POVServer.java | 70 +-
.../server/protocol/bot/BotControlAPI.java | 8 +-
.../protocol/bot/SessionDataManager.java | 27 +-
.../protocol/bot/model/AbilitiesData.java | 27 +-
.../bot/movement/BotMovementManager.java | 792 ------------------
.../bot/movement/BubbleColumnDrag.java | 20 -
.../bot/movement/MutableVector3d.java | 83 --
.../protocol/bot/movement/PhysicsData.java | 54 --
.../bot/movement/PlayerMovementState.java | 93 --
.../bot/{movement => state}/ControlState.java | 24 +-
.../bot/state/entity/ClientEntity.java | 122 ++-
.../protocol/bot/state/entity/Entity.java | 34 +-
14 files changed, 187 insertions(+), 1173 deletions(-)
delete mode 100644 server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BotMovementManager.java
delete mode 100644 server/src/main/java/com/soulfiremc/server/protocol/bot/movement/BubbleColumnDrag.java
delete mode 100644 server/src/main/java/com/soulfiremc/server/protocol/bot/movement/MutableVector3d.java
delete mode 100644 server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PhysicsData.java
delete mode 100644 server/src/main/java/com/soulfiremc/server/protocol/bot/movement/PlayerMovementState.java
rename server/src/main/java/com/soulfiremc/server/protocol/bot/{movement => state}/ControlState.java (53%)
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());
}