From 13373ec55df1efdfbe4a4b1a9d4f43b245ff8b74 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 09:34:05 +0200 Subject: [PATCH 01/28] chore: remove direction function in favor of a constructor --- .../org/cobalt/pathfinder/calculate/Path.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/Path.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/Path.kt index 90649c5..f6fe251 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/Path.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/Path.kt @@ -17,10 +17,10 @@ data class Path( } val keyNodes = mutableListOf(nodes.first()) - var lastDirection = direction(nodes[0], nodes[1]) + var lastDirection = Direction(nodes[0], nodes[1]) for (i in 2 until nodes.size) { - val currentDirection = direction(nodes[i - 1], nodes[i]) + val currentDirection = Direction(nodes[i - 1], nodes[i]) if (currentDirection != lastDirection) { keyNodes += nodes[i - 1] @@ -32,12 +32,11 @@ data class Path( return keyNodes } - private fun direction(a: PathNode, b: PathNode) = Direction( - (b.x - a.x).coerceIn(-1, 1), - (b.y - a.y).coerceIn(-1, 1), - (b.z - a.z).coerceIn(-1, 1) - ) - - private data class Direction(val dx: Int, val dy: Int, val dz: Int) - + private data class Direction(val dx: Int, val dy: Int, val dz: Int) { + constructor(a: PathNode, b: PathNode) : this( + (b.x - a.x).coerceIn(-1, 1), + (b.y - a.y).coerceIn(-1, 1), + (b.z - a.z).coerceIn(-1, 1) + ) + } } From 659058d4d71affd9e04b3a38baddb2df2dc65e56 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 09:37:20 +0200 Subject: [PATCH 02/28] chore: extract movement evaluation into seperate method --- .../calculate/path/AStarPathfinder.kt | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 2261243..9222b33 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -11,18 +11,17 @@ import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult class AStarPathfinder( - val startX: Int, - val startY: Int, - val startZ: Int, - val goal: Goal, - val movements: Array, - val returnBestNode: Boolean, + val startX: Int, + val startY: Int, + val startZ: Int, + val goal: Goal, + val movements: Array, + val returnBestNode: Boolean, ) { private val closedSet = Long2ObjectOpenHashMap() private var startTime = 0L - @Suppress("CognitiveComplexMethod") fun findPath(): Path? { val ctx = CalculationContext() val openSet = BinaryHeapOpenSet() @@ -59,33 +58,7 @@ class AStarPathfinder( return reconstruct(currentNode) } - for (move in movements) { - res.reset() - move.calculateCost(ctx, currentNode, res) - - if (res.cost >= ctx.infCost) { - continue - } - - val neighborCostSoFar = currentNode.costSoFar + res.cost - val neighborNode = getNode( - res.x, res.y, res.z, - PathNode.longHash(res.x, res.y, res.z) - ) - - if (neighborCostSoFar < neighborNode.costSoFar) { - neighborNode.parent = currentNode - neighborNode.costSoFar = neighborCostSoFar - neighborNode.totalCost = neighborCostSoFar + neighborNode.costToEnd - neighborNode.type = move.type - - if (neighborNode.heapPosition == -1) { - openSet.add(neighborNode) - } else { - openSet.relocate(neighborNode) - } - } - } + evaluateMovements(currentNode, ctx, res, openSet) } return if (returnBestNode) { @@ -95,6 +68,41 @@ class AStarPathfinder( } } + private fun evaluateMovements( + currentNode: PathNode, + ctx: CalculationContext, + res: MovementResult, + openSet: BinaryHeapOpenSet, + ) { + for (move in movements) { + res.reset() + move.calculateCost(ctx, currentNode, res) + + if (res.cost >= ctx.infCost) { + continue + } + + val neighborCostSoFar = currentNode.costSoFar + res.cost + val neighborNode = getNode( + res.x, res.y, res.z, + PathNode.longHash(res.x, res.y, res.z) + ) + + if (neighborCostSoFar < neighborNode.costSoFar) { + neighborNode.parent = currentNode + neighborNode.costSoFar = neighborCostSoFar + neighborNode.totalCost = neighborCostSoFar + neighborNode.costToEnd + neighborNode.type = move.type + + if (neighborNode.heapPosition == -1) { + openSet.add(neighborNode) + } else { + openSet.relocate(neighborNode) + } + } + } + } + fun getNode(x: Int, y: Int, z: Int, hash: Long): PathNode { var node: PathNode? = closedSet.get(hash) From 8fc3ee9ee4a20e05c0baa46c21442a1209c061bd Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 10:08:02 +0200 Subject: [PATCH 03/28] chore: make config an actual config and move Type in Movement to MovementType for clarity --- .../org/cobalt/command/impl/MainCommand.kt | 8 +--- .../org/cobalt/pathfinder/PathConfig.kt | 14 +------ .../org/cobalt/pathfinder/PathExecutor.kt | 39 ++++++++++++++----- .../cobalt/pathfinder/calculate/PathNode.kt | 6 +-- .../calculate/path/AStarPathfinder.kt | 2 +- .../cobalt/pathfinder/movement/Movement.kt | 7 +--- .../pathfinder/movement/MovementType.kt | 5 +++ .../movement/impl/fly/FlyAscendMovement.kt | 3 +- .../movement/impl/fly/FlyDescendMovement.kt | 3 +- .../movement/impl/fly/FlyDiagonalMovement.kt | 3 +- .../movement/impl/fly/FlyTraverseMovement.kt | 3 +- .../movement/impl/walk/AscendMovement.kt | 3 +- .../movement/impl/walk/DescendMovement.kt | 3 +- .../movement/impl/walk/DiagonalMovement.kt | 3 +- .../movement/impl/walk/TraverseMovement.kt | 3 +- .../pathfinder/state/impl/CalculatingState.kt | 17 +++++++- .../pathfinder/state/impl/PathingState.kt | 15 ++++--- 17 files changed, 83 insertions(+), 54 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementType.kt diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 8f5dc27..5379437 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -32,13 +32,7 @@ object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { @SubCommand fun goTo(x: Int, y: Int, z: Int, fly: Boolean) { - val mode = if (fly) PathMode.FLY else PathMode.WALK - val config = PathConfig( - goal = GoalBlock(x, y, z), - movements = mode.movements - ) - - PathExecutor.goTo(config) + PathExecutor.goTo(GoalBlock(x, y, z), fly) } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt index de6a840..d098284 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt @@ -1,19 +1,7 @@ package org.cobalt.pathfinder -import org.cobalt.pathfinder.calculate.PathMode -import org.cobalt.pathfinder.goal.Goal -import org.cobalt.pathfinder.movement.Movement - class PathConfig( - val goal: Goal, - val movements: Array = PathMode.WALK.movements, val shouldSprint: Boolean = true, val preferShifting: Boolean = false, val returnBestNode: Boolean = false, -) { - - val useFlyMovement = movements.any { - it.type == Movement.Type.FLY - } - -} +) diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt index cdce036..3a21595 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt @@ -11,6 +11,10 @@ import org.cobalt.event.impl.TickEvent import org.cobalt.event.impl.WorldEvent import org.cobalt.module.impl.misc.Debug import org.cobalt.pathfinder.calculate.Path +import org.cobalt.pathfinder.calculate.PathMode +import org.cobalt.pathfinder.goal.Goal +import org.cobalt.pathfinder.goal.GoalBlock +import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.impl.CalculatingState import org.cobalt.ui.theme.ThemeManager @@ -22,7 +26,10 @@ import org.cobalt.util.WorldRenderUtils object PathExecutor { var state: ExecutorState? = null - var config: PathConfig? = null + var config: PathConfig = PathConfig() + + var currentGoal: Goal? = null + var availableMovements: Array? = null var path: Path? = null var pathIndex: Int = 0 @@ -34,24 +41,37 @@ object PathExecutor { EventBus.register(this) } - fun goTo(config: PathConfig) { - stop() + fun goTo(goal: Goal, fly: Boolean = false) { + // Are we sure we want to do this? There's no warning or anything. + // I've kept it but with a debug message + if (running) { + ChatUtils.sendSystemMessage( + "Stopping current pathfinder because a new one started.", + MessageType.DEBUG + ) + stop() + } - if (config.useFlyMovement && !PlayerUtils.canFly) { + if (fly && !PlayerUtils.canFly) { ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") return } - val player = minecraft.player - if (player != null) { - player.input = pathInput - } else { + if (player == null) { + ChatUtils.sendSystemMessage( + "Tried running pathfinder, but minecraft.player is null!", + MessageType.DEBUG + ) return + } else { + player.input = pathInput } - this.config = config + availableMovements = if (fly) PathMode.FLY.movements else PathMode.WALK.movements + currentGoal = goal + this.running = true changeState(CalculatingState()) @@ -68,7 +88,6 @@ object PathExecutor { } running = false - config = null path = null pathIndex = 0 diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt index 4bdb7d0..f61eff7 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt @@ -3,7 +3,7 @@ package org.cobalt.pathfinder.calculate import net.minecraft.core.BlockPos import net.minecraft.world.phys.Vec3 import org.cobalt.pathfinder.goal.Goal -import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementType data class PathNode( val x: Int, @@ -16,7 +16,7 @@ data class PathNode( val costToEnd = goal.heuristic(x, y, z) var totalCost = 1.0 var heapPosition = -1 - var type = Movement.Type.WALK + var movementType = MovementType.WALK var parent: PathNode? = null val block: BlockPos = BlockPos(x, y, z) @@ -25,7 +25,7 @@ data class PathNode( val topCenterVec: Vec3 = Vec3(x + 0.5, y.toDouble(), z + 0.5) val isFly: Boolean - get() = type == Movement.Type.FLY + get() = movementType == MovementType.FLY override fun equals(other: Any?): Boolean { if (this === other) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 9222b33..6c63d69 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -92,7 +92,7 @@ class AStarPathfinder( neighborNode.parent = currentNode neighborNode.costSoFar = neighborCostSoFar neighborNode.totalCost = neighborCostSoFar + neighborNode.costToEnd - neighborNode.type = move.type + neighborNode.movementType = move.movementType if (neighborNode.heapPosition == -1) { openSet.add(neighborNode) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt index 04fe1dc..8635317 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt @@ -4,13 +4,8 @@ import org.cobalt.pathfinder.calculate.PathNode // TODO: Implement costs in each movement type (to improve path quality) abstract class Movement( - val type: Type, + val movementType: MovementType, ) { abstract fun calculateCost(ctx: CalculationContext, currNode: PathNode, res: MovementResult) - - enum class Type { - WALK, FLY - } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementType.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementType.kt new file mode 100644 index 0000000..997b201 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementType.kt @@ -0,0 +1,5 @@ +package org.cobalt.pathfinder.movement + +enum class MovementType { + WALK, FLY +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt index 0a11c74..eb10135 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt @@ -5,8 +5,9 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType -class FlyAscendMovement : Movement(Type.FLY) { +class FlyAscendMovement : Movement(MovementType.FLY) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt index 1d4d8e6..c1729fc 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt @@ -5,8 +5,9 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType -class FlyDescendMovement : Movement(Type.FLY) { +class FlyDescendMovement : Movement(MovementType.FLY) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt index 6fbe13d..08924f8 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt @@ -5,11 +5,12 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyDiagonalMovement( val dx: Int, val dz: Int, -) : Movement(Type.FLY) { +) : Movement(MovementType.FLY) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt index 970fd6d..df093cb 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt @@ -5,11 +5,12 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyTraverseMovement( val dx: Int, val dz: Int, -) : Movement(Type.FLY) { +) : Movement(MovementType.FLY) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt index 5143f9b..824a630 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt @@ -5,12 +5,13 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( val dx: Int, val dz: Int, -) : Movement(Type.WALK) { +) : Movement(MovementType.WALK) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt index 873e9a4..bb0bd7f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt @@ -5,12 +5,13 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( val dx: Int, val dz: Int, -) : Movement(Type.WALK) { +) : Movement(MovementType.WALK) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt index c755311..4b21376 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt @@ -5,11 +5,12 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class DiagonalMovement( val dx: Int, val dz: Int, -) : Movement(Type.WALK) { +) : Movement(MovementType.WALK) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt index f7c4150..9138f51 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt @@ -5,11 +5,12 @@ import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class TraverseMovement( val dx: Int, val dz: Int, -) : Movement(Type.WALK) { +) : Movement(MovementType.WALK) { override fun calculateCost( ctx: CalculationContext, diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt index 2135381..d752c96 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt @@ -13,10 +13,25 @@ class CalculatingState : ExecutorState() { override fun enter() { val config = PathExecutor.config ?: return val startPos = PlayerUtils.position + val goal = PathExecutor.currentGoal + val availableMovements = PathExecutor.availableMovements + + if (goal == null) { + ChatUtils.sendSystemMessage("Cannot calculate path, no goal set!") + PathExecutor.stop() + return + } + + if (availableMovements == null) { + ChatUtils.sendSystemMessage("Cannot calculate path, no movements set!") + PathExecutor.stop() + return + } val pathFinder = AStarPathfinder( startPos.x, startPos.y, startPos.z, - config.goal, config.movements, + goal, + availableMovements, config.returnBestNode ) diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index 5dc591a..3228671 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -10,9 +10,12 @@ import org.cobalt.dsl.centerVec import org.cobalt.module.impl.misc.Debug import org.cobalt.module.impl.misc.Rotations import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathExecutor.config import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.helper.PlayerInput import org.cobalt.pathfinder.movement.MovementHelper +import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.impl.fly.FlyAscendMovement import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.util.PlayerUtils import org.cobalt.util.RotationUtils @@ -23,7 +26,7 @@ import org.cobalt.util.rotation.RotationTarget class PathingState : ExecutorState() { private val path = PathExecutor.path - private val config = PathExecutor.config + private val movements = PathExecutor.availableMovements private val jumpDelay = Clock() private inline val pathIndex: Int @@ -35,7 +38,7 @@ class PathingState : ExecutorState() { } override fun onTick() { - if (path == null || config == null) { + if (path == null || movements == null) { PathExecutor.stop() return } @@ -63,7 +66,7 @@ class PathingState : ExecutorState() { } override fun onRender() { - if (path == null || config == null) { + if (path == null || movements == null) { PathExecutor.stop() return } @@ -105,9 +108,11 @@ class PathingState : ExecutorState() { } private fun shouldStartFlying(node: PathNode): Boolean { + requireNotNull(movements) + if ( !node.isFly || - !config!!.useFlyMovement || + !movements.contains(FlyAscendMovement.DEFAULT) || !PlayerUtils.canFly || PlayerUtils.isFlying ) { @@ -137,7 +142,7 @@ class PathingState : ExecutorState() { } if (!node.isFly) { - if (config!!.shouldSprint) input.sprint = true + if (config.shouldSprint) input.sprint = true if (shouldJump(playerPos, nodes, index)) input.jump = true } From 7a4bbb5f31d90ef0b16fde8384ceb53b8cfc3106 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 10:09:47 +0200 Subject: [PATCH 04/28] chore: remove magic number in max calculation time --- src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt | 1 + .../org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt index d098284..05e7919 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt @@ -4,4 +4,5 @@ class PathConfig( val shouldSprint: Boolean = true, val preferShifting: Boolean = false, val returnBestNode: Boolean = false, + val maxCalculationTime: Long = 5_000, ) diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 6c63d69..6c4315f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -2,6 +2,7 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import kotlin.time.Duration.Companion.milliseconds +import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.calculate.openset.BinaryHeapOpenSet @@ -40,7 +41,7 @@ class AStarPathfinder( var bestNode = startNode while (!openSet.isEmpty()) { - if (System.currentTimeMillis() - startTime >= 5_000) { + if (System.currentTimeMillis() - startTime >= PathExecutor.config.maxCalculationTime) { break } From 70dd937ea28c2bc62a661f0df60aac42171ffa7e Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 10:12:18 +0200 Subject: [PATCH 05/28] chore: make PathFinderConfig singleton --- src/main/kotlin/org/cobalt/command/impl/MainCommand.kt | 2 -- src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt | 8 -------- src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt | 2 -- src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt | 8 ++++++++ .../cobalt/pathfinder/calculate/path/AStarPathfinder.kt | 3 ++- .../org/cobalt/pathfinder/state/impl/CalculatingState.kt | 4 ++-- .../org/cobalt/pathfinder/state/impl/PathingState.kt | 5 ++--- 7 files changed, 14 insertions(+), 18 deletions(-) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 5379437..4079d5f 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -4,9 +4,7 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.command.Command import org.cobalt.command.annotation.DefaultHandler import org.cobalt.command.annotation.SubCommand -import org.cobalt.pathfinder.PathConfig import org.cobalt.pathfinder.PathExecutor -import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt deleted file mode 100644 index 05e7919..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.cobalt.pathfinder - -class PathConfig( - val shouldSprint: Boolean = true, - val preferShifting: Boolean = false, - val returnBestNode: Boolean = false, - val maxCalculationTime: Long = 5_000, -) diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt index 3a21595..4d121d3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt @@ -13,7 +13,6 @@ import org.cobalt.module.impl.misc.Debug import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.Goal -import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.impl.CalculatingState @@ -26,7 +25,6 @@ import org.cobalt.util.WorldRenderUtils object PathExecutor { var state: ExecutorState? = null - var config: PathConfig = PathConfig() var currentGoal: Goal? = null var availableMovements: Array? = null diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt new file mode 100644 index 0000000..dd7f60a --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt @@ -0,0 +1,8 @@ +package org.cobalt.pathfinder + +object PathFinderConfig { + var shouldSprint: Boolean = true + var preferShifting: Boolean = false + var returnBestNode: Boolean = false + var maxCalculationTime: Long = 5_000 +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 6c4315f..bf83d2d 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -3,6 +3,7 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import kotlin.time.Duration.Companion.milliseconds import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFinderConfig import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.calculate.openset.BinaryHeapOpenSet @@ -41,7 +42,7 @@ class AStarPathfinder( var bestNode = startNode while (!openSet.isEmpty()) { - if (System.currentTimeMillis() - startTime >= PathExecutor.config.maxCalculationTime) { + if (System.currentTimeMillis() - startTime >= PathFinderConfig.maxCalculationTime) { break } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt index d752c96..7903dc0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt @@ -1,6 +1,7 @@ package org.cobalt.pathfinder.state.impl import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFinderConfig import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.util.ChatUtils @@ -11,7 +12,6 @@ import org.cobalt.util.helper.Multithreading class CalculatingState : ExecutorState() { override fun enter() { - val config = PathExecutor.config ?: return val startPos = PlayerUtils.position val goal = PathExecutor.currentGoal val availableMovements = PathExecutor.availableMovements @@ -32,7 +32,7 @@ class CalculatingState : ExecutorState() { startPos.x, startPos.y, startPos.z, goal, availableMovements, - config.returnBestNode + PathFinderConfig.returnBestNode ) Multithreading.runAsync { diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index 3228671..3e9e729 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -10,11 +10,10 @@ import org.cobalt.dsl.centerVec import org.cobalt.module.impl.misc.Debug import org.cobalt.module.impl.misc.Rotations import org.cobalt.pathfinder.PathExecutor -import org.cobalt.pathfinder.PathExecutor.config +import org.cobalt.pathfinder.PathFinderConfig import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.helper.PlayerInput import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementType import org.cobalt.pathfinder.movement.impl.fly.FlyAscendMovement import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.util.PlayerUtils @@ -142,7 +141,7 @@ class PathingState : ExecutorState() { } if (!node.isFly) { - if (config.shouldSprint) input.sprint = true + if (PathFinderConfig.shouldSprint) input.sprint = true if (shouldJump(playerPos, nodes, index)) input.jump = true } From 7a293f435bb735e541246a47e85e2d745b6d0d13 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 10:23:16 +0200 Subject: [PATCH 06/28] chore: rename PathExecutor to PathFindingFacade for clarity on object purpose and handle path rendering in state implementations instead of the facade --- .../org/cobalt/command/impl/MainCommand.kt | 4 +- .../{PathExecutor.kt => PathFindingFacade.kt} | 44 +----------------- .../calculate/path/AStarPathfinder.kt | 1 - .../cobalt/pathfinder/helper/PathRenderer.kt | 46 +++++++++++++++++++ .../cobalt/pathfinder/state/ExecutorState.kt | 4 +- .../pathfinder/state/impl/CalculatingState.kt | 16 +++---- .../pathfinder/state/impl/PathingState.kt | 21 +++++---- .../pathfinder/state/impl/StartFlyState.kt | 10 ++-- 8 files changed, 79 insertions(+), 67 deletions(-) rename src/main/kotlin/org/cobalt/pathfinder/{PathExecutor.kt => PathFindingFacade.kt} (70%) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 4079d5f..2ebe50a 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -4,7 +4,7 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.command.Command import org.cobalt.command.annotation.DefaultHandler import org.cobalt.command.annotation.SubCommand -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen @@ -30,7 +30,7 @@ object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { @SubCommand fun goTo(x: Int, y: Int, z: Int, fly: Boolean) { - PathExecutor.goTo(GoalBlock(x, y, z), fly) + PathFindingFacade.goTo(GoalBlock(x, y, z), fly) } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt similarity index 70% rename from src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt rename to src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt index 4d121d3..014f268 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt @@ -1,29 +1,22 @@ package org.cobalt.pathfinder -import java.awt.Color import net.minecraft.client.player.KeyboardInput import org.cobalt.Cobalt.minecraft -import org.cobalt.dsl.centerVec -import org.cobalt.dsl.smallBox import org.cobalt.event.EventBus import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.TickEvent import org.cobalt.event.impl.WorldEvent -import org.cobalt.module.impl.misc.Debug import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.Goal import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.impl.CalculatingState -import org.cobalt.ui.theme.ThemeManager import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType import org.cobalt.util.PlayerUtils -import org.cobalt.util.WorldRenderUtils - -object PathExecutor { +object PathFindingFacade { var state: ExecutorState? = null var currentGoal: Goal? = null @@ -129,40 +122,7 @@ object PathExecutor { state?.onRender() - if (state is CalculatingState) { - return - } - - val theme = ThemeManager.activeTheme - val nodes = path?.nodes ?: return - val keyNodes = path?.keyNodes ?: return - - val targetNode = nodes[pathIndex].centerVec - val playerPos = PlayerUtils.position.centerVec() - - if (Debug.enabled) { - WorldRenderUtils.drawBox(playerPos.smallBox(), Color.GREEN) - WorldRenderUtils.drawBox(targetNode.smallBox(), Color.RED) - } - - for (index in keyNodes.indices) { - val node = keyNodes[index] - - WorldRenderUtils.drawBlockPos( - if (node.isFly) node.block else node.blockStandingOn, - color = theme.accentPrimary - ) - - if (index > 0) { - val prev = keyNodes[index - 1] - - WorldRenderUtils.drawLine( - if (prev.isFly) prev.centerVec else prev.topCenterVec, - if (node.isFly) node.centerVec else node.topCenterVec, - theme.accentSecondary - ) - } - } +// PathRenderer.render() } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index bf83d2d..e51f822 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -2,7 +2,6 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import kotlin.time.Duration.Companion.milliseconds -import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.PathFinderConfig import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathNode diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt new file mode 100644 index 0000000..42e5dda --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt @@ -0,0 +1,46 @@ +package org.cobalt.pathfinder.helper + +import java.awt.Color +import org.cobalt.dsl.centerVec +import org.cobalt.dsl.smallBox +import org.cobalt.module.impl.misc.Debug +import org.cobalt.pathfinder.PathFindingFacade.path +import org.cobalt.pathfinder.PathFindingFacade.pathIndex +import org.cobalt.ui.theme.ThemeManager +import org.cobalt.util.PlayerUtils +import org.cobalt.util.WorldRenderUtils + +object PathRenderer { + fun render() { + val theme = ThemeManager.activeTheme + val nodes = path?.nodes ?: return + val keyNodes = path?.keyNodes ?: return + + val targetNode = nodes[pathIndex].centerVec + val playerPos = PlayerUtils.position.centerVec() + + if (Debug.enabled) { + WorldRenderUtils.drawBox(playerPos.smallBox(), Color.GREEN) + WorldRenderUtils.drawBox(targetNode.smallBox(), Color.RED) + } + + for (index in keyNodes.indices) { + val node = keyNodes[index] + + WorldRenderUtils.drawBlockPos( + if (node.isFly) node.block else node.blockStandingOn, + color = theme.accentPrimary + ) + + if (index > 0) { + val prev = keyNodes[index - 1] + + WorldRenderUtils.drawLine( + if (prev.isFly) prev.centerVec else prev.topCenterVec, + if (node.isFly) node.centerVec else node.topCenterVec, + theme.accentSecondary + ) + } + } + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt index 7d6d22d..fd3c7ff 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt @@ -1,12 +1,12 @@ package org.cobalt.pathfinder.state -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.PathInput abstract class ExecutorState { protected val input: PathInput = - PathExecutor.pathInput + PathFindingFacade.pathInput open fun enter() {} open fun onTick() {} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt index 7903dc0..aa633ed 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt @@ -1,6 +1,6 @@ package org.cobalt.pathfinder.state.impl -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.PathFinderConfig import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState @@ -13,18 +13,18 @@ class CalculatingState : ExecutorState() { override fun enter() { val startPos = PlayerUtils.position - val goal = PathExecutor.currentGoal - val availableMovements = PathExecutor.availableMovements + val goal = PathFindingFacade.currentGoal + val availableMovements = PathFindingFacade.availableMovements if (goal == null) { ChatUtils.sendSystemMessage("Cannot calculate path, no goal set!") - PathExecutor.stop() + PathFindingFacade.stop() return } if (availableMovements == null) { ChatUtils.sendSystemMessage("Cannot calculate path, no movements set!") - PathExecutor.stop() + PathFindingFacade.stop() return } @@ -40,12 +40,12 @@ class CalculatingState : ExecutorState() { if (path == null) { ChatUtils.sendSystemMessage("Unable to find a path") - PathExecutor.stop() + PathFindingFacade.stop() return@runAsync } - PathExecutor.path = path - PathExecutor.changeState(PathingState()) + PathFindingFacade.path = path + PathFindingFacade.changeState(PathingState()) ChatUtils.sendSystemMessage( "Found ${path.nodes.size} node path in ${path.timeElapsed.inWholeMilliseconds}ms", diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index 3e9e729..72564ee 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -9,9 +9,10 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.dsl.centerVec import org.cobalt.module.impl.misc.Debug import org.cobalt.module.impl.misc.Rotations -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.PathFinderConfig import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.helper.PathRenderer import org.cobalt.pathfinder.helper.PlayerInput import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.impl.fly.FlyAscendMovement @@ -24,12 +25,12 @@ import org.cobalt.util.rotation.RotationTarget class PathingState : ExecutorState() { - private val path = PathExecutor.path - private val movements = PathExecutor.availableMovements + private val path = PathFindingFacade.path + private val movements = PathFindingFacade.availableMovements private val jumpDelay = Clock() private inline val pathIndex: Int - get() = PathExecutor.pathIndex + get() = PathFindingFacade.pathIndex override fun exit() { input.stopMovement() @@ -38,7 +39,7 @@ class PathingState : ExecutorState() { override fun onTick() { if (path == null || movements == null) { - PathExecutor.stop() + PathFindingFacade.stop() return } @@ -66,10 +67,12 @@ class PathingState : ExecutorState() { override fun onRender() { if (path == null || movements == null) { - PathExecutor.stop() + PathFindingFacade.stop() return } + PathRenderer.render() + if (!Debug.enabled) { return } @@ -96,11 +99,11 @@ class PathingState : ExecutorState() { pathIndex + 1 < nodes.size && hasReached(playerPos, nodes, pathIndex) ) { - PathExecutor.pathIndex++ + PathFindingFacade.pathIndex++ } if (pathIndex + 1 >= nodes.size) { - PathExecutor.stop() + PathFindingFacade.stop() } return true @@ -118,7 +121,7 @@ class PathingState : ExecutorState() { return false } - PathExecutor.changeState(StartFlyState()) + PathFindingFacade.changeState(StartFlyState()) return true } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt index d24e6fe..c080fd5 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt @@ -1,7 +1,7 @@ package org.cobalt.pathfinder.state.impl -import org.cobalt.Cobalt.minecraft -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.helper.PathRenderer import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.util.PlayerUtils @@ -18,7 +18,11 @@ class StartFlyState : ExecutorState() { return } - PathExecutor.changeState(PathingState()) + PathFindingFacade.changeState(PathingState()) + } + + override fun onRender() { + PathRenderer.render() } private fun handleFlyStart(): Boolean { From e20765395f9bb476fc88f74d70163611ac372223 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 10:32:52 +0200 Subject: [PATCH 07/28] chore: cleanup pathrendering method to reduce nesting --- .../cobalt/pathfinder/helper/PathRenderer.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt index 42e5dda..d503cd3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt @@ -32,15 +32,15 @@ object PathRenderer { color = theme.accentPrimary ) - if (index > 0) { - val prev = keyNodes[index - 1] - - WorldRenderUtils.drawLine( - if (prev.isFly) prev.centerVec else prev.topCenterVec, - if (node.isFly) node.centerVec else node.topCenterVec, - theme.accentSecondary - ) - } + if (index <= 0) continue + + val prev = keyNodes[index - 1] + + WorldRenderUtils.drawLine( + if (prev.isFly) prev.centerVec else prev.topCenterVec, + if (node.isFly) node.centerVec else node.topCenterVec, + theme.accentSecondary + ) } } } From ac0ab217e9d18952f7ff0fbc36a19e9ebfe606c2 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 10:40:30 +0200 Subject: [PATCH 08/28] chore: cleanup shouldJump --- .../pathfinder/state/impl/PathingState.kt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index 72564ee..e2b5a7b 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -204,7 +204,8 @@ class PathingState : ExecutorState() { nodes: List, currentIndex: Int, ): Boolean { - val level = minecraft.level ?: return false + val level = minecraft.level + requireNotNull(level) if (!PlayerUtils.onGround || !jumpDelay.passed()) { return false @@ -212,11 +213,9 @@ class PathingState : ExecutorState() { val node = nodes[currentIndex] - if (node.block.y - playerPos.y < 1) { - return false - } - - if (MovementHelper.isBottomSlab(level.getBlockState(node.blockStandingOn))) { + if (node.block.y - playerPos.y < 1 || + MovementHelper.isBottomSlab(level.getBlockState(node.blockStandingOn)) + ) { return false } @@ -225,11 +224,7 @@ class PathingState : ExecutorState() { val dx = abs(nodeCenter.x - playerVec.x) val dz = abs(nodeCenter.z - playerVec.z) - if (dx + dz > 1.2) { - return false - } - - if (minOf(dx, dz) > 0.2) { + if (dx + dz > 1.2 || minOf(dx, dz) > 0.2) { return false } From 4ec3cac26a4a92d999b88855f0234c7330c24120 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 11:20:29 +0200 Subject: [PATCH 09/28] chore: remove redundant checks in shouldStartFlying --- .../kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index e2b5a7b..f46be73 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -114,8 +114,6 @@ class PathingState : ExecutorState() { if ( !node.isFly || - !movements.contains(FlyAscendMovement.DEFAULT) || - !PlayerUtils.canFly || PlayerUtils.isFlying ) { return false From 7fa7b2251239f6ac1aec9637f2496aa59925505a Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 11:37:25 +0200 Subject: [PATCH 10/28] feat: add command to stop pathfinder --- src/main/kotlin/org/cobalt/command/impl/MainCommand.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 2ebe50a..94593f3 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -33,4 +33,9 @@ object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { PathFindingFacade.goTo(GoalBlock(x, y, z), fly) } + @SubCommand + fun stopPathfinder() { + PathFindingFacade.stop() + } + } From f4d688a65cb276c39f6349fc91a59f2c5286cb99 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 11:54:47 +0200 Subject: [PATCH 11/28] feat: made fly pathfinder work (technically) --- .../org/cobalt/pathfinder/PathFinderConfig.kt | 1 + .../pathfinder/movement/MovementHelper.kt | 81 ++++++++++++++++--- .../pathfinder/state/impl/PathingState.kt | 44 ++++++---- 3 files changed, 101 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt index dd7f60a..b254e0f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt @@ -5,4 +5,5 @@ object PathFinderConfig { var preferShifting: Boolean = false var returnBestNode: Boolean = false var maxCalculationTime: Long = 5_000 + var hasReachedThreshold: Double = 0.09 // // = sqrt(0.3), so 0.3 blocks of distance is the threshold } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt index 27410c1..3ecdcaf 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt @@ -21,6 +21,39 @@ import org.cobalt.pathfinder.precompute.Ternary import org.cobalt.pathfinder.precompute.Ternary.* object MovementHelper { + private const val DIRECTIONS_COUNT = 8 + private const val FULL_CIRCLE_DEGREES = 360f + private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT // 45f + private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f // 22.5f + + private const val DEAD_BAND_DEGREES = 0.05f + private const val SWITCH_THRESHOLD_DEGREES = SECTOR_HALF_WIDTH_DEGREES + DEAD_BAND_DEGREES // 27.5f + + private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES // 22.5f + private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES // 67.5f + private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES // 112.5f + private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES // 157.5f + + private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES // -22.5f + private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES // -67.5f + private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES // -112.5f + private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES // -157.5f + + private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val BACK_RIGHT_HOLD_DEGREES = BACK_RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val BACKWARD_HOLD_DEGREES = BACKWARD_START_DEGREES + DEAD_BAND_DEGREES + + private const val FORWARD_LEFT_HOLD_DEGREES = FORWARD_LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val LEFT_HOLD_DEGREES = LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES + + // --- Sector identity ---------------------------------------------------- + private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } + + // Persists between calls so we can apply hysteresis. + private var lastSector: Sector = Sector.FORWARD @JvmStatic fun canWalkOn( @@ -308,16 +341,46 @@ object MovementHelper { fun getNeededKeys(playerYaw: Float, idealYaw: Float): PlayerInput { val diff = Mth.wrapDegrees(idealYaw - playerYaw) - return when { - diff >= -22.5f && diff < 22.5f -> PlayerInput(forward = true) - diff in 22.5f..<67.5f -> PlayerInput(forward = true, right = true) - diff in 67.5f..<112.5f -> PlayerInput(right = true) - diff in 112.5f..<157.5f -> PlayerInput(backward = true, right = true) - diff >= 157.5f || diff < -157.5f -> PlayerInput(backward = true) - diff >= -157.5f && diff < -112.5f -> PlayerInput(backward = true, left = true) - diff >= -112.5f && diff < -67.5f -> PlayerInput(left = true) - else -> PlayerInput(forward = true, left = true) + if (staysInSector(lastSector, diff)) { + return sectorToInput(lastSector) } + + val newSector = classify(diff) + lastSector = newSector + return sectorToInput(newSector) + } + + private fun staysInSector(sector: Sector, diff: Float): Boolean = when (sector) { + Sector.FORWARD -> diff in FORWARD_LEFT_HOLD_DEGREES.. diff >= FORWARD_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < RIGHT_HOLD_DEGREES + Sector.RIGHT -> diff >= RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_RIGHT_HOLD_DEGREES + Sector.BACK_RIGHT -> diff >= BACK_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACKWARD_HOLD_DEGREES + Sector.BACKWARD -> diff >= BACKWARD_START_DEGREES - DEAD_BAND_DEGREES || diff < BACKWARD_NEG_HOLD_DEGREES + Sector.BACK_LEFT -> diff >= BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_LEFT_HOLD_DEGREES + Sector.LEFT -> diff >= BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < LEFT_HOLD_DEGREES + Sector.FORWARD_LEFT -> diff >= LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < FORWARD_LEFT_HOLD_DEGREES + } + + private fun classify(diff: Float): Sector = when { + diff in FORWARD_LEFT_START_DEGREES.. Sector.FORWARD + diff in FORWARD_RIGHT_START_DEGREES.. Sector.FORWARD_RIGHT + diff in RIGHT_START_DEGREES.. Sector.RIGHT + diff in BACK_RIGHT_START_DEGREES.. Sector.BACK_RIGHT + diff !in BACKWARD_NEG_START_DEGREES.. Sector.BACKWARD + diff in BACKWARD_NEG_START_DEGREES.. Sector.BACK_LEFT + diff in BACK_LEFT_START_DEGREES.. Sector.LEFT + else -> Sector.FORWARD_LEFT + } + + private fun sectorToInput(sector: Sector): PlayerInput = when (sector) { + Sector.FORWARD -> PlayerInput(forward = true) + Sector.FORWARD_RIGHT -> PlayerInput(forward = true, right = true) + Sector.RIGHT -> PlayerInput(right = true) + Sector.BACK_RIGHT -> PlayerInput(backward = true, right = true) + Sector.BACKWARD -> PlayerInput(backward = true) + Sector.BACK_LEFT -> PlayerInput(backward = true, left = true) + Sector.LEFT -> PlayerInput(left = true) + Sector.FORWARD_LEFT -> PlayerInput(forward = true, left = true) } @JvmStatic diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index f46be73..f3ef906 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -17,6 +17,7 @@ import org.cobalt.pathfinder.helper.PlayerInput import org.cobalt.pathfinder.movement.MovementHelper import org.cobalt.pathfinder.movement.impl.fly.FlyAscendMovement import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.util.ChatUtils import org.cobalt.util.PlayerUtils import org.cobalt.util.RotationUtils import org.cobalt.util.WorldRenderUtils @@ -132,17 +133,31 @@ class PathingState : ExecutorState() { val index = pathIndex val input = PlayerInput() - if (!node.isFly || !sameXZ) { + if (node.isFly && sameXZ) { + val diffY = node.block.y - playerPos.y + when { + diffY > 0 -> input.jump = true + diffY < 0 -> input.sneak = true + } + + ChatUtils.sendSystemMessage("Input: $input") + if (input.jump || input.sneak) return input + ChatUtils.sendSystemMessage("Next") + } + + if (!sameXZ) { val neededKeys = MovementHelper.getNeededKeys( PlayerUtils.rotation.yaw, RotationUtils.getRotation(node.centerVec).yaw ) + ChatUtils.sendSystemMessage("Need keys: $neededKeys") input.apply(neededKeys) } + if (PathFinderConfig.shouldSprint) input.sprint = true + if (!node.isFly) { - if (PathFinderConfig.shouldSprint) input.sprint = true if (shouldJump(playerPos, nodes, index)) input.jump = true } @@ -162,27 +177,24 @@ class PathingState : ExecutorState() { nodes: List, currentIndex: Int, ): Boolean { - val level = minecraft.level ?: return false val node = nodes[currentIndex] - if (node.isFly) { - return false - } +// if (node.isFly) return false val nodeCenter = node.centerVec val playerVec = playerPos.centerVec() - if (playerVec.distanceToSqr(nodeCenter) < 0.3 * 0.3) { - return true - } + if (playerVec.distanceToSqr(nodeCenter) < PathFinderConfig.hasReachedThreshold) return true - if (currentIndex + 1 >= nodes.size) { - return false - } + if (currentIndex + 1 >= nodes.size) return false - val isSlab = MovementHelper.isBottomSlab(level.getBlockState(node.blockStandingOn)) + val level = minecraft.level + requireNotNull(level) - if (!isSlab && (node.block.y > playerPos.y || !PlayerUtils.onGround)) { + if ( + !MovementHelper.isBottomSlab(level.getBlockState(node.blockStandingOn)) && + (node.block.y > playerPos.y || !PlayerUtils.onGround) + ) { return false } @@ -193,8 +205,8 @@ class PathingState : ExecutorState() { return false } - val perpDistSq = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() - return perpDistSq < 1.0 + val squaredPerpendicularDistance = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() + return squaredPerpendicularDistance < 1.0 } private fun shouldJump( From 5e678d030af16b87b80805a843c333ea48eaa3e8 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 11:55:34 +0200 Subject: [PATCH 12/28] chore: remove some crappy claude comments --- .../pathfinder/movement/MovementHelper.kt | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt index 3ecdcaf..c625f0e 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt @@ -23,21 +23,20 @@ import org.cobalt.pathfinder.precompute.Ternary.* object MovementHelper { private const val DIRECTIONS_COUNT = 8 private const val FULL_CIRCLE_DEGREES = 360f - private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT // 45f - private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f // 22.5f + private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT + private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f private const val DEAD_BAND_DEGREES = 0.05f - private const val SWITCH_THRESHOLD_DEGREES = SECTOR_HALF_WIDTH_DEGREES + DEAD_BAND_DEGREES // 27.5f - private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES // 22.5f - private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES // 67.5f - private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES // 112.5f - private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES // 157.5f + private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES + private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES // -22.5f - private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES // -67.5f - private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES // -112.5f - private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES // -157.5f + private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES + private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES + private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES + private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES @@ -49,10 +48,7 @@ object MovementHelper { private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES - // --- Sector identity ---------------------------------------------------- private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } - - // Persists between calls so we can apply hysteresis. private var lastSector: Sector = Sector.FORWARD @JvmStatic From 412c3c439dfd745fe2a2ab66ef21c65cef14e97e Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 11:56:25 +0200 Subject: [PATCH 13/28] chore: remove debug messages --- .../kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index f3ef906..9365a02 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -140,9 +140,7 @@ class PathingState : ExecutorState() { diffY < 0 -> input.sneak = true } - ChatUtils.sendSystemMessage("Input: $input") if (input.jump || input.sneak) return input - ChatUtils.sendSystemMessage("Next") } if (!sameXZ) { @@ -151,7 +149,6 @@ class PathingState : ExecutorState() { RotationUtils.getRotation(node.centerVec).yaw ) - ChatUtils.sendSystemMessage("Need keys: $neededKeys") input.apply(neededKeys) } @@ -179,8 +176,6 @@ class PathingState : ExecutorState() { ): Boolean { val node = nodes[currentIndex] -// if (node.isFly) return false - val nodeCenter = node.centerVec val playerVec = playerPos.centerVec() From a8372f6c57b3462372b15cb9b564ec242cc669cc Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 11:59:46 +0200 Subject: [PATCH 14/28] chore: rename PathFinderConfig to PathFindingConfig for consistency --- .../{PathFinderConfig.kt => PathFindingConfig.kt} | 2 +- .../cobalt/pathfinder/calculate/path/AStarPathfinder.kt | 4 ++-- .../org/cobalt/pathfinder/state/impl/CalculatingState.kt | 4 ++-- .../org/cobalt/pathfinder/state/impl/PathingState.kt | 8 +++----- 4 files changed, 8 insertions(+), 10 deletions(-) rename src/main/kotlin/org/cobalt/pathfinder/{PathFinderConfig.kt => PathFindingConfig.kt} (91%) diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt similarity index 91% rename from src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt rename to src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt index b254e0f..e22c917 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFinderConfig.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt @@ -1,6 +1,6 @@ package org.cobalt.pathfinder -object PathFinderConfig { +object PathFindingConfig { var shouldSprint: Boolean = true var preferShifting: Boolean = false var returnBestNode: Boolean = false diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index e51f822..54da6a0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -2,7 +2,7 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import kotlin.time.Duration.Companion.milliseconds -import org.cobalt.pathfinder.PathFinderConfig +import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.calculate.openset.BinaryHeapOpenSet @@ -41,7 +41,7 @@ class AStarPathfinder( var bestNode = startNode while (!openSet.isEmpty()) { - if (System.currentTimeMillis() - startTime >= PathFinderConfig.maxCalculationTime) { + if (System.currentTimeMillis() - startTime >= PathFindingConfig.maxCalculationTime) { break } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt index aa633ed..b958c3d 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt @@ -1,7 +1,7 @@ package org.cobalt.pathfinder.state.impl import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.PathFinderConfig +import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.util.ChatUtils @@ -32,7 +32,7 @@ class CalculatingState : ExecutorState() { startPos.x, startPos.y, startPos.z, goal, availableMovements, - PathFinderConfig.returnBestNode + PathFindingConfig.returnBestNode ) Multithreading.runAsync { diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index 9365a02..950a4e3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -10,14 +10,12 @@ import org.cobalt.dsl.centerVec import org.cobalt.module.impl.misc.Debug import org.cobalt.module.impl.misc.Rotations import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.PathFinderConfig +import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.helper.PathRenderer import org.cobalt.pathfinder.helper.PlayerInput import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.impl.fly.FlyAscendMovement import org.cobalt.pathfinder.state.ExecutorState -import org.cobalt.util.ChatUtils import org.cobalt.util.PlayerUtils import org.cobalt.util.RotationUtils import org.cobalt.util.WorldRenderUtils @@ -152,7 +150,7 @@ class PathingState : ExecutorState() { input.apply(neededKeys) } - if (PathFinderConfig.shouldSprint) input.sprint = true + if (PathFindingConfig.shouldSprint) input.sprint = true if (!node.isFly) { if (shouldJump(playerPos, nodes, index)) input.jump = true @@ -179,7 +177,7 @@ class PathingState : ExecutorState() { val nodeCenter = node.centerVec val playerVec = playerPos.centerVec() - if (playerVec.distanceToSqr(nodeCenter) < PathFinderConfig.hasReachedThreshold) return true + if (playerVec.distanceToSqr(nodeCenter) < PathFindingConfig.hasReachedThreshold) return true if (currentIndex + 1 >= nodes.size) return false From 3bba3e0718c575ed03190e3141981f7123c5bbe7 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 12:27:57 +0200 Subject: [PATCH 15/28] chore: clean up AStar like... a lot --- .../cobalt/pathfinder/calculate/PathNode.kt | 11 +- .../calculate/path/AStarPathfinder.kt | 100 +++++++++--------- .../cobalt/pathfinder/movement/Movement.kt | 2 +- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt index f61eff7..dfac851 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt @@ -10,7 +10,7 @@ data class PathNode( val y: Int, val z: Int, val goal: Goal, -) { +) : Comparable { var costSoFar = 1e6 val costToEnd = goal.heuristic(x, y, z) @@ -27,6 +27,15 @@ data class PathNode( val isFly: Boolean get() = movementType == MovementType.FLY + override fun compareTo(other: PathNode): Int { + return compareValuesBy( + this, + other, + PathNode::costToEnd, + PathNode::costSoFar, + ) + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 54da6a0..bbdb6b2 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -20,50 +20,41 @@ class AStarPathfinder( val returnBestNode: Boolean, ) { - private val closedSet = Long2ObjectOpenHashMap() - private var startTime = 0L + private val nodeCache = Long2ObjectOpenHashMap() fun findPath(): Path? { - val ctx = CalculationContext() + val calculationContext = CalculationContext() val openSet = BinaryHeapOpenSet() - val res = MovementResult() + val movementResult = MovementResult() val startNode = PathNode( startX, startY, startZ, goal - ).also { - it.costSoFar = 0.0 - it.totalCost = it.costToEnd - } + ) + + startNode.costSoFar = 0.0 + startNode.totalCost = startNode.costToEnd openSet.add(startNode) - startTime = System.currentTimeMillis() + val startTime = System.nanoTime() var bestNode = startNode - while (!openSet.isEmpty()) { - if (System.currentTimeMillis() - startTime >= PathFindingConfig.maxCalculationTime) { - break - } + val deadline = startTime + PathFindingConfig.maxCalculationTime + while (!openSet.isEmpty() && System.nanoTime() < deadline) { val currentNode = openSet.poll() - if ( - currentNode.costToEnd < bestNode.costToEnd || - (currentNode.costToEnd == bestNode.costToEnd && - currentNode.costSoFar < bestNode.costSoFar) - ) { - bestNode = currentNode - } + if (currentNode < bestNode) bestNode = currentNode if (goal.isAtGoal(currentNode.x, currentNode.y, currentNode.z)) { - return reconstruct(currentNode) + return reconstruct(currentNode, startTime) } - evaluateMovements(currentNode, ctx, res, openSet) + evaluateMovements(currentNode, calculationContext, movementResult, openSet) } return if (returnBestNode) { - reconstruct(bestNode) + reconstruct(bestNode, startTime) } else { null } @@ -71,51 +62,56 @@ class AStarPathfinder( private fun evaluateMovements( currentNode: PathNode, - ctx: CalculationContext, - res: MovementResult, + calculationContext: CalculationContext, + movementResult: MovementResult, openSet: BinaryHeapOpenSet, ) { for (move in movements) { - res.reset() - move.calculateCost(ctx, currentNode, res) + movementResult.reset() + move.calculateCost(calculationContext, currentNode, movementResult) - if (res.cost >= ctx.infCost) { - continue - } + if (movementResult.cost < calculationContext.infCost) relaxNeighbor(currentNode, move, movementResult, openSet) + } + } - val neighborCostSoFar = currentNode.costSoFar + res.cost - val neighborNode = getNode( - res.x, res.y, res.z, - PathNode.longHash(res.x, res.y, res.z) - ) - - if (neighborCostSoFar < neighborNode.costSoFar) { - neighborNode.parent = currentNode - neighborNode.costSoFar = neighborCostSoFar - neighborNode.totalCost = neighborCostSoFar + neighborNode.costToEnd - neighborNode.movementType = move.movementType - - if (neighborNode.heapPosition == -1) { - openSet.add(neighborNode) - } else { - openSet.relocate(neighborNode) - } - } + private fun relaxNeighbor( + currentNode: PathNode, + movement: Movement, + movementResult: MovementResult, + openSet: BinaryHeapOpenSet + ) { + val neighborCostSoFar = currentNode.costSoFar + movementResult.cost + val neighborNode = getNode( + movementResult.x, movementResult.y, movementResult.z, + PathNode.longHash(movementResult.x, movementResult.y, movementResult.z) + ) + + if (neighborCostSoFar >= neighborNode.costSoFar) return + + neighborNode.parent = currentNode + neighborNode.costSoFar = neighborCostSoFar + neighborNode.totalCost = neighborCostSoFar + neighborNode.costToEnd + neighborNode.movementType = movement.type + + if (neighborNode.heapPosition == -1) { + openSet.add(neighborNode) + } else { + openSet.relocate(neighborNode) } } fun getNode(x: Int, y: Int, z: Int, hash: Long): PathNode { - var node: PathNode? = closedSet.get(hash) + var node: PathNode? = nodeCache.get(hash) if (node == null) { node = PathNode(x, y, z, goal) - closedSet.put(hash, node) + nodeCache.put(hash, node) } return node } - private fun reconstruct(endNode: PathNode): Path { + private fun reconstruct(endNode: PathNode, startTime: Long): Path { val path = mutableListOf() var node: PathNode? = endNode @@ -126,7 +122,7 @@ class AStarPathfinder( return Path( nodes = path, - timeElapsed = (System.currentTimeMillis() - startTime).milliseconds, + timeElapsed = (System.nanoTime() - startTime).milliseconds, goal = goal ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt index 8635317..fbf61f0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/Movement.kt @@ -4,7 +4,7 @@ import org.cobalt.pathfinder.calculate.PathNode // TODO: Implement costs in each movement type (to improve path quality) abstract class Movement( - val movementType: MovementType, + val type: MovementType, ) { abstract fun calculateCost(ctx: CalculationContext, currNode: PathNode, res: MovementResult) From 7ff959e423d3bda49991963992a771eae5a3ab61 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 12:43:06 +0200 Subject: [PATCH 16/28] chore: make AStar a singleton object --- .../calculate/path/AStarPathfinder.kt | 39 ++++++++++--------- .../pathfinder/state/impl/CalculatingState.kt | 10 +---- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index bbdb6b2..2d7ec54 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -10,26 +10,25 @@ import org.cobalt.pathfinder.goal.Goal import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.util.PlayerUtils -class AStarPathfinder( - val startX: Int, - val startY: Int, - val startZ: Int, - val goal: Goal, - val movements: Array, - val returnBestNode: Boolean, -) { - +object AStarPathfinder { private val nodeCache = Long2ObjectOpenHashMap() + private var goal: Goal? = null // This can't be null after findPath is called. + private var availableMovements: Array? = null // This can't be null after findPath is called. - fun findPath(): Path? { + fun findPath(goal: Goal, movements: Array, returnBestNode: Boolean): Path? { + val startPos = PlayerUtils.position val calculationContext = CalculationContext() val openSet = BinaryHeapOpenSet() val movementResult = MovementResult() - val startNode = PathNode( - startX, startY, startZ, goal - ) + // After this, goal and movements should never be null again, so we can do !! in private functions safely. + // If they are null, someone nuked the code, and we should get angry at them. + AStarPathfinder.goal = goal + availableMovements = movements + + val startNode = PathNode(startPos.x, startPos.y, startPos.z, goal) startNode.costSoFar = 0.0 startNode.totalCost = startNode.costToEnd @@ -66,11 +65,13 @@ class AStarPathfinder( movementResult: MovementResult, openSet: BinaryHeapOpenSet, ) { - for (move in movements) { + for (move in availableMovements!!) { movementResult.reset() move.calculateCost(calculationContext, currentNode, movementResult) - if (movementResult.cost < calculationContext.infCost) relaxNeighbor(currentNode, move, movementResult, openSet) + if (movementResult.cost < calculationContext.infCost) { + relaxNeighbor(currentNode, move, movementResult, openSet) + } } } @@ -81,7 +82,7 @@ class AStarPathfinder( openSet: BinaryHeapOpenSet ) { val neighborCostSoFar = currentNode.costSoFar + movementResult.cost - val neighborNode = getNode( + val neighborNode = getOrCacheNode( movementResult.x, movementResult.y, movementResult.z, PathNode.longHash(movementResult.x, movementResult.y, movementResult.z) ) @@ -100,11 +101,11 @@ class AStarPathfinder( } } - fun getNode(x: Int, y: Int, z: Int, hash: Long): PathNode { + fun getOrCacheNode(x: Int, y: Int, z: Int,hash: Long): PathNode { var node: PathNode? = nodeCache.get(hash) if (node == null) { - node = PathNode(x, y, z, goal) + node = PathNode(x, y, z, goal!!) nodeCache.put(hash, node) } @@ -123,7 +124,7 @@ class AStarPathfinder( return Path( nodes = path, timeElapsed = (System.nanoTime() - startTime).milliseconds, - goal = goal + goal = goal!! ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt index b958c3d..873d1b7 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt @@ -12,7 +12,6 @@ import org.cobalt.util.helper.Multithreading class CalculatingState : ExecutorState() { override fun enter() { - val startPos = PlayerUtils.position val goal = PathFindingFacade.currentGoal val availableMovements = PathFindingFacade.availableMovements @@ -28,15 +27,8 @@ class CalculatingState : ExecutorState() { return } - val pathFinder = AStarPathfinder( - startPos.x, startPos.y, startPos.z, - goal, - availableMovements, - PathFindingConfig.returnBestNode - ) - Multithreading.runAsync { - val path = pathFinder.findPath() + val path = AStarPathfinder.findPath(goal, availableMovements, PathFindingConfig.returnBestNode) if (path == null) { ChatUtils.sendSystemMessage("Unable to find a path") From cea375bd83afb98f97379ecbe5dbb18def70e019 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 12:44:10 +0200 Subject: [PATCH 17/28] chore: run intellij code cleanup --- src/main/kotlin/org/cobalt/pathfinder/PathInput.kt | 1 - .../org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt | 4 ++-- .../kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt | 1 + .../pathfinder/movement/impl/fly/FlyAscendMovement.kt | 6 +----- .../pathfinder/movement/impl/fly/FlyDescendMovement.kt | 6 +----- .../pathfinder/movement/impl/fly/FlyDiagonalMovement.kt | 6 +----- .../pathfinder/movement/impl/fly/FlyTraverseMovement.kt | 6 +----- .../cobalt/pathfinder/movement/impl/walk/AscendMovement.kt | 6 +----- .../cobalt/pathfinder/movement/impl/walk/DescendMovement.kt | 6 +----- .../pathfinder/movement/impl/walk/DiagonalMovement.kt | 6 +----- .../pathfinder/movement/impl/walk/TraverseMovement.kt | 6 +----- .../org/cobalt/pathfinder/state/impl/CalculatingState.kt | 3 +-- .../kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt | 2 +- 13 files changed, 13 insertions(+), 46 deletions(-) diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt index 0a84a6b..96152ec 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt @@ -3,7 +3,6 @@ package org.cobalt.pathfinder import net.minecraft.client.player.ClientInput import net.minecraft.world.entity.player.Input import net.minecraft.world.phys.Vec2 -import org.cobalt.Cobalt.minecraft import org.cobalt.pathfinder.helper.PlayerInput class PathInput : ClientInput() { diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 2d7ec54..64b9b6d 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -79,7 +79,7 @@ object AStarPathfinder { currentNode: PathNode, movement: Movement, movementResult: MovementResult, - openSet: BinaryHeapOpenSet + openSet: BinaryHeapOpenSet, ) { val neighborCostSoFar = currentNode.costSoFar + movementResult.cost val neighborNode = getOrCacheNode( @@ -101,7 +101,7 @@ object AStarPathfinder { } } - fun getOrCacheNode(x: Int, y: Int, z: Int,hash: Long): PathNode { + fun getOrCacheNode(x: Int, y: Int, z: Int, hash: Long): PathNode { var node: PathNode? = nodeCache.get(hash) if (node == null) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt index c625f0e..6778de2 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt @@ -49,6 +49,7 @@ object MovementHelper { private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } + private var lastSector: Sector = Sector.FORWARD @JvmStatic diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt index eb10135..5007e9c 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* class FlyAscendMovement : Movement(MovementType.FLY) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt index c1729fc..430c0a5 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* class FlyDescendMovement : Movement(MovementType.FLY) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt index 08924f8..18c41c3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* class FlyDiagonalMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt index df093cb..f7689bc 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* class FlyTraverseMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt index 824a630..e6ff878 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt index bb0bd7f..47ff069 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt index 4b21376..ee9a521 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* class DiagonalMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt index 9138f51..a8e6372 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt @@ -1,11 +1,7 @@ package org.cobalt.pathfinder.movement.impl.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* class TraverseMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt index 873d1b7..0c147ba 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt @@ -1,12 +1,11 @@ package org.cobalt.pathfinder.state.impl -import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType -import org.cobalt.util.PlayerUtils import org.cobalt.util.helper.Multithreading class CalculatingState : ExecutorState() { diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt index 950a4e3..9bce670 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt @@ -9,8 +9,8 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.dsl.centerVec import org.cobalt.module.impl.misc.Debug import org.cobalt.module.impl.misc.Rotations -import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.helper.PathRenderer import org.cobalt.pathfinder.helper.PlayerInput From fd949c2691b38ec2478f4bb513b12f01dd10dcff Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 13:06:53 +0200 Subject: [PATCH 18/28] chore: extract constants in ChatUtils --- src/main/kotlin/org/cobalt/util/ChatUtils.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/cobalt/util/ChatUtils.kt b/src/main/kotlin/org/cobalt/util/ChatUtils.kt index 4c3fd73..2d2beec 100644 --- a/src/main/kotlin/org/cobalt/util/ChatUtils.kt +++ b/src/main/kotlin/org/cobalt/util/ChatUtils.kt @@ -12,10 +12,21 @@ object ChatUtils { private val logger = LoggerFactory.getLogger(this::class.java) + private const val DARK_GRAY = "" + private const val RESET = "" + private const val GRADIENT_END = "" + + private const val DEFAULT_GRADIENT = "" + private const val DEBUG_GRADIENT = "" + + private const val PREFIX_START = "$DARK_GRAY[$DARK_GRAY]" + private const val PREFIX_END = "$DARK_GRAY] $RESET" + private val defaultPrefix = - "[${Cobalt.MOD_NAME}] " + "$PREFIX_START$DEFAULT_GRADIENT${Cobalt.MOD_NAME}$GRADIENT_END$PREFIX_END" + private val debugPrefix = - "[${Cobalt.MOD_NAME} Debug] " + "$PREFIX_START$DEBUG_GRADIENT${Cobalt.MOD_NAME} Debug$GRADIENT_END$PREFIX_END" private var lastDebugMessage: String? = null From bfaf696c84c912e342c02360d8918982b867e86a Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 13 Jul 2026 14:47:08 +0200 Subject: [PATCH 19/28] chore: thank you nathan for this incredible code. --- .../cobalt/pathfinder/PathFindingConfig.kt | 3 +- .../cobalt/pathfinder/PathFindingFacade.kt | 4 +- .../cobalt/pathfinder/calculate/PathMode.kt | 16 +- .../calculate/path/AStarPathfinder.kt | 14 +- .../pathfinder/helper/BlockStateAccessor.kt | 4 - .../pathfinder/movement/CalculationContext.kt | 5 +- .../pathfinder/movement/FlowingCheck.kt | 32 ++ .../pathfinder/movement/MovementHelper.kt | 463 ------------------ .../movement/MovementInputMapper.kt | 83 ++++ .../movement/PathLookTargetProjector.kt | 271 ++++++++++ .../{impl => }/fly/FlyAscendMovement.kt | 5 +- .../{impl => }/fly/FlyDescendMovement.kt | 5 +- .../{impl => }/fly/FlyDiagonalMovement.kt | 9 +- .../{impl => }/fly/FlyTraverseMovement.kt | 5 +- .../movement/rules/BlockSupportRules.kt | 56 +++ .../movement/rules/BlockTraversalRules.kt | 24 + .../movement/rules/PassthroughRules.kt | 172 +++++++ .../pathfinder/movement/rules/StandOnRules.kt | 110 +++++ .../movement/rules/data/BlockRule.kt | 9 + .../movement/rules/data/BlockSetRule.kt | 12 + .../movement/rules/data/BlockTypeRule.kt | 12 + .../pathfinder/movement/rules/data/Ternary.kt | 5 + .../{impl => }/walk/AscendMovement.kt | 7 +- .../{impl => }/walk/DescendMovement.kt | 7 +- .../{impl => }/walk/DiagonalMovement.kt | 9 +- .../{impl => }/walk/TraverseMovement.kt | 5 +- .../pathfinder/precompute/PrecomputedData.kt | 73 --- .../cobalt/pathfinder/precompute/Ternary.kt | 5 - .../{impl => calculation}/CalculatingState.kt | 32 +- .../state/{impl => fly}/StartFlyState.kt | 31 +- .../pathfinder/state/impl/PathingState.kt | 241 --------- .../pathfinder/state/pathing/JumpDecision.kt | 47 ++ .../state/pathing/PathingAdvancer.kt | 83 ++++ .../state/pathing/PathingInputBuilder.kt | 96 ++++ .../pathfinder/state/pathing/PathingState.kt | 99 ++++ .../org/cobalt/ui/helper/DragHandler.kt | 6 +- .../kotlin/org/cobalt/ui/helper/SnapHelper.kt | 13 +- src/main/kotlin/org/cobalt/util/ChatUtils.kt | 2 +- 38 files changed, 1214 insertions(+), 861 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/fly/FlyAscendMovement.kt (73%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/fly/FlyDescendMovement.kt (73%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/fly/FlyDiagonalMovement.kt (67%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/fly/FlyTraverseMovement.kt (79%) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/Ternary.kt rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/walk/AscendMovement.kt (72%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/walk/DescendMovement.kt (75%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/walk/DiagonalMovement.kt (67%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{impl => }/walk/TraverseMovement.kt (78%) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/precompute/PrecomputedData.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/precompute/Ternary.kt rename src/main/kotlin/org/cobalt/pathfinder/state/{impl => calculation}/CalculatingState.kt (60%) rename src/main/kotlin/org/cobalt/pathfinder/state/{impl => fly}/StartFlyState.kt (54%) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt index e22c917..02d351f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt @@ -2,8 +2,7 @@ package org.cobalt.pathfinder object PathFindingConfig { var shouldSprint: Boolean = true - var preferShifting: Boolean = false var returnBestNode: Boolean = false - var maxCalculationTime: Long = 5_000 + var maxCalculationTime: Long = 10_000_000_000L var hasReachedThreshold: Double = 0.09 // // = sqrt(0.3), so 0.3 blocks of distance is the threshold } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt index 014f268..4e94eb7 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt @@ -11,7 +11,7 @@ import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.Goal import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.state.ExecutorState -import org.cobalt.pathfinder.state.impl.CalculatingState +import org.cobalt.pathfinder.state.calculation.CalculatingState import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType import org.cobalt.util.PlayerUtils @@ -121,8 +121,6 @@ object PathFindingFacade { } state?.onRender() - -// PathRenderer.render() } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathMode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathMode.kt index 4fc16b9..49111f9 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathMode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathMode.kt @@ -1,14 +1,14 @@ package org.cobalt.pathfinder.calculate import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.impl.fly.FlyAscendMovement -import org.cobalt.pathfinder.movement.impl.fly.FlyDescendMovement -import org.cobalt.pathfinder.movement.impl.fly.FlyDiagonalMovement -import org.cobalt.pathfinder.movement.impl.fly.FlyTraverseMovement -import org.cobalt.pathfinder.movement.impl.walk.AscendMovement -import org.cobalt.pathfinder.movement.impl.walk.DescendMovement -import org.cobalt.pathfinder.movement.impl.walk.DiagonalMovement -import org.cobalt.pathfinder.movement.impl.walk.TraverseMovement +import org.cobalt.pathfinder.movement.fly.FlyAscendMovement +import org.cobalt.pathfinder.movement.fly.FlyDescendMovement +import org.cobalt.pathfinder.movement.fly.FlyDiagonalMovement +import org.cobalt.pathfinder.movement.fly.FlyTraverseMovement +import org.cobalt.pathfinder.movement.walk.AscendMovement +import org.cobalt.pathfinder.movement.walk.DescendMovement +import org.cobalt.pathfinder.movement.walk.DiagonalMovement +import org.cobalt.pathfinder.movement.walk.TraverseMovement enum class PathMode(vararg val movements: Movement) { WALK( diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 64b9b6d..608aeb9 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -1,7 +1,7 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap -import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.nanoseconds import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathNode @@ -10,6 +10,8 @@ import org.cobalt.pathfinder.goal.Goal import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.util.ChatUtils +import org.cobalt.util.MessageType import org.cobalt.util.PlayerUtils object AStarPathfinder { @@ -39,8 +41,14 @@ object AStarPathfinder { var bestNode = startNode val deadline = startTime + PathFindingConfig.maxCalculationTime + ChatUtils.sendSystemMessage( + "Starting pathfinding from ${startNode.block}", + MessageType.DEBUG + ) - while (!openSet.isEmpty() && System.nanoTime() < deadline) { + while (!openSet.isEmpty() + && System.nanoTime() < deadline + ) { val currentNode = openSet.poll() if (currentNode < bestNode) bestNode = currentNode @@ -123,7 +131,7 @@ object AStarPathfinder { return Path( nodes = path, - timeElapsed = (System.nanoTime() - startTime).milliseconds, + timeElapsed = (System.nanoTime() - startTime).nanoseconds, goal = goal!! ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/BlockStateAccessor.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/BlockStateAccessor.kt index 2a52fbf..6de5ae7 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/BlockStateAccessor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/BlockStateAccessor.kt @@ -1,7 +1,6 @@ package org.cobalt.pathfinder.helper import net.minecraft.client.multiplayer.ClientLevel -import net.minecraft.core.BlockPos import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.chunk.LevelChunk @@ -9,9 +8,6 @@ import net.minecraft.world.level.chunk.status.ChunkStatus class BlockStateAccessor(val level: ClientLevel) { - val mutablePos = BlockPos.MutableBlockPos() - val access = BlockViewWrapper(this) - private val provider = level.chunkSource private var prevChunk: LevelChunk? = null private val air = Blocks.AIR.defaultBlockState() diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt index b8a13a3..3dfefae 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt @@ -3,7 +3,6 @@ package org.cobalt.pathfinder.movement import net.minecraft.world.effect.MobEffects import org.cobalt.Cobalt.minecraft import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.precompute.PrecomputedData class CalculationContext { @@ -15,7 +14,5 @@ class CalculationContext { val speedAmplifier = player.getEffect(MobEffects.SPEED)?.amplifier ?: -1 val jumpAmplifier = player.getEffect(MobEffects.JUMP_BOOST)?.amplifier ?: -1 - val bsa = BlockStateAccessor(level) - val precomputedData = PrecomputedData() - + val blockStateAccessor = BlockStateAccessor(level) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt new file mode 100644 index 0000000..d30886f --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt @@ -0,0 +1,32 @@ +package org.cobalt.pathfinder.movement + +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.FlowingFluid +import org.cobalt.pathfinder.helper.BlockStateAccessor + +object FlowingCheck { + private fun possiblyFlowing(state: BlockState): Boolean { + val fluidState = state.fluidState + + return fluidState.type is FlowingFluid && + fluidState.type.getAmount(fluidState) != 8 + } + + fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { + val fluidState = state.fluidState + + if (fluidState.type !is FlowingFluid) { + return false + } + + if (fluidState.type.getAmount(fluidState) != 8) { + return true + } + + return possiblyFlowing(bsa.get(x + 1, y, z)) + || possiblyFlowing(bsa.get(x - 1, y, z)) + || possiblyFlowing(bsa.get(x, y, z + 1)) + || possiblyFlowing(bsa.get(x, y, z - 1)) + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt deleted file mode 100644 index 6778de2..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementHelper.kt +++ /dev/null @@ -1,463 +0,0 @@ -@file:Suppress("TooManyFunctions") - -package org.cobalt.pathfinder.movement - -import net.minecraft.core.BlockPos -import net.minecraft.util.Mth -import net.minecraft.world.level.EmptyBlockGetter -import net.minecraft.world.level.block.* -import net.minecraft.world.level.block.piston.MovingPistonBlock -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.level.block.state.properties.SlabType -import net.minecraft.world.level.material.FlowingFluid -import net.minecraft.world.level.material.Fluids -import net.minecraft.world.level.material.WaterFluid -import net.minecraft.world.level.pathfinder.PathComputationType -import net.minecraft.world.phys.Vec3 -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.helper.PlayerInput -import org.cobalt.pathfinder.precompute.Ternary -import org.cobalt.pathfinder.precompute.Ternary.* - -object MovementHelper { - private const val DIRECTIONS_COUNT = 8 - private const val FULL_CIRCLE_DEGREES = 360f - private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT - private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f - - private const val DEAD_BAND_DEGREES = 0.05f - - private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES - private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - - private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES - private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES - private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES - private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES - - private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val BACK_RIGHT_HOLD_DEGREES = BACK_RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val BACKWARD_HOLD_DEGREES = BACKWARD_START_DEGREES + DEAD_BAND_DEGREES - - private const val FORWARD_LEFT_HOLD_DEGREES = FORWARD_LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val LEFT_HOLD_DEGREES = LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES - - private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } - - private var lastSector: Sector = Sector.FORWARD - - @JvmStatic - fun canWalkOn( - ctx: CalculationContext, - x: Int, y: Int, z: Int, - state: BlockState = ctx.bsa.get(x, y, z), - ): Boolean { - if (!canWalkThrough(ctx, x, y + 1, z)) { - return false - } - - return canStandOn(ctx, x, y, z, state) - } - - @JvmStatic - fun canWalkThrough( - ctx: CalculationContext, - x: Int, y: Int, z: Int, - state: BlockState = ctx.bsa.get(x, y, z), - ): Boolean { - return canPassThrough(ctx, x, y, z, state) && - canPassThrough(ctx, x, y + 1, z) - } - - @JvmStatic - fun canPassThrough( - ctx: CalculationContext, - x: Int, y: Int, z: Int, - state: BlockState = ctx.bsa.get(x, y, z), - ): Boolean { - return ctx.precomputedData.canPassThrough(ctx, x, y, z, state) - } - - @JvmStatic - fun canStandOn( - ctx: CalculationContext, - x: Int, y: Int, z: Int, - state: BlockState = ctx.bsa.get(x, y, z), - ): Boolean { - return ctx.precomputedData.canStandOn(ctx, x, y, z, state) - } - - @JvmStatic - fun canPassThrough( - bsa: BlockStateAccessor, - x: Int, y: Int, z: Int, - state: BlockState = bsa.get(x, y, z), - ): Boolean { - val result = canPassThroughState(state) - - if (result == YES) { - return true - } - - if (result == NO) { - return false - } - - return canPassThroughPosition(bsa, x, y, z, state) - } - - @JvmStatic - fun canStandOn( - bsa: BlockStateAccessor, - x: Int, y: Int, z: Int, - state: BlockState = bsa.get(x, y, z), - ): Boolean { - val result = canStandOnState(state) - - if (result == YES) { - return true - } - - if (result == NO) { - return false - } - - return canStandOnPosition(bsa, x, y, z, state) - } - - @JvmStatic - fun canPassThroughState(state: BlockState): Ternary { - val block = state.block - - return when { - block is AirBlock -> YES - - block is BaseFireBlock || - block == Blocks.COBWEB || - block == Blocks.END_PORTAL || - block == Blocks.COCOA || - block is AbstractSkullBlock || - block == Blocks.BUBBLE_COLUMN || - block is ShulkerBoxBlock || - block is SlabBlock || - block is TrapDoorBlock || - block == Blocks.HONEY_BLOCK || - block == Blocks.END_ROD || - block == Blocks.SWEET_BERRY_BUSH || - block == Blocks.POINTED_DRIPSTONE || - block is AmethystClusterBlock || - block is AzaleaBlock || - block == Blocks.BIG_DRIPLEAF || - block == Blocks.POWDER_SNOW -> NO - - block is DoorBlock || block is FenceGateBlock -> - if (block == Blocks.IRON_DOOR) NO else YES - - block is CarpetBlock || - block is SnowLayerBlock -> MAYBE - - !state.fluidState.isEmpty -> - if (state.fluidState.type.getAmount(state.fluidState) != 8) NO else MAYBE - - block is CauldronBlock -> NO - - else -> - if (state.isPathfindable(PathComputationType.LAND)) YES else NO - } - } - - @JvmStatic - fun canStandOnState(state: BlockState): Ternary { - val block = state.block - - return when { - isBlockNormalCube(state) && - block != Blocks.BUBBLE_COLUMN && - block != Blocks.HONEY_BLOCK -> YES - - block is AzaleaBlock || - block == Blocks.LADDER || - block == Blocks.VINE || - block == Blocks.FARMLAND || - block == Blocks.DIRT_PATH || - block == Blocks.SOUL_SAND || - block == Blocks.ENDER_CHEST || - block == Blocks.CHEST || - block == Blocks.TRAPPED_CHEST || - block == Blocks.GLASS || - block is StainedGlassBlock || - block is StairBlock || - block is SlabBlock -> YES - - isWater(state) -> MAYBE - isLava(state) -> MAYBE - - else -> NO - } - } - - private fun canPassThroughPosition(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { - val block = state.block - - return when { - block is CarpetBlock -> { - canStandOn(bsa, x, y - 1, z) - } - - block is SnowLayerBlock -> { - if (!bsa.isLoaded(x, z)) { - true - } else if (state.getValue(SnowLayerBlock.LAYERS) >= 3) { - false - } else { - canStandOn(bsa, x, y - 1, z) - } - } - - !state.fluidState.isEmpty -> { - val fluidState = state.fluidState - - if (isFlowing(x, y, z, state, bsa)) { - false - } else { - val upState = bsa.get(x, y + 1, z) - - if (!upState.fluidState.isEmpty || upState.block is LilyPadBlock) { - false - } else { - fluidState.type is WaterFluid - } - } - } - - else -> state.isPathfindable(PathComputationType.LAND) - } - } - - private fun canStandOnPosition(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { - if (isWater(state)) { - val upState = bsa.get(x, y + 1, z) - val upBlock = upState.block - - if (upBlock == Blocks.LILY_PAD || upBlock is CarpetBlock) { - return true - } - - if (isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { - return isWater(upState) - } - - return isWater(upState) xor false - } - - return false - } - - @JvmStatic - fun isLiquid(state: BlockState): Boolean { - return !state.fluidState.isEmpty - } - - @JvmStatic - fun isWater(state: BlockState): Boolean { - val fluid = state.fluidState.type - return fluid == Fluids.WATER || fluid == Fluids.FLOWING_WATER - } - - @JvmStatic - fun isLava(state: BlockState): Boolean { - val fluid = state.fluidState.type - return fluid == Fluids.LAVA || fluid == Fluids.FLOWING_LAVA - } - - private fun possiblyFlowing(state: BlockState): Boolean { - val fluidState = state.fluidState - - return fluidState.type is FlowingFluid && - fluidState.type.getAmount(fluidState) != 8 - } - - @JvmStatic - private fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { - val fluidState = state.fluidState - - if (fluidState.type !is FlowingFluid) { - return false - } - - if (fluidState.type.getAmount(fluidState) != 8) { - return true - } - - return possiblyFlowing(bsa.get(x + 1, y, z)) - || possiblyFlowing(bsa.get(x - 1, y, z)) - || possiblyFlowing(bsa.get(x, y, z + 1)) - || possiblyFlowing(bsa.get(x, y, z - 1)) - } - - @JvmStatic - fun isBottomSlab(state: BlockState): Boolean { - return state.block is SlabBlock && - state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM - } - - @JvmStatic - fun isBlockNormalCube(state: BlockState): Boolean { - val block = state.block - - if ( - block is BambooStalkBlock || - block is MovingPistonBlock || - block is ScaffoldingBlock - ) { - return false - } - - if ( - block is ShulkerBoxBlock || - block is PointedDripstoneBlock || - block is AmethystClusterBlock - ) { - return false - } - - return try { - Block.isShapeFullBlock(state.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) - } catch (_: Exception) { - false - } - } - - @JvmStatic - fun getNeededKeys(playerYaw: Float, idealYaw: Float): PlayerInput { - val diff = Mth.wrapDegrees(idealYaw - playerYaw) - - if (staysInSector(lastSector, diff)) { - return sectorToInput(lastSector) - } - - val newSector = classify(diff) - lastSector = newSector - return sectorToInput(newSector) - } - - private fun staysInSector(sector: Sector, diff: Float): Boolean = when (sector) { - Sector.FORWARD -> diff in FORWARD_LEFT_HOLD_DEGREES.. diff >= FORWARD_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < RIGHT_HOLD_DEGREES - Sector.RIGHT -> diff >= RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_RIGHT_HOLD_DEGREES - Sector.BACK_RIGHT -> diff >= BACK_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACKWARD_HOLD_DEGREES - Sector.BACKWARD -> diff >= BACKWARD_START_DEGREES - DEAD_BAND_DEGREES || diff < BACKWARD_NEG_HOLD_DEGREES - Sector.BACK_LEFT -> diff >= BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_LEFT_HOLD_DEGREES - Sector.LEFT -> diff >= BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < LEFT_HOLD_DEGREES - Sector.FORWARD_LEFT -> diff >= LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < FORWARD_LEFT_HOLD_DEGREES - } - - private fun classify(diff: Float): Sector = when { - diff in FORWARD_LEFT_START_DEGREES.. Sector.FORWARD - diff in FORWARD_RIGHT_START_DEGREES.. Sector.FORWARD_RIGHT - diff in RIGHT_START_DEGREES.. Sector.RIGHT - diff in BACK_RIGHT_START_DEGREES.. Sector.BACK_RIGHT - diff !in BACKWARD_NEG_START_DEGREES.. Sector.BACKWARD - diff in BACKWARD_NEG_START_DEGREES.. Sector.BACK_LEFT - diff in BACK_LEFT_START_DEGREES.. Sector.LEFT - else -> Sector.FORWARD_LEFT - } - - private fun sectorToInput(sector: Sector): PlayerInput = when (sector) { - Sector.FORWARD -> PlayerInput(forward = true) - Sector.FORWARD_RIGHT -> PlayerInput(forward = true, right = true) - Sector.RIGHT -> PlayerInput(right = true) - Sector.BACK_RIGHT -> PlayerInput(backward = true, right = true) - Sector.BACKWARD -> PlayerInput(backward = true) - Sector.BACK_LEFT -> PlayerInput(backward = true, left = true) - Sector.LEFT -> PlayerInput(left = true) - Sector.FORWARD_LEFT -> PlayerInput(forward = true, left = true) - } - - @JvmStatic - fun getRotationTarget(playerEyePos: Vec3, nodes: List, currentIndex: Int, lookAhead: Double = 3.0): Vec3 { - val playerXZ = Vec3(playerEyePos.x, 0.0, playerEyePos.z) - - var bestDistSq = Double.MAX_VALUE - var bestSegIndex = currentIndex - var bestT = 0.0 - - val searchStart = (currentIndex - 1).coerceAtLeast(0) - val searchEnd = (currentIndex + 3).coerceAtMost(nodes.size - 1) - - for (i in searchStart until searchEnd) { - val from = nodes[i].centerVec - val to = nodes[i + 1].centerVec - val seg = Vec3(to.x - from.x, 0.0, to.z - from.z) - val segLenSq = seg.x * seg.x + seg.z * seg.z - - if (segLenSq <= 0.0) continue - - val t = ((playerXZ.x - from.x) * seg.x + (playerXZ.z - from.z) * seg.z) - .coerceIn(0.0, segLenSq) / segLenSq - - val px = from.x + seg.x * t - val pz = from.z + seg.z * t - val dx = playerXZ.x - px - val dz = playerXZ.z - pz - val distSq = dx * dx + dz * dz - - if (distSq < bestDistSq) { - bestDistSq = distSq - bestSegIndex = i - bestT = t - } - } - - var remaining = lookAhead - - val from = nodes[bestSegIndex].centerVec - val to = nodes[bestSegIndex + 1].centerVec - val seg = Vec3(to.x - from.x, 0.0, to.z - from.z) - val segLen = seg.length() - - if (segLen > 0.0) { - val remainingSeg = (1.0 - bestT) * segLen - if (remaining <= remainingSeg) { - val point = from.add(seg.scale(bestT + remaining / segLen)) - return Vec3(point.x, playerEyePos.y, point.z) - } - remaining -= remainingSeg - } - - for (i in (bestSegIndex + 1) until nodes.size - 1) { - val sFrom = nodes[i].centerVec - val sTo = nodes[i + 1].centerVec - val sSeg = Vec3(sTo.x - sFrom.x, 0.0, sTo.z - sFrom.z) - val sLen = sSeg.length() - - if (sLen <= 0.0) continue - - if (remaining <= sLen) { - val point = sFrom.add(sSeg.scale(remaining / sLen)) - return Vec3(point.x, playerEyePos.y, point.z) - } - - remaining -= sLen - } - - val last = nodes.last() - val prev = if (nodes.size > 1) nodes[nodes.size - 2] else last - val dir = Vec3((last.x - prev.x).toDouble(), 0.0, (last.z - prev.z).toDouble()) - val dirLen = dir.length() - val extrapolated = if (dirLen > 0.0) { - last.centerVec.add(dir.scale(remaining / dirLen)) - } else { - last.centerVec - } - - return Vec3(extrapolated.x, playerEyePos.y, extrapolated.z) - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt new file mode 100644 index 0000000..bf406c0 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt @@ -0,0 +1,83 @@ +package org.cobalt.pathfinder.movement + +import net.minecraft.util.Mth +import org.cobalt.pathfinder.helper.PlayerInput + +object MovementInputMapper { + private const val DIRECTIONS_COUNT = 8 + private const val FULL_CIRCLE_DEGREES = 360f + private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT + private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f + + private const val DEAD_BAND_DEGREES = 0.05f + + private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES + private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + + private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES + private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES + private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES + private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES + + private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val BACK_RIGHT_HOLD_DEGREES = BACK_RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val BACKWARD_HOLD_DEGREES = BACKWARD_START_DEGREES + DEAD_BAND_DEGREES + + private const val FORWARD_LEFT_HOLD_DEGREES = FORWARD_LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val LEFT_HOLD_DEGREES = LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES + + private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } + + private var lastSector: Sector = Sector.FORWARD + + @JvmStatic + fun getNeededKeys(playerYaw: Float, idealYaw: Float): PlayerInput { + val diff = Mth.wrapDegrees(idealYaw - playerYaw) + + if (staysInSector(lastSector, diff)) { + return sectorToInput(lastSector) + } + + val newSector = classify(diff) + lastSector = newSector + return sectorToInput(newSector) + } + + private fun staysInSector(sector: Sector, diff: Float): Boolean = when (sector) { + Sector.FORWARD -> diff in FORWARD_LEFT_HOLD_DEGREES.. diff >= FORWARD_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < RIGHT_HOLD_DEGREES + Sector.RIGHT -> diff >= RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_RIGHT_HOLD_DEGREES + Sector.BACK_RIGHT -> diff >= BACK_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACKWARD_HOLD_DEGREES + Sector.BACKWARD -> diff >= BACKWARD_START_DEGREES - DEAD_BAND_DEGREES || diff < BACKWARD_NEG_HOLD_DEGREES + Sector.BACK_LEFT -> diff >= BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_LEFT_HOLD_DEGREES + Sector.LEFT -> diff >= BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < LEFT_HOLD_DEGREES + Sector.FORWARD_LEFT -> diff >= LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < FORWARD_LEFT_HOLD_DEGREES + } + + private fun classify(diff: Float): Sector = when (diff) { + in FORWARD_LEFT_START_DEGREES.. Sector.FORWARD + in FORWARD_RIGHT_START_DEGREES.. Sector.FORWARD_RIGHT + in RIGHT_START_DEGREES.. Sector.RIGHT + in BACK_RIGHT_START_DEGREES.. Sector.BACK_RIGHT + !in BACKWARD_NEG_START_DEGREES.. Sector.BACKWARD + in BACKWARD_NEG_START_DEGREES.. Sector.BACK_LEFT + in BACK_LEFT_START_DEGREES.. Sector.LEFT + else -> Sector.FORWARD_LEFT + } + + private fun sectorToInput(sector: Sector): PlayerInput = when (sector) { + Sector.FORWARD -> PlayerInput(forward = true) + Sector.FORWARD_RIGHT -> PlayerInput(forward = true, right = true) + Sector.RIGHT -> PlayerInput(right = true) + Sector.BACK_RIGHT -> PlayerInput(backward = true, right = true) + Sector.BACKWARD -> PlayerInput(backward = true) + Sector.BACK_LEFT -> PlayerInput(backward = true, left = true) + Sector.LEFT -> PlayerInput(left = true) + Sector.FORWARD_LEFT -> PlayerInput(forward = true, left = true) + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt new file mode 100644 index 0000000..887bcfc --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt @@ -0,0 +1,271 @@ +package org.cobalt.pathfinder.movement + +import net.minecraft.world.phys.Vec3 +import org.cobalt.pathfinder.calculate.PathNode + +object PathLookTargetProjector { + private const val SEARCH_BACKWARD_SEGMENTS = 1 + private const val SEARCH_FORWARD_SEGMENTS = 3 + private const val ZERO = 0.0 + private const val FULL_SEGMENT = 1.0 + + private data class ClosestSegment( + val index: Int, + val progress: Double, + ) + + private data class SegmentProjection( + val progress: Double, + val distanceSquared: Double, + ) + + @JvmStatic + fun getRotationTarget( + playerEyePosition: Vec3, + pathNodes: List, + currentPathIndex: Int, + lookAheadDistance: Double = 3.0, + ): Vec3 { + val closestSegment = findClosestSegment( + playerEyePosition, + pathNodes, + currentPathIndex + ) + + val horizontalTarget = moveAlongPath( + pathNodes, + closestSegment, + lookAheadDistance + ) + + return Vec3( + horizontalTarget.x, + playerEyePosition.y, + horizontalTarget.z + ) + } + + private fun findClosestSegment( + playerEyePosition: Vec3, + pathNodes: List, + currentPathIndex: Int, + ): ClosestSegment { + val playerPosition = Vec3( + playerEyePosition.x, + ZERO, + playerEyePosition.z + ) + + var closestSegment = ClosestSegment( + currentPathIndex, + ZERO + ) + + var closestDistanceSquared = Double.MAX_VALUE + + val startIndex = maxOf( + ZERO.toInt(), + currentPathIndex - SEARCH_BACKWARD_SEGMENTS + ) + + val endIndex = minOf( + pathNodes.lastIndex, + currentPathIndex + SEARCH_FORWARD_SEGMENTS + ) + + for (segmentIndex in startIndex until endIndex) { + val projection = findClosestPointOnSegment( + playerPosition, + pathNodes[segmentIndex].centerVec, + pathNodes[segmentIndex + 1].centerVec + ) + + if (projection == null) { + continue + } + + if (projection.distanceSquared < closestDistanceSquared) { + closestDistanceSquared = projection.distanceSquared + closestSegment = ClosestSegment( + segmentIndex, + projection.progress + ) + } + } + + return closestSegment + } + + private fun findClosestPointOnSegment( + point: Vec3, + start: Vec3, + end: Vec3, + ): SegmentProjection? { + val direction = Vec3( + end.x - start.x, + ZERO, + end.z - start.z + ) + + val lengthSquared = + direction.x * direction.x + + direction.z * direction.z + + if (lengthSquared == ZERO) { + return null + } + + val progress = + ( + (point.x - start.x) * direction.x + + (point.z - start.z) * direction.z + ) + .coerceIn(ZERO, lengthSquared) / lengthSquared + + val closestX = start.x + direction.x * progress + val closestZ = start.z + direction.z * progress + + val distanceX = point.x - closestX + val distanceZ = point.z - closestZ + + return SegmentProjection( + progress, + distanceX * distanceX + distanceZ * distanceZ + ) + } + + private fun moveAlongPath( + pathNodes: List, + startingSegment: ClosestSegment, + distance: Double, + ): Vec3 { + var remainingDistance = distance + + val currentSegmentResult = moveOnSegment( + pathNodes, + startingSegment.index, + startingSegment.progress, + remainingDistance + ) + + if (currentSegmentResult != null) { + return currentSegmentResult + } + + remainingDistance -= distanceRemainingOnSegment( + pathNodes, + startingSegment + ) + + for (segmentIndex in startingSegment.index + 1 until pathNodes.lastIndex) { + val result = moveOnSegment( + pathNodes, + segmentIndex, + ZERO, + remainingDistance + ) + + if (result != null) { + return result + } + + remainingDistance -= segmentLength( + pathNodes, + segmentIndex + ) + } + + return extrapolatePastEnd( + pathNodes, + remainingDistance + ) + } + + private fun moveOnSegment( + pathNodes: List, + segmentIndex: Int, + startProgress: Double, + distance: Double, + ): Vec3? { + val start = pathNodes[segmentIndex].centerVec + val end = pathNodes[segmentIndex + 1].centerVec + + val direction = Vec3( + end.x - start.x, + ZERO, + end.z - start.z + ) + + val length = direction.length() + + if (length == ZERO) { + return null + } + + val availableDistance = + (FULL_SEGMENT - startProgress) * length + + if (distance > availableDistance) { + return null + } + + val progress = + startProgress + distance / length + + return start.add( + direction.scale(progress) + ) + } + + private fun distanceRemainingOnSegment( + pathNodes: List, + segment: ClosestSegment, + ): Double { + return (FULL_SEGMENT - segment.progress) * + segmentLength(pathNodes, segment.index) + } + + private fun segmentLength( + pathNodes: List, + segmentIndex: Int, + ): Double { + val start = pathNodes[segmentIndex].centerVec + val end = pathNodes[segmentIndex + 1].centerVec + + return Vec3( + end.x - start.x, + ZERO, + end.z - start.z + ).length() + } + + private fun extrapolatePastEnd( + pathNodes: List, + distance: Double, + ): Vec3 { + val last = pathNodes.last() + + val previous = + if (pathNodes.size > 1) { + pathNodes[pathNodes.lastIndex - 1] + } else { + last + } + + val direction = Vec3( + (last.x - previous.x).toDouble(), + ZERO, + (last.z - previous.z).toDouble() + ) + + val length = direction.length() + + if (length == ZERO) { + return last.centerVec + } + + return last.centerVec.add( + direction.scale(distance / length) + ) + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt similarity index 73% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt index 5007e9c..9b33671 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.fly +package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyAscendMovement : Movement(MovementType.FLY) { @@ -14,7 +15,7 @@ class FlyAscendMovement : Movement(MovementType.FLY) { val y = currNode.y + 1 val z = currNode.z - if (!MovementHelper.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt similarity index 73% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt index 430c0a5..92f136a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.fly +package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyDescendMovement : Movement(MovementType.FLY) { @@ -14,7 +15,7 @@ class FlyDescendMovement : Movement(MovementType.FLY) { val y = currNode.y - 1 val z = currNode.z - if (!MovementHelper.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt similarity index 67% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt index 18c41c3..5078010 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.fly +package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyDiagonalMovement( val dx: Int, @@ -17,15 +18,15 @@ class FlyDiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementHelper.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } - if (!MovementHelper.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!MovementHelper.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt similarity index 79% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt index f7689bc..89bf73a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.fly +package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyTraverseMovement( val dx: Int, @@ -17,7 +18,7 @@ class FlyTraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementHelper.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt new file mode 100644 index 0000000..cb78970 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt @@ -0,0 +1,56 @@ +package org.cobalt.pathfinder.movement.rules + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.EmptyBlockGetter +import net.minecraft.world.level.block.* +import net.minecraft.world.level.block.piston.MovingPistonBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.block.state.properties.SlabType +import net.minecraft.world.level.material.Fluids + +object BlockSupportRules { + @JvmStatic + fun isWater(state: BlockState): Boolean { + val fluid = state.fluidState.type + return fluid == Fluids.WATER || fluid == Fluids.FLOWING_WATER + } + + @JvmStatic + fun isLava(state: BlockState): Boolean { + val fluid = state.fluidState.type + return fluid == Fluids.LAVA || fluid == Fluids.FLOWING_LAVA + } + + @JvmStatic + fun isBottomSlab(state: BlockState): Boolean { + return state.block is SlabBlock && + state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM + } + + @JvmStatic + fun isBlockNormalCube(state: BlockState): Boolean { + val block = state.block + + if ( + block is BambooStalkBlock || + block is MovingPistonBlock || + block is ScaffoldingBlock + ) { + return false + } + + if ( + block is ShulkerBoxBlock || + block is PointedDripstoneBlock || + block is AmethystClusterBlock + ) { + return false + } + + return try { + Block.isShapeFullBlock(state.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) + } catch (_: Exception) { + false + } + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt new file mode 100644 index 0000000..75bbe2e --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt @@ -0,0 +1,24 @@ +package org.cobalt.pathfinder.movement.rules + +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.rules.PassthroughRules.canPassThrough +import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn + +object BlockTraversalRules { + @JvmStatic + fun canWalkOn(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { + if ( + !canWalkThrough(calculationContext, x, y + 1, z) + ) { + return false + } + + return canStandOn(calculationContext.blockStateAccessor, x, y, z) + } + + @JvmStatic + fun canWalkThrough(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { + return canPassThrough(calculationContext.blockStateAccessor, x, y, z) && + canPassThrough(calculationContext.blockStateAccessor, x, y + 1, z) + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt new file mode 100644 index 0000000..a14c950 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt @@ -0,0 +1,172 @@ +package org.cobalt.pathfinder.movement.rules + +import net.minecraft.world.level.block.AbstractSkullBlock +import net.minecraft.world.level.block.AirBlock +import net.minecraft.world.level.block.AmethystClusterBlock +import net.minecraft.world.level.block.AzaleaBlock +import net.minecraft.world.level.block.BaseFireBlock +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.CarpetBlock +import net.minecraft.world.level.block.CauldronBlock +import net.minecraft.world.level.block.DoorBlock +import net.minecraft.world.level.block.FenceGateBlock +import net.minecraft.world.level.block.ShulkerBoxBlock +import net.minecraft.world.level.block.SlabBlock +import net.minecraft.world.level.block.SnowLayerBlock +import net.minecraft.world.level.block.TrapDoorBlock +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.LilyPadBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.block.state.properties.BlockStateProperties +import net.minecraft.world.level.material.WaterFluid +import net.minecraft.world.level.pathfinder.PathComputationType +import org.cobalt.pathfinder.helper.BlockStateAccessor +import org.cobalt.pathfinder.movement.FlowingCheck +import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn +import org.cobalt.pathfinder.movement.rules.data.BlockSetRule +import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule +import org.cobalt.pathfinder.movement.rules.data.Ternary + +object PassthroughRules { + + val PASS_THROUGH_RULES = listOf( + BlockTypeRule( + setOf(AirBlock::class.java), + Ternary.YES, + ), + + BlockSetRule( + setOf( + Blocks.COBWEB, + Blocks.END_PORTAL, + Blocks.COCOA, + Blocks.BUBBLE_COLUMN, + Blocks.HONEY_BLOCK, + Blocks.END_ROD, + Blocks.SWEET_BERRY_BUSH, + Blocks.POINTED_DRIPSTONE, + Blocks.BIG_DRIPLEAF, + Blocks.POWDER_SNOW, + ), + Ternary.NO, + ), + + BlockTypeRule( + setOf( + BaseFireBlock::class.java, + AbstractSkullBlock::class.java, + ShulkerBoxBlock::class.java, + SlabBlock::class.java, + AmethystClusterBlock::class.java, + AzaleaBlock::class.java, + ), + Ternary.NO, + ), + + BlockTypeRule( + setOf( + CarpetBlock::class.java, + SnowLayerBlock::class.java, + ), + Ternary.MAYBE, + ), + + BlockTypeRule( + setOf(CauldronBlock::class.java), + Ternary.NO, + ), + ) + + private val PRECOMPUTED = Array(Block.BLOCK_STATE_REGISTRY.size()) { + Ternary.NO + } + + init { + Block.BLOCK_STATE_REGISTRY.forEach { state -> + PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] = compute(state) + } + } + + private fun compute(state: BlockState): Ternary { + PASS_THROUGH_RULES.firstOrNull { it.matches(state) } + ?.let { return it.result } + + val block = state.block + + if (block is DoorBlock || block is FenceGateBlock || block is TrapDoorBlock) { + return Ternary.MAYBE + } + + if (!state.fluidState.isEmpty) { + return if (state.fluidState.type.getAmount(state.fluidState) != 8) { + Ternary.NO + } else { + Ternary.MAYBE + } + } + + return if (state.isPathfindable(PathComputationType.LAND)) { + Ternary.YES + } else { + Ternary.NO + } + } + + fun canPassThrough(bsa: BlockStateAccessor, x: Int, y: Int, z: Int): Boolean { + val state = bsa.get(x, y, z) + val result = PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] + + if (result == Ternary.YES) { + return true + } + + if (result == Ternary.NO) { + return false + } + + return resolveMaybe(bsa, x, y, z, state) + } + + private fun resolveMaybe(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { + val block = state.block + + return when { + block is DoorBlock || block is FenceGateBlock || block is TrapDoorBlock -> { + state.getValue(BlockStateProperties.OPEN) + } + + block is CarpetBlock -> { + canStandOn(bsa, x, y - 1, z) + } + + block is SnowLayerBlock -> { + if (!bsa.isLoaded(x, z)) { + true + } else if (state.getValue(SnowLayerBlock.LAYERS) >= 3) { + false + } else { + canStandOn(bsa, x, y - 1, z) + } + } + + !state.fluidState.isEmpty -> { + val fluidState = state.fluidState + + if (FlowingCheck.isFlowing(x, y, z, state, bsa)) { + false + } else { + val upState = bsa.get(x, y + 1, z) + + if (!upState.fluidState.isEmpty || upState.block is LilyPadBlock) { + false + } else { + fluidState.type is WaterFluid + } + } + } + + else -> state.isPathfindable(PathComputationType.LAND) + } + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt new file mode 100644 index 0000000..07a24a4 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt @@ -0,0 +1,110 @@ +package org.cobalt.pathfinder.movement.rules + +import net.minecraft.world.level.block.AzaleaBlock +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.CarpetBlock +import net.minecraft.world.level.block.SlabBlock +import net.minecraft.world.level.block.StainedGlassBlock +import net.minecraft.world.level.block.StairBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.Fluids +import org.cobalt.pathfinder.helper.BlockStateAccessor +import org.cobalt.pathfinder.movement.FlowingCheck +import org.cobalt.pathfinder.movement.rules.data.BlockSetRule +import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule +import org.cobalt.pathfinder.movement.rules.data.Ternary + +object StandOnRules { + + val STAND_ON_RULES = listOf( + BlockSetRule( + setOf( + Blocks.LADDER, + Blocks.VINE, + Blocks.FARMLAND, + Blocks.DIRT_PATH, + Blocks.SOUL_SAND, + Blocks.ENDER_CHEST, + Blocks.CHEST, + Blocks.TRAPPED_CHEST, + Blocks.GLASS, + ), + Ternary.YES, + ), + + BlockTypeRule( + setOf( + StainedGlassBlock::class.java, + StairBlock::class.java, + SlabBlock::class.java, + AzaleaBlock::class.java, + ), + Ternary.YES, + ), + ) + + private val PRECOMPUTED = Array(Block.BLOCK_STATE_REGISTRY.size()) { + Ternary.NO + } + + init { + Block.BLOCK_STATE_REGISTRY.forEach { state -> + PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] = compute(state) + } + } + + private fun compute(state: BlockState): Ternary { + val block = state.block + + if (BlockSupportRules.isBlockNormalCube(state) && + block != Blocks.BUBBLE_COLUMN && + block != Blocks.HONEY_BLOCK + ) { + return Ternary.YES + } + + STAND_ON_RULES.firstOrNull { it.matches(state) } + ?.let { return it.result } + + return when { + BlockSupportRules.isWater(state) -> Ternary.MAYBE + BlockSupportRules.isLava(state) -> Ternary.MAYBE + else -> Ternary.NO + } + } + + fun canStandOn(bsa: BlockStateAccessor, x: Int, y: Int, z: Int): Boolean { + val state = bsa.get(x, y, z) + val result = PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] + + if (result == Ternary.YES) { + return true + } + + if (result == Ternary.NO) { + return false + } + + return resolveMaybe(bsa, x, y, z, state) + } + + private fun resolveMaybe(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { + if (BlockSupportRules.isWater(state)) { + val upState = bsa.get(x, y + 1, z) + val upBlock = upState.block + + if (upBlock == Blocks.LILY_PAD || upBlock is CarpetBlock) { + return true + } + + if (FlowingCheck.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { + return BlockSupportRules.isWater(upState) + } + + return BlockSupportRules.isWater(upState) xor false + } + + return false + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt new file mode 100644 index 0000000..a7c22da --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt @@ -0,0 +1,9 @@ +package org.cobalt.pathfinder.movement.rules.data + +import net.minecraft.world.level.block.state.BlockState + +interface BlockRule { + val result: Ternary + + fun matches(state: BlockState): Boolean +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt new file mode 100644 index 0000000..0f8f0ca --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt @@ -0,0 +1,12 @@ +package org.cobalt.pathfinder.movement.rules.data + +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.state.BlockState + +class BlockSetRule( + private val blocks: Set, + override val result: Ternary, +) : BlockRule { + override fun matches(state: BlockState): Boolean = + state.block in blocks +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt new file mode 100644 index 0000000..c720ee0 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt @@ -0,0 +1,12 @@ +package org.cobalt.pathfinder.movement.rules.data + +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.state.BlockState + +class BlockTypeRule( + private val types: Set>, + override val result: Ternary, +) : BlockRule { + override fun matches(state: BlockState): Boolean = + types.any { it.isInstance(state.block) } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/Ternary.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/Ternary.kt new file mode 100644 index 0000000..7dc30e4 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/Ternary.kt @@ -0,0 +1,5 @@ +package org.cobalt.pathfinder.movement.rules.data + +enum class Ternary { + YES, MAYBE, NO +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt similarity index 72% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt index e6ff878..9954540 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.walk +package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( @@ -18,11 +19,11 @@ class AscendMovement( val y = currNode.y + 1 val z = currNode.z + dz - if (!MovementHelper.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } - if (!MovementHelper.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt similarity index 75% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt index 47ff069..39a26dc 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.walk +package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( @@ -18,11 +19,11 @@ class DescendMovement( val y = currNode.y - 1 val z = currNode.z + dz - if (!MovementHelper.canWalkThrough(ctx, x, currNode.y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, currNode.y, z)) { return } - if (!MovementHelper.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt similarity index 67% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt index ee9a521..8ea0bef 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.walk +package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class DiagonalMovement( val dx: Int, @@ -17,15 +18,15 @@ class DiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementHelper.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } - if (!MovementHelper.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!MovementHelper.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt similarity index 78% rename from src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt index a8e6372..57b8585 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/impl/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt @@ -1,7 +1,8 @@ -package org.cobalt.pathfinder.movement.impl.walk +package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class TraverseMovement( val dx: Int, @@ -17,7 +18,7 @@ class TraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementHelper.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/precompute/PrecomputedData.kt b/src/main/kotlin/org/cobalt/pathfinder/precompute/PrecomputedData.kt deleted file mode 100644 index 48509fe..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/precompute/PrecomputedData.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.cobalt.pathfinder.precompute - -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.state.BlockState -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.MovementHelper - -class PrecomputedData { - - private val data = ByteArray(Block.BLOCK_STATE_REGISTRY.size()) - - companion object { - private const val COMPLETED_MASK = 1 shl 0 - private const val CAN_PASS_THROUGH_MAYBE_MASK = 1 shl 1 - private const val CAN_PASS_THROUGH_MASK = 1 shl 2 - private const val CAN_STAND_ON_MAYBE_MASK = 1 shl 3 - private const val CAN_STAND_ON_MASK = 1 shl 4 - } - - private fun fillData(id: Int, state: BlockState): Byte { - var blockData = 0 - - when (MovementHelper.canPassThroughState(state)) { - Ternary.YES -> blockData = 0 or CAN_PASS_THROUGH_MASK - Ternary.MAYBE -> blockData = 0 or CAN_PASS_THROUGH_MAYBE_MASK - Ternary.NO -> {} - } - - when (MovementHelper.canStandOnState(state)) { - Ternary.YES -> blockData = blockData or CAN_STAND_ON_MASK - Ternary.MAYBE -> blockData = blockData or CAN_STAND_ON_MAYBE_MASK - Ternary.NO -> {} - } - - blockData = blockData or COMPLETED_MASK - - val byteResult = blockData.toByte() - data[id] = byteResult - - return byteResult - } - - fun canPassThrough(ctx: CalculationContext, x: Int, y: Int, z: Int, state: BlockState): Boolean { - val id = Block.BLOCK_STATE_REGISTRY.getId(state) - var blockData = data[id].toInt() - - if ((blockData and COMPLETED_MASK) == 0) { - blockData = fillData(id, state).toInt() - } - - return if ((blockData and CAN_PASS_THROUGH_MAYBE_MASK) != 0) { - MovementHelper.canPassThrough(ctx.bsa, x, y, z, state) - } else { - (blockData and CAN_PASS_THROUGH_MASK) != 0 - } - } - - fun canStandOn(ctx: CalculationContext, x: Int, y: Int, z: Int, state: BlockState): Boolean { - val id = Block.BLOCK_STATE_REGISTRY.getId(state) - var blockData = data[id].toInt() - - if ((blockData and COMPLETED_MASK) == 0) { - blockData = fillData(id, state).toInt() - } - - return if ((blockData and CAN_STAND_ON_MAYBE_MASK) != 0) { - MovementHelper.canStandOn(ctx.bsa, x, y, z, state) - } else { - (blockData and CAN_STAND_ON_MASK) != 0 - } - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/precompute/Ternary.kt b/src/main/kotlin/org/cobalt/pathfinder/precompute/Ternary.kt deleted file mode 100644 index 9492e01..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/precompute/Ternary.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.cobalt.pathfinder.precompute - -enum class Ternary { - YES, MAYBE, NO -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt similarity index 60% rename from src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt rename to src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt index 0c147ba..3e1b16d 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt @@ -1,14 +1,19 @@ -package org.cobalt.pathfinder.state.impl +package org.cobalt.pathfinder.state.calculation +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.ChatUtils -import org.cobalt.util.MessageType -import org.cobalt.util.helper.Multithreading class CalculatingState : ExecutorState() { + private var calculationJob: Job? = null override fun enter() { val goal = PathFindingFacade.currentGoal @@ -26,23 +31,28 @@ class CalculatingState : ExecutorState() { return } - Multithreading.runAsync { - val path = AStarPathfinder.findPath(goal, availableMovements, PathFindingConfig.returnBestNode) + calculationJob = CoroutineScope(Dispatchers.Default).launch { + val path = AStarPathfinder.findPath( + goal, + availableMovements, + PathFindingConfig.returnBestNode + ) + + if (!isActive) return@launch if (path == null) { ChatUtils.sendSystemMessage("Unable to find a path") PathFindingFacade.stop() - return@runAsync + return@launch } PathFindingFacade.path = path PathFindingFacade.changeState(PathingState()) - - ChatUtils.sendSystemMessage( - "Found ${path.nodes.size} node path in ${path.timeElapsed.inWholeMilliseconds}ms", - MessageType.DEBUG - ) } } + override fun exit() { + calculationJob?.cancel() + calculationJob = null + } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt similarity index 54% rename from src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt rename to src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt index c080fd5..ac27ae3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt @@ -1,16 +1,17 @@ -package org.cobalt.pathfinder.state.impl +package org.cobalt.pathfinder.state.fly import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.helper.PathRenderer import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.PlayerUtils class StartFlyState : ExecutorState() { - private var flyStage = 0 + private var flyStage = FlyStage.INITIAL_JUMP override fun enter() { - flyStage = 0 + flyStage = FlyStage.INITIAL_JUMP } override fun onTick() { @@ -27,33 +28,33 @@ class StartFlyState : ExecutorState() { private fun handleFlyStart(): Boolean { if (PlayerUtils.isFlying) { - flyStage = 0 + flyStage = FlyStage.INITIAL_JUMP return false } if (PlayerUtils.onGround) { - flyStage = 0 + flyStage = FlyStage.INITIAL_JUMP } input.stopMovement() when (flyStage) { - 0 -> { + FlyStage.INITIAL_JUMP -> { input.jump = true - flyStage = 1 + flyStage = FlyStage.RELEASE_INITIAL_JUMP } - 1 -> { + FlyStage.RELEASE_INITIAL_JUMP -> { input.jump = false - flyStage = 2 + flyStage = FlyStage.SECOND_JUMP } - 2 -> { + FlyStage.SECOND_JUMP -> { input.jump = true - flyStage = 3 + flyStage = FlyStage.RELEASE_SECOND_JUMP } - 3 -> { + FlyStage.RELEASE_SECOND_JUMP -> { input.jump = false } } @@ -61,4 +62,10 @@ class StartFlyState : ExecutorState() { return true } + private enum class FlyStage { + INITIAL_JUMP, + RELEASE_INITIAL_JUMP, + SECOND_JUMP, + RELEASE_SECOND_JUMP, + } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt deleted file mode 100644 index 9bce670..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/impl/PathingState.kt +++ /dev/null @@ -1,241 +0,0 @@ -package org.cobalt.pathfinder.state.impl - -import java.awt.Color -import kotlin.math.abs -import kotlin.random.Random -import net.minecraft.core.BlockPos -import net.minecraft.world.phys.AABB -import org.cobalt.Cobalt.minecraft -import org.cobalt.dsl.centerVec -import org.cobalt.module.impl.misc.Debug -import org.cobalt.module.impl.misc.Rotations -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.helper.PathRenderer -import org.cobalt.pathfinder.helper.PlayerInput -import org.cobalt.pathfinder.movement.MovementHelper -import org.cobalt.pathfinder.state.ExecutorState -import org.cobalt.util.PlayerUtils -import org.cobalt.util.RotationUtils -import org.cobalt.util.WorldRenderUtils -import org.cobalt.util.helper.Clock -import org.cobalt.util.rotation.RotationTarget - -class PathingState : ExecutorState() { - - private val path = PathFindingFacade.path - private val movements = PathFindingFacade.availableMovements - private val jumpDelay = Clock() - - private inline val pathIndex: Int - get() = PathFindingFacade.pathIndex - - override fun exit() { - input.stopMovement() - Rotations.stop() - } - - override fun onTick() { - if (path == null || movements == null) { - PathFindingFacade.stop() - return - } - - val player = PlayerUtils.player ?: return - val nodes = path.nodes - val node = nodes[pathIndex] - val playerPos = PlayerUtils.position - - if ( - advanceReachedNodes(playerPos, nodes) || - shouldStartFlying(node) - ) { - return - } - - val sameXZ = node.block.x == playerPos.x && node.block.z == playerPos.z - val movement = buildPlayerInput(playerPos, node, nodes, sameXZ) - val lookTarget = MovementHelper.getRotationTarget( - player.eyePosition, nodes, pathIndex - ) - - Rotations.track(RotationTarget(lookTarget)) - input.applyInput(movement) - } - - override fun onRender() { - if (path == null || movements == null) { - PathFindingFacade.stop() - return - } - - PathRenderer.render() - - if (!Debug.enabled) { - return - } - - val lookAhead = (PlayerUtils.velocity.length() * 6.0).coerceIn(5.0, 32.0) - val target = MovementHelper.getRotationTarget( - PlayerUtils.player!!.eyePosition, path.nodes, pathIndex, lookAhead - ) - - WorldRenderUtils.drawBox( - AABB( - target.x - 0.25, target.y - 0.25, target.z - 0.25, - target.x + 0.25, target.y + 0.25, target.z + 0.25 - ), Color.GREEN - ) - } - - private fun advanceReachedNodes(playerPos: BlockPos, nodes: List): Boolean { - if (!hasReached(playerPos, nodes, pathIndex)) { - return false - } - - while ( - pathIndex + 1 < nodes.size && - hasReached(playerPos, nodes, pathIndex) - ) { - PathFindingFacade.pathIndex++ - } - - if (pathIndex + 1 >= nodes.size) { - PathFindingFacade.stop() - } - - return true - } - - private fun shouldStartFlying(node: PathNode): Boolean { - requireNotNull(movements) - - if ( - !node.isFly || - PlayerUtils.isFlying - ) { - return false - } - - PathFindingFacade.changeState(StartFlyState()) - return true - } - - private fun buildPlayerInput( - playerPos: BlockPos, - node: PathNode, - nodes: List, - sameXZ: Boolean, - ): PlayerInput { - val index = pathIndex - val input = PlayerInput() - - if (node.isFly && sameXZ) { - val diffY = node.block.y - playerPos.y - when { - diffY > 0 -> input.jump = true - diffY < 0 -> input.sneak = true - } - - if (input.jump || input.sneak) return input - } - - if (!sameXZ) { - val neededKeys = MovementHelper.getNeededKeys( - PlayerUtils.rotation.yaw, - RotationUtils.getRotation(node.centerVec).yaw - ) - - input.apply(neededKeys) - } - - if (PathFindingConfig.shouldSprint) input.sprint = true - - if (!node.isFly) { - if (shouldJump(playerPos, nodes, index)) input.jump = true - } - - if (node.isFly && sameXZ) { - val diffY = node.block.y - playerPos.y - when { - diffY > 0 -> input.jump = true - diffY < 0 -> input.sneak = true - } - } - - return input - } - - private fun hasReached( - playerPos: BlockPos, - nodes: List, - currentIndex: Int, - ): Boolean { - val node = nodes[currentIndex] - - val nodeCenter = node.centerVec - val playerVec = playerPos.centerVec() - - if (playerVec.distanceToSqr(nodeCenter) < PathFindingConfig.hasReachedThreshold) return true - - if (currentIndex + 1 >= nodes.size) return false - - val level = minecraft.level - requireNotNull(level) - - if ( - !MovementHelper.isBottomSlab(level.getBlockState(node.blockStandingOn)) && - (node.block.y > playerPos.y || !PlayerUtils.onGround) - ) { - return false - } - - val segment = nodes[currentIndex + 1].centerVec.subtract(nodeCenter) - val toPlayer = playerVec.subtract(nodeCenter) - - if (toPlayer.dot(segment) < 0.0) { - return false - } - - val squaredPerpendicularDistance = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() - return squaredPerpendicularDistance < 1.0 - } - - private fun shouldJump( - playerPos: BlockPos, - nodes: List, - currentIndex: Int, - ): Boolean { - val level = minecraft.level - requireNotNull(level) - - if (!PlayerUtils.onGround || !jumpDelay.passed()) { - return false - } - - val node = nodes[currentIndex] - - if (node.block.y - playerPos.y < 1 || - MovementHelper.isBottomSlab(level.getBlockState(node.blockStandingOn)) - ) { - return false - } - - val nodeCenter = node.centerVec - val playerVec = playerPos.centerVec() - val dx = abs(nodeCenter.x - playerVec.x) - val dz = abs(nodeCenter.z - playerVec.z) - - if (dx + dz > 1.2 || minOf(dx, dz) > 0.2) { - return false - } - - if (PlayerUtils.canFly) { - jumpDelay.schedule(Random.nextLong(350, 450)) - } - - return true - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt new file mode 100644 index 0000000..04d4cd7 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt @@ -0,0 +1,47 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.core.BlockPos +import org.cobalt.Cobalt +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.movement.rules.BlockSupportRules +import org.cobalt.util.PlayerUtils +import org.cobalt.dsl.centerVec +import org.cobalt.util.helper.Clock +import kotlin.math.abs +import kotlin.random.Random + +class JumpDecision { + private val jumpDelay = Clock() + + fun shouldJump(playerPos: BlockPos, nodes: List, currentIndex: Int): Boolean { + val level = Cobalt.minecraft.level + requireNotNull(level) + + if (!PlayerUtils.onGround || !jumpDelay.passed()) { + return false + } + + val node = nodes[currentIndex] + + if (node.block.y - playerPos.y < 1 || + BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) + ) { + return false + } + + val nodeCenter = node.centerVec + val playerVec = playerPos.centerVec() + val dx = abs(nodeCenter.x - playerVec.x) + val dz = abs(nodeCenter.z - playerVec.z) + + if (dx + dz > 1.2 || minOf(dx, dz) > 0.2) { + return false + } + + if (PlayerUtils.canFly) { + jumpDelay.schedule(Random.nextLong(350, 450)) + } + + return true + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt new file mode 100644 index 0000000..d2304a2 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt @@ -0,0 +1,83 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.core.BlockPos +import net.minecraft.world.phys.Vec3 +import org.cobalt.dsl.centerVec +import org.cobalt.Cobalt +import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.movement.rules.BlockSupportRules +import org.cobalt.util.PlayerUtils + +class PathingAdvancer { + fun advanceIfReached(playerPos: BlockPos, nodes: List): Boolean { + val currentIndex = PathFindingFacade.pathIndex + + if (!hasReached(playerPos, nodes, currentIndex)) { + return false + } + + while ( + PathFindingFacade.pathIndex + 1 < nodes.size && + hasReached(playerPos, nodes, PathFindingFacade.pathIndex) + ) { + PathFindingFacade.pathIndex++ + } + + if (PathFindingFacade.pathIndex + 1 >= nodes.size) { + PathFindingFacade.stop() + } + + return true + } + + private fun hasReached( + playerPos: BlockPos, + nodes: List, + currentIndex: Int, + ): Boolean { + val node = nodes[currentIndex] + + val nodeCenter = node.centerVec + val playerVec = playerPos.centerVec() + + if (playerVec.distanceToSqr(nodeCenter) < PathFindingConfig.hasReachedThreshold) return true + + if (currentIndex + 1 >= nodes.size || !canAdvancePastNode(playerPos, node)) { + return false + } + + if (!isCloseEnoughToSegment(nodeCenter, playerVec, nodes[currentIndex + 1].centerVec)) { + return false + } + + return true + } + + private fun canAdvancePastNode(playerPos: BlockPos, node: PathNode): Boolean { + val level = Cobalt.minecraft.level + requireNotNull(level) + + return BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) || + (node.block.y <= playerPos.y && PlayerUtils.onGround) + } + + private fun isCloseEnoughToSegment( + nodeCenter: Vec3, + playerVec: Vec3, + nextNodeCenter: Vec3, + ): Boolean { + val segment = nextNodeCenter.subtract(nodeCenter) + val toPlayer = playerVec.subtract(nodeCenter) + + if (toPlayer.dot(segment) < 0.0) { + return false + } + + val squaredPerpendicularDistance = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() + return squaredPerpendicularDistance < 1.0 + } +} + + diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt new file mode 100644 index 0000000..9a33ec4 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt @@ -0,0 +1,96 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.core.BlockPos +import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.helper.PlayerInput +import org.cobalt.pathfinder.movement.MovementInputMapper +import org.cobalt.util.PlayerUtils +import org.cobalt.util.RotationUtils + + +class PathingInputBuilder(private val jumpDecision: JumpDecision) { + fun build( + playerPos: BlockPos, + node: PathNode, + nodes: List, + sameXZ: Boolean, + currentIndex: Int, + ): PlayerInput { + return if (node.isFly && sameXZ) { + buildFlyVerticalInput(node, playerPos) + } else { + buildStandardInput(playerPos, node, nodes, sameXZ, currentIndex) + } + } + + private fun buildFlyVerticalInput( + node: PathNode, + playerPos: BlockPos, + ): PlayerInput { + val input = PlayerInput() + + val diffY = node.block.y - playerPos.y + when { + diffY > 0 -> input.jump = true + diffY < 0 -> input.sneak = true + } + + return input + } + + private fun buildStandardInput( + playerPos: BlockPos, + node: PathNode, + nodes: List, + sameXZ: Boolean, + currentIndex: Int, + ): PlayerInput { + val input = PlayerInput() + + applyHorizontalInput(input, node, sameXZ) + applySprintIfNeeded(input) + applyJumpIfNeeded(input, playerPos, nodes, currentIndex, node) + + return input + } + + private fun applyHorizontalInput( + input: PlayerInput, + node: PathNode, + sameXZ: Boolean, + ) { + if (sameXZ) { + return + } + + val neededKeys = MovementInputMapper.getNeededKeys( + PlayerUtils.rotation.yaw, + RotationUtils.getRotation(node.centerVec).yaw + ) + + input.apply(neededKeys) + } + + private fun applySprintIfNeeded(input: PlayerInput) { + if (PathFindingConfig.shouldSprint) { + input.sprint = true + } + } + + private fun applyJumpIfNeeded( + input: PlayerInput, + playerPos: BlockPos, + nodes: List, + currentIndex: Int, + node: PathNode, + ) { + if (node.isFly) { + return + } + + if (jumpDecision.shouldJump(playerPos, nodes, currentIndex)) { + input.jump = true + } + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt new file mode 100644 index 0000000..ea09ed9 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt @@ -0,0 +1,99 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.world.phys.AABB +import org.cobalt.module.impl.misc.Debug +import org.cobalt.module.impl.misc.Rotations +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.helper.PathRenderer +import org.cobalt.pathfinder.movement.PathLookTargetProjector +import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.pathfinder.state.fly.StartFlyState +import org.cobalt.util.PlayerUtils +import org.cobalt.util.WorldRenderUtils +import org.cobalt.util.rotation.RotationTarget +import java.awt.Color + +class PathingState : ExecutorState() { + private val path = PathFindingFacade.path + private val movements = PathFindingFacade.availableMovements + private val jumpDecision = JumpDecision() + private val inputBuilder = PathingInputBuilder(jumpDecision) + private val advancer = PathingAdvancer() + + private inline val pathIndex: Int + get() = PathFindingFacade.pathIndex + + override fun exit() { + input.stopMovement() + Rotations.stop() + } + + override fun onTick() { + if (path == null || movements == null) { + PathFindingFacade.stop() + return + } + + val player = PlayerUtils.player ?: return + val nodes = path.nodes + val node = nodes[pathIndex] + val playerPos = PlayerUtils.position + + if ( + advancer.advanceIfReached(playerPos, nodes) || + shouldStartFlying(node) + ) { + return + } + + val sameXZ = node.block.x == playerPos.x && node.block.z == playerPos.z + val movement = inputBuilder.build(playerPos, node, nodes, sameXZ, pathIndex) + val lookTarget = PathLookTargetProjector.getRotationTarget( + player.eyePosition, nodes, pathIndex + ) + + Rotations.track(RotationTarget(lookTarget)) + input.applyInput(movement) + } + + override fun onRender() { + if (path == null || movements == null) { + PathFindingFacade.stop() + return + } + + PathRenderer.render() + + if (!Debug.enabled) { + return + } + + val lookAhead = (PlayerUtils.velocity.length() * 6.0).coerceIn(5.0, 32.0) + val target = PathLookTargetProjector.getRotationTarget( + PlayerUtils.player!!.eyePosition, path.nodes, pathIndex, lookAhead + ) + + WorldRenderUtils.drawBox( + AABB( + target.x - 0.25, target.y - 0.25, target.z - 0.25, + target.x + 0.25, target.y + 0.25, target.z + 0.25 + ), Color.GREEN + ) + } + + private fun shouldStartFlying(node: PathNode): Boolean { + requireNotNull(movements) + + if ( + !node.isFly || + PlayerUtils.isFlying + ) { + return false + } + + PathFindingFacade.changeState(StartFlyState()) + return true + } + +} diff --git a/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt b/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt index d7276a9..a306efe 100644 --- a/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt +++ b/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt @@ -103,7 +103,11 @@ class DragHandler { } val (snappedX, snappedY) = snapHelper.findAlignmentGuides( - clampedX, clampedY, width, height, windowWidth, windowHeight, otherBounds + clampedX, + clampedY, + width, + height, + otherBounds, ) module.xPos = snappedX / scaleX diff --git a/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt b/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt index 13b1810..cea0999 100644 --- a/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt +++ b/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt @@ -1,6 +1,7 @@ package org.cobalt.ui.helper import kotlin.math.abs +import org.cobalt.util.WindowUtils class SnapHelper(private val snapThreshold: Float = 5f) { @@ -12,8 +13,6 @@ class SnapHelper(private val snapThreshold: Float = 5f) { moduleY: Float, moduleW: Float, moduleH: Float, - screenWidth: Float, - screenHeight: Float, otherModuleBounds: List, ): Pair { val right = moduleX + moduleW @@ -21,8 +20,8 @@ class SnapHelper(private val snapThreshold: Float = 5f) { val bottom = moduleY + moduleH val centerY = moduleY + moduleH / 2f - val xTargets = mutableListOf(0f, screenWidth / 2f, screenWidth) - val yTargets = mutableListOf(0f, screenHeight / 2f, screenHeight) + val xTargets = mutableListOf(0f, WindowUtils.windowWidth / 2f, WindowUtils.windowWidth) + val yTargets = mutableListOf(0f, WindowUtils.windowHeight / 2f, WindowUtils.windowHeight) otherModuleBounds.forEach { bounds -> xTargets.add(bounds.x) @@ -35,15 +34,14 @@ class SnapHelper(private val snapThreshold: Float = 5f) { var snappedX = moduleX var snappedY = moduleY - var bestXDiff = snapThreshold + 1f - var bestYDiff = snapThreshold + 1f + val bestXDiff = snapThreshold + 1f + val bestYDiff = snapThreshold + 1f var xGuide: GuideLine? = null var yGuide: GuideLine? = null fun checkX(target: Float, edge: Float, newX: Float) { val diff = abs(edge - target) if (diff <= snapThreshold && diff < bestXDiff) { - bestXDiff = diff snappedX = newX xGuide = GuideLine(true, target) } @@ -52,7 +50,6 @@ class SnapHelper(private val snapThreshold: Float = 5f) { fun checkY(target: Float, edge: Float, newY: Float) { val diff = abs(edge - target) if (diff <= snapThreshold && diff < bestYDiff) { - bestYDiff = diff snappedY = newY yGuide = GuideLine(false, target) } diff --git a/src/main/kotlin/org/cobalt/util/ChatUtils.kt b/src/main/kotlin/org/cobalt/util/ChatUtils.kt index 2d2beec..f37e6e7 100644 --- a/src/main/kotlin/org/cobalt/util/ChatUtils.kt +++ b/src/main/kotlin/org/cobalt/util/ChatUtils.kt @@ -19,7 +19,7 @@ object ChatUtils { private const val DEFAULT_GRADIENT = "" private const val DEBUG_GRADIENT = "" - private const val PREFIX_START = "$DARK_GRAY[$DARK_GRAY]" + private const val PREFIX_START = "$DARK_GRAY[$DARK_GRAY" private const val PREFIX_END = "$DARK_GRAY] $RESET" private val defaultPrefix = From 5cac3287ed532192f88b0de48c3ac2720c151856 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:09:30 -0400 Subject: [PATCH 20/28] chore: cleanup attempt --- .../org/cobalt/command/impl/MainCommand.kt | 14 +- .../org/cobalt/pathfinder/PathConfig.kt | 22 ++ .../{PathFindingFacade.kt => PathExecutor.kt} | 45 ++- .../cobalt/pathfinder/PathFindingConfig.kt | 8 - .../pathfinder/{helper => }/PathRenderer.kt | 28 +- .../cobalt/pathfinder/calculate/PathNode.kt | 3 +- .../calculate/path/AStarPathfinder.kt | 86 +++--- .../pathfinder/{ => helper}/PathInput.kt | 17 +- .../pathfinder/movement/CalculationContext.kt | 1 + .../pathfinder/movement/FlowingCheck.kt | 32 --- .../movement/MovementInputMapper.kt | 83 ------ ...TraversalRules.kt => MovementValidator.kt} | 13 +- .../movement/PathLookTargetProjector.kt | 271 ------------------ .../movement/fly/FlyAscendMovement.kt | 9 +- .../movement/fly/FlyDescendMovement.kt | 9 +- .../movement/fly/FlyDiagonalMovement.kt | 13 +- .../movement/fly/FlyTraverseMovement.kt | 9 +- .../movement/rules/PassthroughRules.kt | 6 +- .../{StandOnRules.kt => StandingRules.kt} | 23 +- .../movement/rules/data/BlockRule.kt | 2 + .../movement/rules/data/BlockSetRule.kt | 2 + .../movement/rules/data/BlockTypeRule.kt | 6 +- .../movement/walk/AscendMovement.kt | 15 +- .../movement/walk/DescendMovement.kt | 11 +- .../movement/walk/DiagonalMovement.kt | 13 +- .../movement/walk/TraverseMovement.kt | 9 +- .../cobalt/pathfinder/state/ExecutorState.kt | 6 +- .../state/calculation/CalculatingState.kt | 45 ++- .../pathfinder/state/fly/StartFlyState.kt | 10 +- .../pathfinder/state/pathing/JumpDecision.kt | 47 --- .../state/pathing/PathingAdvancer.kt | 83 ------ .../state/pathing/PathingInputBuilder.kt | 96 ------- .../pathfinder/state/pathing/PathingState.kt | 96 +------ .../BlockUtils.kt} | 42 ++- 34 files changed, 286 insertions(+), 889 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt rename src/main/kotlin/org/cobalt/pathfinder/{PathFindingFacade.kt => PathExecutor.kt} (76%) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt rename src/main/kotlin/org/cobalt/pathfinder/{helper => }/PathRenderer.kt (58%) rename src/main/kotlin/org/cobalt/pathfinder/{ => helper}/PathInput.kt (85%) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt rename src/main/kotlin/org/cobalt/pathfinder/movement/{rules/BlockTraversalRules.kt => MovementValidator.kt} (66%) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt rename src/main/kotlin/org/cobalt/pathfinder/movement/rules/{StandOnRules.kt => StandingRules.kt} (80%) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt rename src/main/kotlin/org/cobalt/{pathfinder/movement/rules/BlockSupportRules.kt => util/BlockUtils.kt} (52%) diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 94593f3..a7ceb42 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -4,7 +4,9 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.command.Command import org.cobalt.command.annotation.DefaultHandler import org.cobalt.command.annotation.SubCommand -import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.PathConfig +import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen @@ -30,12 +32,18 @@ object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { @SubCommand fun goTo(x: Int, y: Int, z: Int, fly: Boolean) { - PathFindingFacade.goTo(GoalBlock(x, y, z), fly) + val pathMode = if (fly) PathMode.FLY else PathMode.WALK + val config = PathConfig( + goal = GoalBlock(x, y, z), + mode = pathMode + ) + + PathExecutor.goTo(config) } @SubCommand fun stopPathfinder() { - PathFindingFacade.stop() + PathExecutor.stop() } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt new file mode 100644 index 0000000..c99007f --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt @@ -0,0 +1,22 @@ +package org.cobalt.pathfinder + +import org.cobalt.pathfinder.calculate.PathMode +import org.cobalt.pathfinder.goal.Goal + +class PathConfig( + val goal: Goal, + val mode: PathMode = PathMode.WALK, + val shouldSprint: Boolean = true, + val returnBestNode: Boolean = false, + val maxCalculationTime: Long = MAX_CALCULATION_TIME, + val hasReachedThreshold: Double = 0.09, +) { + + val allowFly: Boolean = + mode == PathMode.FLY + + companion object { + const val MAX_CALCULATION_TIME = 10_000_000_000L + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt similarity index 76% rename from src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt rename to src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt index 4e94eb7..1656a99 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt @@ -7,20 +7,17 @@ import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.TickEvent import org.cobalt.event.impl.WorldEvent import org.cobalt.pathfinder.calculate.Path -import org.cobalt.pathfinder.calculate.PathMode -import org.cobalt.pathfinder.goal.Goal -import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.helper.PathInput import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.calculation.CalculatingState import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType import org.cobalt.util.PlayerUtils -object PathFindingFacade { - var state: ExecutorState? = null +object PathExecutor { - var currentGoal: Goal? = null - var availableMovements: Array? = null + var state: ExecutorState? = null + var config: PathConfig? = null var path: Path? = null var pathIndex: Int = 0 @@ -32,37 +29,33 @@ object PathFindingFacade { EventBus.register(this) } - fun goTo(goal: Goal, fly: Boolean = false) { - // Are we sure we want to do this? There's no warning or anything. - // I've kept it but with a debug message - if (running) { + fun goTo(config: PathConfig) { + val player = minecraft.player ?: run { ChatUtils.sendSystemMessage( - "Stopping current pathfinder because a new one started.", + "Tried running pathfinder, but minecraft.player is null!", MessageType.DEBUG ) - stop() - } - if (fly && !PlayerUtils.canFly) { - ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") return } - val player = minecraft.player - - if (player == null) { + if (running) { ChatUtils.sendSystemMessage( - "Tried running pathfinder, but minecraft.player is null!", + "Stopping current pathfinder because a new one started.", MessageType.DEBUG ) + + stop() + } + + if (config.allowFly && !PlayerUtils.canFly) { + ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") return - } else { - player.input = pathInput } - availableMovements = if (fly) PathMode.FLY.movements else PathMode.WALK.movements - currentGoal = goal + player.input = pathInput + this.config = config this.running = true changeState(CalculatingState()) @@ -120,6 +113,10 @@ object PathFindingFacade { return } + if (state !is CalculatingState) { + PathRenderer.render() + } + state?.onRender() } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt deleted file mode 100644 index 02d351f..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.cobalt.pathfinder - -object PathFindingConfig { - var shouldSprint: Boolean = true - var returnBestNode: Boolean = false - var maxCalculationTime: Long = 10_000_000_000L - var hasReachedThreshold: Double = 0.09 // // = sqrt(0.3), so 0.3 blocks of distance is the threshold -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt similarity index 58% rename from src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt rename to src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt index d503cd3..82d5005 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt @@ -1,22 +1,27 @@ -package org.cobalt.pathfinder.helper +package org.cobalt.pathfinder -import java.awt.Color import org.cobalt.dsl.centerVec import org.cobalt.dsl.smallBox import org.cobalt.module.impl.misc.Debug -import org.cobalt.pathfinder.PathFindingFacade.path -import org.cobalt.pathfinder.PathFindingFacade.pathIndex import org.cobalt.ui.theme.ThemeManager import org.cobalt.util.PlayerUtils import org.cobalt.util.WorldRenderUtils +import java.awt.Color +import org.cobalt.pathfinder.calculate.Path +import org.cobalt.ui.theme.Theme object PathRenderer { + + private inline val theme: Theme + get() = ThemeManager.activeTheme + fun render() { - val theme = ThemeManager.activeTheme - val nodes = path?.nodes ?: return - val keyNodes = path?.keyNodes ?: return + val path: Path = PathExecutor.path ?: return + + val nodes = path.nodes + val keyNodes = path.keyNodes - val targetNode = nodes[pathIndex].centerVec + val targetNode = nodes[PathExecutor.pathIndex].centerVec val playerPos = PlayerUtils.position.centerVec() if (Debug.enabled) { @@ -28,7 +33,7 @@ object PathRenderer { val node = keyNodes[index] WorldRenderUtils.drawBlockPos( - if (node.isFly) node.block else node.blockStandingOn, + if (node.useMovementFly) node.block else node.blockStandingOn, color = theme.accentPrimary ) @@ -37,10 +42,11 @@ object PathRenderer { val prev = keyNodes[index - 1] WorldRenderUtils.drawLine( - if (prev.isFly) prev.centerVec else prev.topCenterVec, - if (node.isFly) node.centerVec else node.topCenterVec, + if (prev.useMovementFly) prev.centerVec else prev.topCenterVec, + if (node.useMovementFly) node.centerVec else node.topCenterVec, theme.accentSecondary ) } } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt index dfac851..901013a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt @@ -16,6 +16,7 @@ data class PathNode( val costToEnd = goal.heuristic(x, y, z) var totalCost = 1.0 var heapPosition = -1 + var movementType = MovementType.WALK var parent: PathNode? = null @@ -24,7 +25,7 @@ data class PathNode( val centerVec: Vec3 = Vec3(x + 0.5, y + 0.5, z + 0.5) val topCenterVec: Vec3 = Vec3(x + 0.5, y.toDouble(), z + 0.5) - val isFly: Boolean + val useMovementFly: Boolean get() = movementType == MovementType.FLY override fun compareTo(other: PathNode): Int { diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 608aeb9..daaf817 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -1,9 +1,10 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.nanoseconds -import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.Path +import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.calculate.openset.BinaryHeapOpenSet import org.cobalt.pathfinder.goal.Goal @@ -12,68 +13,77 @@ import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType -import org.cobalt.util.PlayerUtils -object AStarPathfinder { - private val nodeCache = Long2ObjectOpenHashMap() - private var goal: Goal? = null // This can't be null after findPath is called. - private var availableMovements: Array? = null // This can't be null after findPath is called. - - fun findPath(goal: Goal, movements: Array, returnBestNode: Boolean): Path? { - val startPos = PlayerUtils.position +class AStarPathfinder( + val startX: Int, + val startY: Int, + val startZ: Int, + val goal: Goal, + val mode: PathMode, + val returnBestNode: Boolean = false, + val maxCalculationTime: Long = 10_000_000L +) { + + private val closedSet = Long2ObjectOpenHashMap() + private val movements = mode.movements + private var startTime = 0L + + fun findPath(): Path? { val calculationContext = CalculationContext() val openSet = BinaryHeapOpenSet() val movementResult = MovementResult() - // After this, goal and movements should never be null again, so we can do !! in private functions safely. - // If they are null, someone nuked the code, and we should get angry at them. - AStarPathfinder.goal = goal - availableMovements = movements - - val startNode = PathNode(startPos.x, startPos.y, startPos.z, goal) - - startNode.costSoFar = 0.0 - startNode.totalCost = startNode.costToEnd + val startNode = PathNode( + startX, startY, startZ, goal + ).also { + it.costSoFar = 0.0 + it.totalCost = it.costToEnd + } openSet.add(startNode) + startTime = System.nanoTime() - val startTime = System.nanoTime() var bestNode = startNode + val deadline = startTime + maxCalculationTime - val deadline = startTime + PathFindingConfig.maxCalculationTime ChatUtils.sendSystemMessage( "Starting pathfinding from ${startNode.block}", MessageType.DEBUG ) - while (!openSet.isEmpty() - && System.nanoTime() < deadline - ) { + while (!openSet.isEmpty() && System.nanoTime() < deadline) { val currentNode = openSet.poll() - if (currentNode < bestNode) bestNode = currentNode + if (currentNode < bestNode) { + bestNode = currentNode + } if (goal.isAtGoal(currentNode.x, currentNode.y, currentNode.z)) { - return reconstruct(currentNode, startTime) + return reconstruct(currentNode) } - evaluateMovements(currentNode, calculationContext, movementResult, openSet) + evaluateMovements( + calculationContext, + currentNode, + movementResult, + openSet + ) } return if (returnBestNode) { - reconstruct(bestNode, startTime) + reconstruct(bestNode) } else { null } } private fun evaluateMovements( - currentNode: PathNode, calculationContext: CalculationContext, + currentNode: PathNode, movementResult: MovementResult, openSet: BinaryHeapOpenSet, ) { - for (move in availableMovements!!) { + for (move in movements) { movementResult.reset() move.calculateCost(calculationContext, currentNode, movementResult) @@ -90,12 +100,14 @@ object AStarPathfinder { openSet: BinaryHeapOpenSet, ) { val neighborCostSoFar = currentNode.costSoFar + movementResult.cost - val neighborNode = getOrCacheNode( + val neighborNode = getNode( movementResult.x, movementResult.y, movementResult.z, PathNode.longHash(movementResult.x, movementResult.y, movementResult.z) ) - if (neighborCostSoFar >= neighborNode.costSoFar) return + if (neighborCostSoFar >= neighborNode.costSoFar) { + return + } neighborNode.parent = currentNode neighborNode.costSoFar = neighborCostSoFar @@ -109,18 +121,18 @@ object AStarPathfinder { } } - fun getOrCacheNode(x: Int, y: Int, z: Int, hash: Long): PathNode { - var node: PathNode? = nodeCache.get(hash) + fun getNode(x: Int, y: Int, z: Int, hash: Long): PathNode { + var node: PathNode? = closedSet.get(hash) if (node == null) { - node = PathNode(x, y, z, goal!!) - nodeCache.put(hash, node) + node = PathNode(x, y, z, goal) + closedSet.put(hash, node) } return node } - private fun reconstruct(endNode: PathNode, startTime: Long): Path { + private fun reconstruct(endNode: PathNode): Path { val path = mutableListOf() var node: PathNode? = endNode @@ -132,7 +144,7 @@ object AStarPathfinder { return Path( nodes = path, timeElapsed = (System.nanoTime() - startTime).nanoseconds, - goal = goal!! + goal = goal ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt similarity index 85% rename from src/main/kotlin/org/cobalt/pathfinder/PathInput.kt rename to src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt index 96152ec..70d7886 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt @@ -1,9 +1,8 @@ -package org.cobalt.pathfinder +package org.cobalt.pathfinder.helper import net.minecraft.client.player.ClientInput import net.minecraft.world.entity.player.Input import net.minecraft.world.phys.Vec2 -import org.cobalt.pathfinder.helper.PlayerInput class PathInput : ClientInput() { @@ -31,13 +30,13 @@ class PathInput : ClientInput() { override fun tick() { this.keyPresses = Input( - forward, - backward, - left, - right, - jump, - shift, - sprint + forward, + backward, + left, + right, + jump, + shift, + sprint ) val forwardImpulse = calculateImpulse(forward, backward) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt index 3dfefae..5733196 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt @@ -15,4 +15,5 @@ class CalculationContext { val jumpAmplifier = player.getEffect(MobEffects.JUMP_BOOST)?.amplifier ?: -1 val blockStateAccessor = BlockStateAccessor(level) + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt deleted file mode 100644 index d30886f..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.cobalt.pathfinder.movement - -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.level.material.FlowingFluid -import org.cobalt.pathfinder.helper.BlockStateAccessor - -object FlowingCheck { - private fun possiblyFlowing(state: BlockState): Boolean { - val fluidState = state.fluidState - - return fluidState.type is FlowingFluid && - fluidState.type.getAmount(fluidState) != 8 - } - - fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { - val fluidState = state.fluidState - - if (fluidState.type !is FlowingFluid) { - return false - } - - if (fluidState.type.getAmount(fluidState) != 8) { - return true - } - - return possiblyFlowing(bsa.get(x + 1, y, z)) - || possiblyFlowing(bsa.get(x - 1, y, z)) - || possiblyFlowing(bsa.get(x, y, z + 1)) - || possiblyFlowing(bsa.get(x, y, z - 1)) - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt deleted file mode 100644 index bf406c0..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.cobalt.pathfinder.movement - -import net.minecraft.util.Mth -import org.cobalt.pathfinder.helper.PlayerInput - -object MovementInputMapper { - private const val DIRECTIONS_COUNT = 8 - private const val FULL_CIRCLE_DEGREES = 360f - private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT - private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f - - private const val DEAD_BAND_DEGREES = 0.05f - - private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES - private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - - private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES - private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES - private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES - private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES - - private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val BACK_RIGHT_HOLD_DEGREES = BACK_RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val BACKWARD_HOLD_DEGREES = BACKWARD_START_DEGREES + DEAD_BAND_DEGREES - - private const val FORWARD_LEFT_HOLD_DEGREES = FORWARD_LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val LEFT_HOLD_DEGREES = LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES - - private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } - - private var lastSector: Sector = Sector.FORWARD - - @JvmStatic - fun getNeededKeys(playerYaw: Float, idealYaw: Float): PlayerInput { - val diff = Mth.wrapDegrees(idealYaw - playerYaw) - - if (staysInSector(lastSector, diff)) { - return sectorToInput(lastSector) - } - - val newSector = classify(diff) - lastSector = newSector - return sectorToInput(newSector) - } - - private fun staysInSector(sector: Sector, diff: Float): Boolean = when (sector) { - Sector.FORWARD -> diff in FORWARD_LEFT_HOLD_DEGREES.. diff >= FORWARD_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < RIGHT_HOLD_DEGREES - Sector.RIGHT -> diff >= RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_RIGHT_HOLD_DEGREES - Sector.BACK_RIGHT -> diff >= BACK_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACKWARD_HOLD_DEGREES - Sector.BACKWARD -> diff >= BACKWARD_START_DEGREES - DEAD_BAND_DEGREES || diff < BACKWARD_NEG_HOLD_DEGREES - Sector.BACK_LEFT -> diff >= BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_LEFT_HOLD_DEGREES - Sector.LEFT -> diff >= BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < LEFT_HOLD_DEGREES - Sector.FORWARD_LEFT -> diff >= LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < FORWARD_LEFT_HOLD_DEGREES - } - - private fun classify(diff: Float): Sector = when (diff) { - in FORWARD_LEFT_START_DEGREES.. Sector.FORWARD - in FORWARD_RIGHT_START_DEGREES.. Sector.FORWARD_RIGHT - in RIGHT_START_DEGREES.. Sector.RIGHT - in BACK_RIGHT_START_DEGREES.. Sector.BACK_RIGHT - !in BACKWARD_NEG_START_DEGREES.. Sector.BACKWARD - in BACKWARD_NEG_START_DEGREES.. Sector.BACK_LEFT - in BACK_LEFT_START_DEGREES.. Sector.LEFT - else -> Sector.FORWARD_LEFT - } - - private fun sectorToInput(sector: Sector): PlayerInput = when (sector) { - Sector.FORWARD -> PlayerInput(forward = true) - Sector.FORWARD_RIGHT -> PlayerInput(forward = true, right = true) - Sector.RIGHT -> PlayerInput(right = true) - Sector.BACK_RIGHT -> PlayerInput(backward = true, right = true) - Sector.BACKWARD -> PlayerInput(backward = true) - Sector.BACK_LEFT -> PlayerInput(backward = true, left = true) - Sector.LEFT -> PlayerInput(left = true) - Sector.FORWARD_LEFT -> PlayerInput(forward = true, left = true) - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt similarity index 66% rename from src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt index 75bbe2e..5bc1f2d 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt @@ -1,15 +1,13 @@ -package org.cobalt.pathfinder.movement.rules +package org.cobalt.pathfinder.movement -import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.rules.PassthroughRules.canPassThrough -import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn +import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn + +object MovementValidator { -object BlockTraversalRules { @JvmStatic fun canWalkOn(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { - if ( - !canWalkThrough(calculationContext, x, y + 1, z) - ) { + if (!canWalkThrough(calculationContext, x, y + 1, z)) { return false } @@ -21,4 +19,5 @@ object BlockTraversalRules { return canPassThrough(calculationContext.blockStateAccessor, x, y, z) && canPassThrough(calculationContext.blockStateAccessor, x, y + 1, z) } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt deleted file mode 100644 index 887bcfc..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt +++ /dev/null @@ -1,271 +0,0 @@ -package org.cobalt.pathfinder.movement - -import net.minecraft.world.phys.Vec3 -import org.cobalt.pathfinder.calculate.PathNode - -object PathLookTargetProjector { - private const val SEARCH_BACKWARD_SEGMENTS = 1 - private const val SEARCH_FORWARD_SEGMENTS = 3 - private const val ZERO = 0.0 - private const val FULL_SEGMENT = 1.0 - - private data class ClosestSegment( - val index: Int, - val progress: Double, - ) - - private data class SegmentProjection( - val progress: Double, - val distanceSquared: Double, - ) - - @JvmStatic - fun getRotationTarget( - playerEyePosition: Vec3, - pathNodes: List, - currentPathIndex: Int, - lookAheadDistance: Double = 3.0, - ): Vec3 { - val closestSegment = findClosestSegment( - playerEyePosition, - pathNodes, - currentPathIndex - ) - - val horizontalTarget = moveAlongPath( - pathNodes, - closestSegment, - lookAheadDistance - ) - - return Vec3( - horizontalTarget.x, - playerEyePosition.y, - horizontalTarget.z - ) - } - - private fun findClosestSegment( - playerEyePosition: Vec3, - pathNodes: List, - currentPathIndex: Int, - ): ClosestSegment { - val playerPosition = Vec3( - playerEyePosition.x, - ZERO, - playerEyePosition.z - ) - - var closestSegment = ClosestSegment( - currentPathIndex, - ZERO - ) - - var closestDistanceSquared = Double.MAX_VALUE - - val startIndex = maxOf( - ZERO.toInt(), - currentPathIndex - SEARCH_BACKWARD_SEGMENTS - ) - - val endIndex = minOf( - pathNodes.lastIndex, - currentPathIndex + SEARCH_FORWARD_SEGMENTS - ) - - for (segmentIndex in startIndex until endIndex) { - val projection = findClosestPointOnSegment( - playerPosition, - pathNodes[segmentIndex].centerVec, - pathNodes[segmentIndex + 1].centerVec - ) - - if (projection == null) { - continue - } - - if (projection.distanceSquared < closestDistanceSquared) { - closestDistanceSquared = projection.distanceSquared - closestSegment = ClosestSegment( - segmentIndex, - projection.progress - ) - } - } - - return closestSegment - } - - private fun findClosestPointOnSegment( - point: Vec3, - start: Vec3, - end: Vec3, - ): SegmentProjection? { - val direction = Vec3( - end.x - start.x, - ZERO, - end.z - start.z - ) - - val lengthSquared = - direction.x * direction.x + - direction.z * direction.z - - if (lengthSquared == ZERO) { - return null - } - - val progress = - ( - (point.x - start.x) * direction.x + - (point.z - start.z) * direction.z - ) - .coerceIn(ZERO, lengthSquared) / lengthSquared - - val closestX = start.x + direction.x * progress - val closestZ = start.z + direction.z * progress - - val distanceX = point.x - closestX - val distanceZ = point.z - closestZ - - return SegmentProjection( - progress, - distanceX * distanceX + distanceZ * distanceZ - ) - } - - private fun moveAlongPath( - pathNodes: List, - startingSegment: ClosestSegment, - distance: Double, - ): Vec3 { - var remainingDistance = distance - - val currentSegmentResult = moveOnSegment( - pathNodes, - startingSegment.index, - startingSegment.progress, - remainingDistance - ) - - if (currentSegmentResult != null) { - return currentSegmentResult - } - - remainingDistance -= distanceRemainingOnSegment( - pathNodes, - startingSegment - ) - - for (segmentIndex in startingSegment.index + 1 until pathNodes.lastIndex) { - val result = moveOnSegment( - pathNodes, - segmentIndex, - ZERO, - remainingDistance - ) - - if (result != null) { - return result - } - - remainingDistance -= segmentLength( - pathNodes, - segmentIndex - ) - } - - return extrapolatePastEnd( - pathNodes, - remainingDistance - ) - } - - private fun moveOnSegment( - pathNodes: List, - segmentIndex: Int, - startProgress: Double, - distance: Double, - ): Vec3? { - val start = pathNodes[segmentIndex].centerVec - val end = pathNodes[segmentIndex + 1].centerVec - - val direction = Vec3( - end.x - start.x, - ZERO, - end.z - start.z - ) - - val length = direction.length() - - if (length == ZERO) { - return null - } - - val availableDistance = - (FULL_SEGMENT - startProgress) * length - - if (distance > availableDistance) { - return null - } - - val progress = - startProgress + distance / length - - return start.add( - direction.scale(progress) - ) - } - - private fun distanceRemainingOnSegment( - pathNodes: List, - segment: ClosestSegment, - ): Double { - return (FULL_SEGMENT - segment.progress) * - segmentLength(pathNodes, segment.index) - } - - private fun segmentLength( - pathNodes: List, - segmentIndex: Int, - ): Double { - val start = pathNodes[segmentIndex].centerVec - val end = pathNodes[segmentIndex + 1].centerVec - - return Vec3( - end.x - start.x, - ZERO, - end.z - start.z - ).length() - } - - private fun extrapolatePastEnd( - pathNodes: List, - distance: Double, - ): Vec3 { - val last = pathNodes.last() - - val previous = - if (pathNodes.size > 1) { - pathNodes[pathNodes.lastIndex - 1] - } else { - last - } - - val direction = Vec3( - (last.x - previous.x).toDouble(), - ZERO, - (last.z - previous.z).toDouble() - ) - - val length = direction.length() - - if (length == ZERO) { - return last.centerVec - } - - return last.centerVec.add( - direction.scale(distance / length) - ) - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt index 9b33671..5514c36 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyAscendMovement : Movement(MovementType.FLY) { @@ -15,7 +18,7 @@ class FlyAscendMovement : Movement(MovementType.FLY) { val y = currNode.y + 1 val z = currNode.z - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt index 92f136a..46a94b0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyDescendMovement : Movement(MovementType.FLY) { @@ -15,7 +18,7 @@ class FlyDescendMovement : Movement(MovementType.FLY) { val y = currNode.y - 1 val z = currNode.z - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt index 5078010..75d35fd 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyDiagonalMovement( val dx: Int, @@ -18,15 +21,15 @@ class FlyDiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt index 89bf73a..86d319f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyTraverseMovement( val dx: Int, @@ -18,7 +21,7 @@ class FlyTraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt index a14c950..654b82f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt @@ -21,11 +21,11 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties import net.minecraft.world.level.material.WaterFluid import net.minecraft.world.level.pathfinder.PathComputationType import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.movement.FlowingCheck -import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn +import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary +import org.cobalt.util.BlockUtils object PassthroughRules { @@ -152,7 +152,7 @@ object PassthroughRules { !state.fluidState.isEmpty -> { val fluidState = state.fluidState - if (FlowingCheck.isFlowing(x, y, z, state, bsa)) { + if (BlockUtils.isFlowing(x, y, z, state, bsa)) { false } else { val upState = bsa.get(x, y + 1, z) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt similarity index 80% rename from src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt index 07a24a4..a7d71e5 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt @@ -10,14 +10,14 @@ import net.minecraft.world.level.block.StairBlock import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.material.Fluids import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.movement.FlowingCheck import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary +import org.cobalt.util.BlockUtils -object StandOnRules { +object StandingRules { - val STAND_ON_RULES = listOf( + val STANDING_RULES = listOf( BlockSetRule( setOf( Blocks.LADDER, @@ -57,19 +57,19 @@ object StandOnRules { private fun compute(state: BlockState): Ternary { val block = state.block - if (BlockSupportRules.isBlockNormalCube(state) && + if (BlockUtils.isBlockNormalCube(state) && block != Blocks.BUBBLE_COLUMN && block != Blocks.HONEY_BLOCK ) { return Ternary.YES } - STAND_ON_RULES.firstOrNull { it.matches(state) } + STANDING_RULES.firstOrNull { it.matches(state) } ?.let { return it.result } return when { - BlockSupportRules.isWater(state) -> Ternary.MAYBE - BlockSupportRules.isLava(state) -> Ternary.MAYBE + BlockUtils.isWater(state) -> Ternary.MAYBE + BlockUtils.isLava(state) -> Ternary.MAYBE else -> Ternary.NO } } @@ -90,7 +90,7 @@ object StandOnRules { } private fun resolveMaybe(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { - if (BlockSupportRules.isWater(state)) { + if (BlockUtils.isWater(state)) { val upState = bsa.get(x, y + 1, z) val upBlock = upState.block @@ -98,13 +98,14 @@ object StandOnRules { return true } - if (FlowingCheck.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { - return BlockSupportRules.isWater(upState) + if (BlockUtils.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { + return BlockUtils.isWater(upState) } - return BlockSupportRules.isWater(upState) xor false + return BlockUtils.isWater(upState) xor false } return false } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt index a7c22da..09fe992 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt @@ -3,7 +3,9 @@ package org.cobalt.pathfinder.movement.rules.data import net.minecraft.world.level.block.state.BlockState interface BlockRule { + val result: Ternary fun matches(state: BlockState): Boolean + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt index 0f8f0ca..1a949f4 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt @@ -7,6 +7,8 @@ class BlockSetRule( private val blocks: Set, override val result: Ternary, ) : BlockRule { + override fun matches(state: BlockState): Boolean = state.block in blocks + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt index c720ee0..3d837a6 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt @@ -4,9 +4,11 @@ import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState class BlockTypeRule( - private val types: Set>, - override val result: Ternary, + private val types: Set>, + override val result: Ternary, ) : BlockRule { + override fun matches(state: BlockState): Boolean = types.any { it.isInstance(state.block) } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt index 9954540..0a4ecb0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( @@ -19,11 +22,15 @@ class AscendMovement( val y = currNode.y + 1 val z = currNode.z + dz - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, currNode.x, currNode.y, currNode.z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { + return + } + + if (!MovementValidator.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt index 39a26dc..2466173 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( @@ -19,11 +22,11 @@ class DescendMovement( val y = currNode.y - 1 val z = currNode.z + dz - if (!BlockTraversalRules.canWalkThrough(ctx, x, currNode.y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, currNode.y, z)) { return } - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt index 8ea0bef..6e7b5a3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class DiagonalMovement( val dx: Int, @@ -18,15 +21,15 @@ class DiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt index 57b8585..e824ee6 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class TraverseMovement( val dx: Int, @@ -18,7 +21,7 @@ class TraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt index fd3c7ff..58a793f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt @@ -1,12 +1,12 @@ package org.cobalt.pathfinder.state -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.PathInput +import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.helper.PathInput abstract class ExecutorState { protected val input: PathInput = - PathFindingFacade.pathInput + PathExecutor.pathInput open fun enter() {} open fun onTick() {} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt index 3e1b16d..948d186 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt @@ -5,49 +5,47 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.ChatUtils +import org.cobalt.util.PlayerUtils class CalculatingState : ExecutorState() { + private var calculationJob: Job? = null override fun enter() { - val goal = PathFindingFacade.currentGoal - val availableMovements = PathFindingFacade.availableMovements - - if (goal == null) { - ChatUtils.sendSystemMessage("Cannot calculate path, no goal set!") - PathFindingFacade.stop() + val config = PathExecutor.config ?: run { + ChatUtils.sendSystemMessage("Cannot calculate path, no path config set!") + PathExecutor.stop() return } - if (availableMovements == null) { - ChatUtils.sendSystemMessage("Cannot calculate path, no movements set!") - PathFindingFacade.stop() - return - } + val startPos = PlayerUtils.position + val goal = config.goal calculationJob = CoroutineScope(Dispatchers.Default).launch { - val path = AStarPathfinder.findPath( - goal, - availableMovements, - PathFindingConfig.returnBestNode - ) - - if (!isActive) return@launch + val path = AStarPathfinder( + startPos.x, startPos.y, startPos.z, + goal, config.mode, + config.returnBestNode, + config.maxCalculationTime + ).findPath() + + if (!isActive) { + return@launch + } if (path == null) { ChatUtils.sendSystemMessage("Unable to find a path") - PathFindingFacade.stop() + PathExecutor.stop() return@launch } - PathFindingFacade.path = path - PathFindingFacade.changeState(PathingState()) + PathExecutor.path = path + PathExecutor.changeState(PathingState()) } } @@ -55,4 +53,5 @@ class CalculatingState : ExecutorState() { calculationJob?.cancel() calculationJob = null } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt index ac27ae3..354a689 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt @@ -1,7 +1,6 @@ package org.cobalt.pathfinder.state.fly -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.helper.PathRenderer +import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.PlayerUtils @@ -19,11 +18,7 @@ class StartFlyState : ExecutorState() { return } - PathFindingFacade.changeState(PathingState()) - } - - override fun onRender() { - PathRenderer.render() + PathExecutor.changeState(PathingState()) } private fun handleFlyStart(): Boolean { @@ -68,4 +63,5 @@ class StartFlyState : ExecutorState() { SECOND_JUMP, RELEASE_SECOND_JUMP, } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt deleted file mode 100644 index 04d4cd7..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.cobalt.pathfinder.state.pathing - -import net.minecraft.core.BlockPos -import org.cobalt.Cobalt -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.rules.BlockSupportRules -import org.cobalt.util.PlayerUtils -import org.cobalt.dsl.centerVec -import org.cobalt.util.helper.Clock -import kotlin.math.abs -import kotlin.random.Random - -class JumpDecision { - private val jumpDelay = Clock() - - fun shouldJump(playerPos: BlockPos, nodes: List, currentIndex: Int): Boolean { - val level = Cobalt.minecraft.level - requireNotNull(level) - - if (!PlayerUtils.onGround || !jumpDelay.passed()) { - return false - } - - val node = nodes[currentIndex] - - if (node.block.y - playerPos.y < 1 || - BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) - ) { - return false - } - - val nodeCenter = node.centerVec - val playerVec = playerPos.centerVec() - val dx = abs(nodeCenter.x - playerVec.x) - val dz = abs(nodeCenter.z - playerVec.z) - - if (dx + dz > 1.2 || minOf(dx, dz) > 0.2) { - return false - } - - if (PlayerUtils.canFly) { - jumpDelay.schedule(Random.nextLong(350, 450)) - } - - return true - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt deleted file mode 100644 index d2304a2..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.cobalt.pathfinder.state.pathing - -import net.minecraft.core.BlockPos -import net.minecraft.world.phys.Vec3 -import org.cobalt.dsl.centerVec -import org.cobalt.Cobalt -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.rules.BlockSupportRules -import org.cobalt.util.PlayerUtils - -class PathingAdvancer { - fun advanceIfReached(playerPos: BlockPos, nodes: List): Boolean { - val currentIndex = PathFindingFacade.pathIndex - - if (!hasReached(playerPos, nodes, currentIndex)) { - return false - } - - while ( - PathFindingFacade.pathIndex + 1 < nodes.size && - hasReached(playerPos, nodes, PathFindingFacade.pathIndex) - ) { - PathFindingFacade.pathIndex++ - } - - if (PathFindingFacade.pathIndex + 1 >= nodes.size) { - PathFindingFacade.stop() - } - - return true - } - - private fun hasReached( - playerPos: BlockPos, - nodes: List, - currentIndex: Int, - ): Boolean { - val node = nodes[currentIndex] - - val nodeCenter = node.centerVec - val playerVec = playerPos.centerVec() - - if (playerVec.distanceToSqr(nodeCenter) < PathFindingConfig.hasReachedThreshold) return true - - if (currentIndex + 1 >= nodes.size || !canAdvancePastNode(playerPos, node)) { - return false - } - - if (!isCloseEnoughToSegment(nodeCenter, playerVec, nodes[currentIndex + 1].centerVec)) { - return false - } - - return true - } - - private fun canAdvancePastNode(playerPos: BlockPos, node: PathNode): Boolean { - val level = Cobalt.minecraft.level - requireNotNull(level) - - return BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) || - (node.block.y <= playerPos.y && PlayerUtils.onGround) - } - - private fun isCloseEnoughToSegment( - nodeCenter: Vec3, - playerVec: Vec3, - nextNodeCenter: Vec3, - ): Boolean { - val segment = nextNodeCenter.subtract(nodeCenter) - val toPlayer = playerVec.subtract(nodeCenter) - - if (toPlayer.dot(segment) < 0.0) { - return false - } - - val squaredPerpendicularDistance = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() - return squaredPerpendicularDistance < 1.0 - } -} - - diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt deleted file mode 100644 index 9a33ec4..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt +++ /dev/null @@ -1,96 +0,0 @@ -package org.cobalt.pathfinder.state.pathing - -import net.minecraft.core.BlockPos -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.helper.PlayerInput -import org.cobalt.pathfinder.movement.MovementInputMapper -import org.cobalt.util.PlayerUtils -import org.cobalt.util.RotationUtils - - -class PathingInputBuilder(private val jumpDecision: JumpDecision) { - fun build( - playerPos: BlockPos, - node: PathNode, - nodes: List, - sameXZ: Boolean, - currentIndex: Int, - ): PlayerInput { - return if (node.isFly && sameXZ) { - buildFlyVerticalInput(node, playerPos) - } else { - buildStandardInput(playerPos, node, nodes, sameXZ, currentIndex) - } - } - - private fun buildFlyVerticalInput( - node: PathNode, - playerPos: BlockPos, - ): PlayerInput { - val input = PlayerInput() - - val diffY = node.block.y - playerPos.y - when { - diffY > 0 -> input.jump = true - diffY < 0 -> input.sneak = true - } - - return input - } - - private fun buildStandardInput( - playerPos: BlockPos, - node: PathNode, - nodes: List, - sameXZ: Boolean, - currentIndex: Int, - ): PlayerInput { - val input = PlayerInput() - - applyHorizontalInput(input, node, sameXZ) - applySprintIfNeeded(input) - applyJumpIfNeeded(input, playerPos, nodes, currentIndex, node) - - return input - } - - private fun applyHorizontalInput( - input: PlayerInput, - node: PathNode, - sameXZ: Boolean, - ) { - if (sameXZ) { - return - } - - val neededKeys = MovementInputMapper.getNeededKeys( - PlayerUtils.rotation.yaw, - RotationUtils.getRotation(node.centerVec).yaw - ) - - input.apply(neededKeys) - } - - private fun applySprintIfNeeded(input: PlayerInput) { - if (PathFindingConfig.shouldSprint) { - input.sprint = true - } - } - - private fun applyJumpIfNeeded( - input: PlayerInput, - playerPos: BlockPos, - nodes: List, - currentIndex: Int, - node: PathNode, - ) { - if (node.isFly) { - return - } - - if (jumpDecision.shouldJump(playerPos, nodes, currentIndex)) { - input.jump = true - } - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt index ea09ed9..8462c57 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt @@ -1,99 +1,5 @@ package org.cobalt.pathfinder.state.pathing -import net.minecraft.world.phys.AABB -import org.cobalt.module.impl.misc.Debug -import org.cobalt.module.impl.misc.Rotations -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.helper.PathRenderer -import org.cobalt.pathfinder.movement.PathLookTargetProjector import org.cobalt.pathfinder.state.ExecutorState -import org.cobalt.pathfinder.state.fly.StartFlyState -import org.cobalt.util.PlayerUtils -import org.cobalt.util.WorldRenderUtils -import org.cobalt.util.rotation.RotationTarget -import java.awt.Color -class PathingState : ExecutorState() { - private val path = PathFindingFacade.path - private val movements = PathFindingFacade.availableMovements - private val jumpDecision = JumpDecision() - private val inputBuilder = PathingInputBuilder(jumpDecision) - private val advancer = PathingAdvancer() - - private inline val pathIndex: Int - get() = PathFindingFacade.pathIndex - - override fun exit() { - input.stopMovement() - Rotations.stop() - } - - override fun onTick() { - if (path == null || movements == null) { - PathFindingFacade.stop() - return - } - - val player = PlayerUtils.player ?: return - val nodes = path.nodes - val node = nodes[pathIndex] - val playerPos = PlayerUtils.position - - if ( - advancer.advanceIfReached(playerPos, nodes) || - shouldStartFlying(node) - ) { - return - } - - val sameXZ = node.block.x == playerPos.x && node.block.z == playerPos.z - val movement = inputBuilder.build(playerPos, node, nodes, sameXZ, pathIndex) - val lookTarget = PathLookTargetProjector.getRotationTarget( - player.eyePosition, nodes, pathIndex - ) - - Rotations.track(RotationTarget(lookTarget)) - input.applyInput(movement) - } - - override fun onRender() { - if (path == null || movements == null) { - PathFindingFacade.stop() - return - } - - PathRenderer.render() - - if (!Debug.enabled) { - return - } - - val lookAhead = (PlayerUtils.velocity.length() * 6.0).coerceIn(5.0, 32.0) - val target = PathLookTargetProjector.getRotationTarget( - PlayerUtils.player!!.eyePosition, path.nodes, pathIndex, lookAhead - ) - - WorldRenderUtils.drawBox( - AABB( - target.x - 0.25, target.y - 0.25, target.z - 0.25, - target.x + 0.25, target.y + 0.25, target.z + 0.25 - ), Color.GREEN - ) - } - - private fun shouldStartFlying(node: PathNode): Boolean { - requireNotNull(movements) - - if ( - !node.isFly || - PlayerUtils.isFlying - ) { - return false - } - - PathFindingFacade.changeState(StartFlyState()) - return true - } - -} +class PathingState : ExecutorState() diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt b/src/main/kotlin/org/cobalt/util/BlockUtils.kt similarity index 52% rename from src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt rename to src/main/kotlin/org/cobalt/util/BlockUtils.kt index cb78970..b96769f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt +++ b/src/main/kotlin/org/cobalt/util/BlockUtils.kt @@ -1,14 +1,23 @@ -package org.cobalt.pathfinder.movement.rules +package org.cobalt.util import net.minecraft.core.BlockPos import net.minecraft.world.level.EmptyBlockGetter -import net.minecraft.world.level.block.* +import net.minecraft.world.level.block.AmethystClusterBlock +import net.minecraft.world.level.block.BambooStalkBlock +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.PointedDripstoneBlock +import net.minecraft.world.level.block.ScaffoldingBlock +import net.minecraft.world.level.block.ShulkerBoxBlock +import net.minecraft.world.level.block.SlabBlock import net.minecraft.world.level.block.piston.MovingPistonBlock import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.properties.SlabType +import net.minecraft.world.level.material.FlowingFluid import net.minecraft.world.level.material.Fluids +import org.cobalt.pathfinder.helper.BlockStateAccessor + +object BlockUtils { -object BlockSupportRules { @JvmStatic fun isWater(state: BlockState): Boolean { val fluid = state.fluidState.type @@ -53,4 +62,31 @@ object BlockSupportRules { false } } + + @JvmStatic + fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { + val fluidState = state.fluidState + + if (fluidState.type !is FlowingFluid) { + return false + } + + if (fluidState.type.getAmount(fluidState) != 8) { + return true + } + + return possiblyFlowing(bsa.get(x + 1, y, z)) + || possiblyFlowing(bsa.get(x - 1, y, z)) + || possiblyFlowing(bsa.get(x, y, z + 1)) + || possiblyFlowing(bsa.get(x, y, z - 1)) + } + + @JvmStatic + fun possiblyFlowing(state: BlockState): Boolean { + val fluidState = state.fluidState + + return fluidState.type is FlowingFluid && + fluidState.type.getAmount(fluidState) != 8 + } + } From dc163c2a7a9bc7f2d7e715be78b8326bbe5157f7 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:09:46 -0400 Subject: [PATCH 21/28] Revert "chore: cleanup attempt" This reverts commit 5cac3287ed532192f88b0de48c3ac2720c151856. --- .../org/cobalt/command/impl/MainCommand.kt | 14 +- .../org/cobalt/pathfinder/PathConfig.kt | 22 -- .../cobalt/pathfinder/PathFindingConfig.kt | 8 + .../{PathExecutor.kt => PathFindingFacade.kt} | 45 +-- .../pathfinder/{helper => }/PathInput.kt | 17 +- .../cobalt/pathfinder/calculate/PathNode.kt | 3 +- .../calculate/path/AStarPathfinder.kt | 86 +++--- .../pathfinder/{ => helper}/PathRenderer.kt | 28 +- .../pathfinder/movement/CalculationContext.kt | 1 - .../pathfinder/movement/FlowingCheck.kt | 32 +++ .../movement/MovementInputMapper.kt | 83 ++++++ .../movement/PathLookTargetProjector.kt | 271 ++++++++++++++++++ .../movement/fly/FlyAscendMovement.kt | 9 +- .../movement/fly/FlyDescendMovement.kt | 9 +- .../movement/fly/FlyDiagonalMovement.kt | 13 +- .../movement/fly/FlyTraverseMovement.kt | 9 +- .../movement/rules/BlockSupportRules.kt} | 42 +-- .../BlockTraversalRules.kt} | 13 +- .../movement/rules/PassthroughRules.kt | 6 +- .../{StandingRules.kt => StandOnRules.kt} | 23 +- .../movement/rules/data/BlockRule.kt | 2 - .../movement/rules/data/BlockSetRule.kt | 2 - .../movement/rules/data/BlockTypeRule.kt | 6 +- .../movement/walk/AscendMovement.kt | 15 +- .../movement/walk/DescendMovement.kt | 11 +- .../movement/walk/DiagonalMovement.kt | 13 +- .../movement/walk/TraverseMovement.kt | 9 +- .../cobalt/pathfinder/state/ExecutorState.kt | 6 +- .../state/calculation/CalculatingState.kt | 45 +-- .../pathfinder/state/fly/StartFlyState.kt | 10 +- .../pathfinder/state/pathing/JumpDecision.kt | 47 +++ .../state/pathing/PathingAdvancer.kt | 83 ++++++ .../state/pathing/PathingInputBuilder.kt | 96 +++++++ .../pathfinder/state/pathing/PathingState.kt | 96 ++++++- 34 files changed, 889 insertions(+), 286 deletions(-) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt rename src/main/kotlin/org/cobalt/pathfinder/{PathExecutor.kt => PathFindingFacade.kt} (76%) rename src/main/kotlin/org/cobalt/pathfinder/{helper => }/PathInput.kt (85%) rename src/main/kotlin/org/cobalt/pathfinder/{ => helper}/PathRenderer.kt (58%) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt rename src/main/kotlin/org/cobalt/{util/BlockUtils.kt => pathfinder/movement/rules/BlockSupportRules.kt} (52%) rename src/main/kotlin/org/cobalt/pathfinder/movement/{MovementValidator.kt => rules/BlockTraversalRules.kt} (66%) rename src/main/kotlin/org/cobalt/pathfinder/movement/rules/{StandingRules.kt => StandOnRules.kt} (80%) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index a7ceb42..94593f3 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -4,9 +4,7 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.command.Command import org.cobalt.command.annotation.DefaultHandler import org.cobalt.command.annotation.SubCommand -import org.cobalt.pathfinder.PathConfig -import org.cobalt.pathfinder.PathExecutor -import org.cobalt.pathfinder.calculate.PathMode +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen @@ -32,18 +30,12 @@ object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { @SubCommand fun goTo(x: Int, y: Int, z: Int, fly: Boolean) { - val pathMode = if (fly) PathMode.FLY else PathMode.WALK - val config = PathConfig( - goal = GoalBlock(x, y, z), - mode = pathMode - ) - - PathExecutor.goTo(config) + PathFindingFacade.goTo(GoalBlock(x, y, z), fly) } @SubCommand fun stopPathfinder() { - PathExecutor.stop() + PathFindingFacade.stop() } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt deleted file mode 100644 index c99007f..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.cobalt.pathfinder - -import org.cobalt.pathfinder.calculate.PathMode -import org.cobalt.pathfinder.goal.Goal - -class PathConfig( - val goal: Goal, - val mode: PathMode = PathMode.WALK, - val shouldSprint: Boolean = true, - val returnBestNode: Boolean = false, - val maxCalculationTime: Long = MAX_CALCULATION_TIME, - val hasReachedThreshold: Double = 0.09, -) { - - val allowFly: Boolean = - mode == PathMode.FLY - - companion object { - const val MAX_CALCULATION_TIME = 10_000_000_000L - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt new file mode 100644 index 0000000..02d351f --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt @@ -0,0 +1,8 @@ +package org.cobalt.pathfinder + +object PathFindingConfig { + var shouldSprint: Boolean = true + var returnBestNode: Boolean = false + var maxCalculationTime: Long = 10_000_000_000L + var hasReachedThreshold: Double = 0.09 // // = sqrt(0.3), so 0.3 blocks of distance is the threshold +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt similarity index 76% rename from src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt rename to src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt index 1656a99..4e94eb7 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt @@ -7,17 +7,20 @@ import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.TickEvent import org.cobalt.event.impl.WorldEvent import org.cobalt.pathfinder.calculate.Path -import org.cobalt.pathfinder.helper.PathInput +import org.cobalt.pathfinder.calculate.PathMode +import org.cobalt.pathfinder.goal.Goal +import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.calculation.CalculatingState import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType import org.cobalt.util.PlayerUtils -object PathExecutor { - +object PathFindingFacade { var state: ExecutorState? = null - var config: PathConfig? = null + + var currentGoal: Goal? = null + var availableMovements: Array? = null var path: Path? = null var pathIndex: Int = 0 @@ -29,33 +32,37 @@ object PathExecutor { EventBus.register(this) } - fun goTo(config: PathConfig) { - val player = minecraft.player ?: run { + fun goTo(goal: Goal, fly: Boolean = false) { + // Are we sure we want to do this? There's no warning or anything. + // I've kept it but with a debug message + if (running) { ChatUtils.sendSystemMessage( - "Tried running pathfinder, but minecraft.player is null!", + "Stopping current pathfinder because a new one started.", MessageType.DEBUG ) + stop() + } + if (fly && !PlayerUtils.canFly) { + ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") return } - if (running) { + val player = minecraft.player + + if (player == null) { ChatUtils.sendSystemMessage( - "Stopping current pathfinder because a new one started.", + "Tried running pathfinder, but minecraft.player is null!", MessageType.DEBUG ) - - stop() - } - - if (config.allowFly && !PlayerUtils.canFly) { - ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") return + } else { + player.input = pathInput } - player.input = pathInput + availableMovements = if (fly) PathMode.FLY.movements else PathMode.WALK.movements + currentGoal = goal - this.config = config this.running = true changeState(CalculatingState()) @@ -113,10 +120,6 @@ object PathExecutor { return } - if (state !is CalculatingState) { - PathRenderer.render() - } - state?.onRender() } diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt similarity index 85% rename from src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt rename to src/main/kotlin/org/cobalt/pathfinder/PathInput.kt index 70d7886..96152ec 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt @@ -1,8 +1,9 @@ -package org.cobalt.pathfinder.helper +package org.cobalt.pathfinder import net.minecraft.client.player.ClientInput import net.minecraft.world.entity.player.Input import net.minecraft.world.phys.Vec2 +import org.cobalt.pathfinder.helper.PlayerInput class PathInput : ClientInput() { @@ -30,13 +31,13 @@ class PathInput : ClientInput() { override fun tick() { this.keyPresses = Input( - forward, - backward, - left, - right, - jump, - shift, - sprint + forward, + backward, + left, + right, + jump, + shift, + sprint ) val forwardImpulse = calculateImpulse(forward, backward) diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt index 901013a..dfac851 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt @@ -16,7 +16,6 @@ data class PathNode( val costToEnd = goal.heuristic(x, y, z) var totalCost = 1.0 var heapPosition = -1 - var movementType = MovementType.WALK var parent: PathNode? = null @@ -25,7 +24,7 @@ data class PathNode( val centerVec: Vec3 = Vec3(x + 0.5, y + 0.5, z + 0.5) val topCenterVec: Vec3 = Vec3(x + 0.5, y.toDouble(), z + 0.5) - val useMovementFly: Boolean + val isFly: Boolean get() = movementType == MovementType.FLY override fun compareTo(other: PathNode): Int { diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index daaf817..608aeb9 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -1,10 +1,9 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap -import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.nanoseconds +import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.Path -import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.calculate.openset.BinaryHeapOpenSet import org.cobalt.pathfinder.goal.Goal @@ -13,77 +12,68 @@ import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType +import org.cobalt.util.PlayerUtils -class AStarPathfinder( - val startX: Int, - val startY: Int, - val startZ: Int, - val goal: Goal, - val mode: PathMode, - val returnBestNode: Boolean = false, - val maxCalculationTime: Long = 10_000_000L -) { - - private val closedSet = Long2ObjectOpenHashMap() - private val movements = mode.movements - private var startTime = 0L - - fun findPath(): Path? { +object AStarPathfinder { + private val nodeCache = Long2ObjectOpenHashMap() + private var goal: Goal? = null // This can't be null after findPath is called. + private var availableMovements: Array? = null // This can't be null after findPath is called. + + fun findPath(goal: Goal, movements: Array, returnBestNode: Boolean): Path? { + val startPos = PlayerUtils.position val calculationContext = CalculationContext() val openSet = BinaryHeapOpenSet() val movementResult = MovementResult() - val startNode = PathNode( - startX, startY, startZ, goal - ).also { - it.costSoFar = 0.0 - it.totalCost = it.costToEnd - } + // After this, goal and movements should never be null again, so we can do !! in private functions safely. + // If they are null, someone nuked the code, and we should get angry at them. + AStarPathfinder.goal = goal + availableMovements = movements + + val startNode = PathNode(startPos.x, startPos.y, startPos.z, goal) + + startNode.costSoFar = 0.0 + startNode.totalCost = startNode.costToEnd openSet.add(startNode) - startTime = System.nanoTime() + val startTime = System.nanoTime() var bestNode = startNode - val deadline = startTime + maxCalculationTime + val deadline = startTime + PathFindingConfig.maxCalculationTime ChatUtils.sendSystemMessage( "Starting pathfinding from ${startNode.block}", MessageType.DEBUG ) - while (!openSet.isEmpty() && System.nanoTime() < deadline) { + while (!openSet.isEmpty() + && System.nanoTime() < deadline + ) { val currentNode = openSet.poll() - if (currentNode < bestNode) { - bestNode = currentNode - } + if (currentNode < bestNode) bestNode = currentNode if (goal.isAtGoal(currentNode.x, currentNode.y, currentNode.z)) { - return reconstruct(currentNode) + return reconstruct(currentNode, startTime) } - evaluateMovements( - calculationContext, - currentNode, - movementResult, - openSet - ) + evaluateMovements(currentNode, calculationContext, movementResult, openSet) } return if (returnBestNode) { - reconstruct(bestNode) + reconstruct(bestNode, startTime) } else { null } } private fun evaluateMovements( - calculationContext: CalculationContext, currentNode: PathNode, + calculationContext: CalculationContext, movementResult: MovementResult, openSet: BinaryHeapOpenSet, ) { - for (move in movements) { + for (move in availableMovements!!) { movementResult.reset() move.calculateCost(calculationContext, currentNode, movementResult) @@ -100,14 +90,12 @@ class AStarPathfinder( openSet: BinaryHeapOpenSet, ) { val neighborCostSoFar = currentNode.costSoFar + movementResult.cost - val neighborNode = getNode( + val neighborNode = getOrCacheNode( movementResult.x, movementResult.y, movementResult.z, PathNode.longHash(movementResult.x, movementResult.y, movementResult.z) ) - if (neighborCostSoFar >= neighborNode.costSoFar) { - return - } + if (neighborCostSoFar >= neighborNode.costSoFar) return neighborNode.parent = currentNode neighborNode.costSoFar = neighborCostSoFar @@ -121,18 +109,18 @@ class AStarPathfinder( } } - fun getNode(x: Int, y: Int, z: Int, hash: Long): PathNode { - var node: PathNode? = closedSet.get(hash) + fun getOrCacheNode(x: Int, y: Int, z: Int, hash: Long): PathNode { + var node: PathNode? = nodeCache.get(hash) if (node == null) { - node = PathNode(x, y, z, goal) - closedSet.put(hash, node) + node = PathNode(x, y, z, goal!!) + nodeCache.put(hash, node) } return node } - private fun reconstruct(endNode: PathNode): Path { + private fun reconstruct(endNode: PathNode, startTime: Long): Path { val path = mutableListOf() var node: PathNode? = endNode @@ -144,7 +132,7 @@ class AStarPathfinder( return Path( nodes = path, timeElapsed = (System.nanoTime() - startTime).nanoseconds, - goal = goal + goal = goal!! ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt similarity index 58% rename from src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt rename to src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt index 82d5005..d503cd3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt @@ -1,27 +1,22 @@ -package org.cobalt.pathfinder +package org.cobalt.pathfinder.helper +import java.awt.Color import org.cobalt.dsl.centerVec import org.cobalt.dsl.smallBox import org.cobalt.module.impl.misc.Debug +import org.cobalt.pathfinder.PathFindingFacade.path +import org.cobalt.pathfinder.PathFindingFacade.pathIndex import org.cobalt.ui.theme.ThemeManager import org.cobalt.util.PlayerUtils import org.cobalt.util.WorldRenderUtils -import java.awt.Color -import org.cobalt.pathfinder.calculate.Path -import org.cobalt.ui.theme.Theme object PathRenderer { - - private inline val theme: Theme - get() = ThemeManager.activeTheme - fun render() { - val path: Path = PathExecutor.path ?: return - - val nodes = path.nodes - val keyNodes = path.keyNodes + val theme = ThemeManager.activeTheme + val nodes = path?.nodes ?: return + val keyNodes = path?.keyNodes ?: return - val targetNode = nodes[PathExecutor.pathIndex].centerVec + val targetNode = nodes[pathIndex].centerVec val playerPos = PlayerUtils.position.centerVec() if (Debug.enabled) { @@ -33,7 +28,7 @@ object PathRenderer { val node = keyNodes[index] WorldRenderUtils.drawBlockPos( - if (node.useMovementFly) node.block else node.blockStandingOn, + if (node.isFly) node.block else node.blockStandingOn, color = theme.accentPrimary ) @@ -42,11 +37,10 @@ object PathRenderer { val prev = keyNodes[index - 1] WorldRenderUtils.drawLine( - if (prev.useMovementFly) prev.centerVec else prev.topCenterVec, - if (node.useMovementFly) node.centerVec else node.topCenterVec, + if (prev.isFly) prev.centerVec else prev.topCenterVec, + if (node.isFly) node.centerVec else node.topCenterVec, theme.accentSecondary ) } } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt index 5733196..3dfefae 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt @@ -15,5 +15,4 @@ class CalculationContext { val jumpAmplifier = player.getEffect(MobEffects.JUMP_BOOST)?.amplifier ?: -1 val blockStateAccessor = BlockStateAccessor(level) - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt new file mode 100644 index 0000000..d30886f --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt @@ -0,0 +1,32 @@ +package org.cobalt.pathfinder.movement + +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.FlowingFluid +import org.cobalt.pathfinder.helper.BlockStateAccessor + +object FlowingCheck { + private fun possiblyFlowing(state: BlockState): Boolean { + val fluidState = state.fluidState + + return fluidState.type is FlowingFluid && + fluidState.type.getAmount(fluidState) != 8 + } + + fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { + val fluidState = state.fluidState + + if (fluidState.type !is FlowingFluid) { + return false + } + + if (fluidState.type.getAmount(fluidState) != 8) { + return true + } + + return possiblyFlowing(bsa.get(x + 1, y, z)) + || possiblyFlowing(bsa.get(x - 1, y, z)) + || possiblyFlowing(bsa.get(x, y, z + 1)) + || possiblyFlowing(bsa.get(x, y, z - 1)) + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt new file mode 100644 index 0000000..bf406c0 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt @@ -0,0 +1,83 @@ +package org.cobalt.pathfinder.movement + +import net.minecraft.util.Mth +import org.cobalt.pathfinder.helper.PlayerInput + +object MovementInputMapper { + private const val DIRECTIONS_COUNT = 8 + private const val FULL_CIRCLE_DEGREES = 360f + private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT + private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f + + private const val DEAD_BAND_DEGREES = 0.05f + + private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES + private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES + + private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES + private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES + private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES + private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES + + private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val BACK_RIGHT_HOLD_DEGREES = BACK_RIGHT_START_DEGREES + DEAD_BAND_DEGREES + private const val BACKWARD_HOLD_DEGREES = BACKWARD_START_DEGREES + DEAD_BAND_DEGREES + + private const val FORWARD_LEFT_HOLD_DEGREES = FORWARD_LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val LEFT_HOLD_DEGREES = LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES + private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES + + private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } + + private var lastSector: Sector = Sector.FORWARD + + @JvmStatic + fun getNeededKeys(playerYaw: Float, idealYaw: Float): PlayerInput { + val diff = Mth.wrapDegrees(idealYaw - playerYaw) + + if (staysInSector(lastSector, diff)) { + return sectorToInput(lastSector) + } + + val newSector = classify(diff) + lastSector = newSector + return sectorToInput(newSector) + } + + private fun staysInSector(sector: Sector, diff: Float): Boolean = when (sector) { + Sector.FORWARD -> diff in FORWARD_LEFT_HOLD_DEGREES.. diff >= FORWARD_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < RIGHT_HOLD_DEGREES + Sector.RIGHT -> diff >= RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_RIGHT_HOLD_DEGREES + Sector.BACK_RIGHT -> diff >= BACK_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACKWARD_HOLD_DEGREES + Sector.BACKWARD -> diff >= BACKWARD_START_DEGREES - DEAD_BAND_DEGREES || diff < BACKWARD_NEG_HOLD_DEGREES + Sector.BACK_LEFT -> diff >= BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_LEFT_HOLD_DEGREES + Sector.LEFT -> diff >= BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < LEFT_HOLD_DEGREES + Sector.FORWARD_LEFT -> diff >= LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < FORWARD_LEFT_HOLD_DEGREES + } + + private fun classify(diff: Float): Sector = when (diff) { + in FORWARD_LEFT_START_DEGREES.. Sector.FORWARD + in FORWARD_RIGHT_START_DEGREES.. Sector.FORWARD_RIGHT + in RIGHT_START_DEGREES.. Sector.RIGHT + in BACK_RIGHT_START_DEGREES.. Sector.BACK_RIGHT + !in BACKWARD_NEG_START_DEGREES.. Sector.BACKWARD + in BACKWARD_NEG_START_DEGREES.. Sector.BACK_LEFT + in BACK_LEFT_START_DEGREES.. Sector.LEFT + else -> Sector.FORWARD_LEFT + } + + private fun sectorToInput(sector: Sector): PlayerInput = when (sector) { + Sector.FORWARD -> PlayerInput(forward = true) + Sector.FORWARD_RIGHT -> PlayerInput(forward = true, right = true) + Sector.RIGHT -> PlayerInput(right = true) + Sector.BACK_RIGHT -> PlayerInput(backward = true, right = true) + Sector.BACKWARD -> PlayerInput(backward = true) + Sector.BACK_LEFT -> PlayerInput(backward = true, left = true) + Sector.LEFT -> PlayerInput(left = true) + Sector.FORWARD_LEFT -> PlayerInput(forward = true, left = true) + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt new file mode 100644 index 0000000..887bcfc --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt @@ -0,0 +1,271 @@ +package org.cobalt.pathfinder.movement + +import net.minecraft.world.phys.Vec3 +import org.cobalt.pathfinder.calculate.PathNode + +object PathLookTargetProjector { + private const val SEARCH_BACKWARD_SEGMENTS = 1 + private const val SEARCH_FORWARD_SEGMENTS = 3 + private const val ZERO = 0.0 + private const val FULL_SEGMENT = 1.0 + + private data class ClosestSegment( + val index: Int, + val progress: Double, + ) + + private data class SegmentProjection( + val progress: Double, + val distanceSquared: Double, + ) + + @JvmStatic + fun getRotationTarget( + playerEyePosition: Vec3, + pathNodes: List, + currentPathIndex: Int, + lookAheadDistance: Double = 3.0, + ): Vec3 { + val closestSegment = findClosestSegment( + playerEyePosition, + pathNodes, + currentPathIndex + ) + + val horizontalTarget = moveAlongPath( + pathNodes, + closestSegment, + lookAheadDistance + ) + + return Vec3( + horizontalTarget.x, + playerEyePosition.y, + horizontalTarget.z + ) + } + + private fun findClosestSegment( + playerEyePosition: Vec3, + pathNodes: List, + currentPathIndex: Int, + ): ClosestSegment { + val playerPosition = Vec3( + playerEyePosition.x, + ZERO, + playerEyePosition.z + ) + + var closestSegment = ClosestSegment( + currentPathIndex, + ZERO + ) + + var closestDistanceSquared = Double.MAX_VALUE + + val startIndex = maxOf( + ZERO.toInt(), + currentPathIndex - SEARCH_BACKWARD_SEGMENTS + ) + + val endIndex = minOf( + pathNodes.lastIndex, + currentPathIndex + SEARCH_FORWARD_SEGMENTS + ) + + for (segmentIndex in startIndex until endIndex) { + val projection = findClosestPointOnSegment( + playerPosition, + pathNodes[segmentIndex].centerVec, + pathNodes[segmentIndex + 1].centerVec + ) + + if (projection == null) { + continue + } + + if (projection.distanceSquared < closestDistanceSquared) { + closestDistanceSquared = projection.distanceSquared + closestSegment = ClosestSegment( + segmentIndex, + projection.progress + ) + } + } + + return closestSegment + } + + private fun findClosestPointOnSegment( + point: Vec3, + start: Vec3, + end: Vec3, + ): SegmentProjection? { + val direction = Vec3( + end.x - start.x, + ZERO, + end.z - start.z + ) + + val lengthSquared = + direction.x * direction.x + + direction.z * direction.z + + if (lengthSquared == ZERO) { + return null + } + + val progress = + ( + (point.x - start.x) * direction.x + + (point.z - start.z) * direction.z + ) + .coerceIn(ZERO, lengthSquared) / lengthSquared + + val closestX = start.x + direction.x * progress + val closestZ = start.z + direction.z * progress + + val distanceX = point.x - closestX + val distanceZ = point.z - closestZ + + return SegmentProjection( + progress, + distanceX * distanceX + distanceZ * distanceZ + ) + } + + private fun moveAlongPath( + pathNodes: List, + startingSegment: ClosestSegment, + distance: Double, + ): Vec3 { + var remainingDistance = distance + + val currentSegmentResult = moveOnSegment( + pathNodes, + startingSegment.index, + startingSegment.progress, + remainingDistance + ) + + if (currentSegmentResult != null) { + return currentSegmentResult + } + + remainingDistance -= distanceRemainingOnSegment( + pathNodes, + startingSegment + ) + + for (segmentIndex in startingSegment.index + 1 until pathNodes.lastIndex) { + val result = moveOnSegment( + pathNodes, + segmentIndex, + ZERO, + remainingDistance + ) + + if (result != null) { + return result + } + + remainingDistance -= segmentLength( + pathNodes, + segmentIndex + ) + } + + return extrapolatePastEnd( + pathNodes, + remainingDistance + ) + } + + private fun moveOnSegment( + pathNodes: List, + segmentIndex: Int, + startProgress: Double, + distance: Double, + ): Vec3? { + val start = pathNodes[segmentIndex].centerVec + val end = pathNodes[segmentIndex + 1].centerVec + + val direction = Vec3( + end.x - start.x, + ZERO, + end.z - start.z + ) + + val length = direction.length() + + if (length == ZERO) { + return null + } + + val availableDistance = + (FULL_SEGMENT - startProgress) * length + + if (distance > availableDistance) { + return null + } + + val progress = + startProgress + distance / length + + return start.add( + direction.scale(progress) + ) + } + + private fun distanceRemainingOnSegment( + pathNodes: List, + segment: ClosestSegment, + ): Double { + return (FULL_SEGMENT - segment.progress) * + segmentLength(pathNodes, segment.index) + } + + private fun segmentLength( + pathNodes: List, + segmentIndex: Int, + ): Double { + val start = pathNodes[segmentIndex].centerVec + val end = pathNodes[segmentIndex + 1].centerVec + + return Vec3( + end.x - start.x, + ZERO, + end.z - start.z + ).length() + } + + private fun extrapolatePastEnd( + pathNodes: List, + distance: Double, + ): Vec3 { + val last = pathNodes.last() + + val previous = + if (pathNodes.size > 1) { + pathNodes[pathNodes.lastIndex - 1] + } else { + last + } + + val direction = Vec3( + (last.x - previous.x).toDouble(), + ZERO, + (last.z - previous.z).toDouble() + ) + + val length = direction.length() + + if (length == ZERO) { + return last.centerVec + } + + return last.centerVec.add( + direction.scale(distance / length) + ) + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt index 5514c36..9b33671 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyAscendMovement : Movement(MovementType.FLY) { @@ -18,7 +15,7 @@ class FlyAscendMovement : Movement(MovementType.FLY) { val y = currNode.y + 1 val z = currNode.z - if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt index 46a94b0..92f136a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyDescendMovement : Movement(MovementType.FLY) { @@ -18,7 +15,7 @@ class FlyDescendMovement : Movement(MovementType.FLY) { val y = currNode.y - 1 val z = currNode.z - if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt index 75d35fd..5078010 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyDiagonalMovement( val dx: Int, @@ -21,15 +18,15 @@ class FlyDiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } - if (!MovementValidator.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!MovementValidator.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt index 86d319f..89bf73a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class FlyTraverseMovement( val dx: Int, @@ -21,7 +18,7 @@ class FlyTraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/util/BlockUtils.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt similarity index 52% rename from src/main/kotlin/org/cobalt/util/BlockUtils.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt index b96769f..cb78970 100644 --- a/src/main/kotlin/org/cobalt/util/BlockUtils.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt @@ -1,23 +1,14 @@ -package org.cobalt.util +package org.cobalt.pathfinder.movement.rules import net.minecraft.core.BlockPos import net.minecraft.world.level.EmptyBlockGetter -import net.minecraft.world.level.block.AmethystClusterBlock -import net.minecraft.world.level.block.BambooStalkBlock -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.PointedDripstoneBlock -import net.minecraft.world.level.block.ScaffoldingBlock -import net.minecraft.world.level.block.ShulkerBoxBlock -import net.minecraft.world.level.block.SlabBlock +import net.minecraft.world.level.block.* import net.minecraft.world.level.block.piston.MovingPistonBlock import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.properties.SlabType -import net.minecraft.world.level.material.FlowingFluid import net.minecraft.world.level.material.Fluids -import org.cobalt.pathfinder.helper.BlockStateAccessor - -object BlockUtils { +object BlockSupportRules { @JvmStatic fun isWater(state: BlockState): Boolean { val fluid = state.fluidState.type @@ -62,31 +53,4 @@ object BlockUtils { false } } - - @JvmStatic - fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { - val fluidState = state.fluidState - - if (fluidState.type !is FlowingFluid) { - return false - } - - if (fluidState.type.getAmount(fluidState) != 8) { - return true - } - - return possiblyFlowing(bsa.get(x + 1, y, z)) - || possiblyFlowing(bsa.get(x - 1, y, z)) - || possiblyFlowing(bsa.get(x, y, z + 1)) - || possiblyFlowing(bsa.get(x, y, z - 1)) - } - - @JvmStatic - fun possiblyFlowing(state: BlockState): Boolean { - val fluidState = state.fluidState - - return fluidState.type is FlowingFluid && - fluidState.type.getAmount(fluidState) != 8 - } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt similarity index 66% rename from src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt index 5bc1f2d..75bbe2e 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt @@ -1,13 +1,15 @@ -package org.cobalt.pathfinder.movement +package org.cobalt.pathfinder.movement.rules +import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.rules.PassthroughRules.canPassThrough -import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn - -object MovementValidator { +import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn +object BlockTraversalRules { @JvmStatic fun canWalkOn(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { - if (!canWalkThrough(calculationContext, x, y + 1, z)) { + if ( + !canWalkThrough(calculationContext, x, y + 1, z) + ) { return false } @@ -19,5 +21,4 @@ object MovementValidator { return canPassThrough(calculationContext.blockStateAccessor, x, y, z) && canPassThrough(calculationContext.blockStateAccessor, x, y + 1, z) } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt index 654b82f..a14c950 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt @@ -21,11 +21,11 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties import net.minecraft.world.level.material.WaterFluid import net.minecraft.world.level.pathfinder.PathComputationType import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn +import org.cobalt.pathfinder.movement.FlowingCheck +import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary -import org.cobalt.util.BlockUtils object PassthroughRules { @@ -152,7 +152,7 @@ object PassthroughRules { !state.fluidState.isEmpty -> { val fluidState = state.fluidState - if (BlockUtils.isFlowing(x, y, z, state, bsa)) { + if (FlowingCheck.isFlowing(x, y, z, state, bsa)) { false } else { val upState = bsa.get(x, y + 1, z) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt similarity index 80% rename from src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt rename to src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt index a7d71e5..07a24a4 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt @@ -10,14 +10,14 @@ import net.minecraft.world.level.block.StairBlock import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.material.Fluids import org.cobalt.pathfinder.helper.BlockStateAccessor +import org.cobalt.pathfinder.movement.FlowingCheck import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary -import org.cobalt.util.BlockUtils -object StandingRules { +object StandOnRules { - val STANDING_RULES = listOf( + val STAND_ON_RULES = listOf( BlockSetRule( setOf( Blocks.LADDER, @@ -57,19 +57,19 @@ object StandingRules { private fun compute(state: BlockState): Ternary { val block = state.block - if (BlockUtils.isBlockNormalCube(state) && + if (BlockSupportRules.isBlockNormalCube(state) && block != Blocks.BUBBLE_COLUMN && block != Blocks.HONEY_BLOCK ) { return Ternary.YES } - STANDING_RULES.firstOrNull { it.matches(state) } + STAND_ON_RULES.firstOrNull { it.matches(state) } ?.let { return it.result } return when { - BlockUtils.isWater(state) -> Ternary.MAYBE - BlockUtils.isLava(state) -> Ternary.MAYBE + BlockSupportRules.isWater(state) -> Ternary.MAYBE + BlockSupportRules.isLava(state) -> Ternary.MAYBE else -> Ternary.NO } } @@ -90,7 +90,7 @@ object StandingRules { } private fun resolveMaybe(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { - if (BlockUtils.isWater(state)) { + if (BlockSupportRules.isWater(state)) { val upState = bsa.get(x, y + 1, z) val upBlock = upState.block @@ -98,14 +98,13 @@ object StandingRules { return true } - if (BlockUtils.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { - return BlockUtils.isWater(upState) + if (FlowingCheck.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { + return BlockSupportRules.isWater(upState) } - return BlockUtils.isWater(upState) xor false + return BlockSupportRules.isWater(upState) xor false } return false } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt index 09fe992..a7c22da 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt @@ -3,9 +3,7 @@ package org.cobalt.pathfinder.movement.rules.data import net.minecraft.world.level.block.state.BlockState interface BlockRule { - val result: Ternary fun matches(state: BlockState): Boolean - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt index 1a949f4..0f8f0ca 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt @@ -7,8 +7,6 @@ class BlockSetRule( private val blocks: Set, override val result: Ternary, ) : BlockRule { - override fun matches(state: BlockState): Boolean = state.block in blocks - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt index 3d837a6..c720ee0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt @@ -4,11 +4,9 @@ import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState class BlockTypeRule( - private val types: Set>, - override val result: Ternary, + private val types: Set>, + override val result: Ternary, ) : BlockRule { - override fun matches(state: BlockState): Boolean = types.any { it.isInstance(state.block) } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt index 0a4ecb0..9954540 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( @@ -22,15 +19,11 @@ class AscendMovement( val y = currNode.y + 1 val z = currNode.z + dz - if (!MovementValidator.canWalkOn(ctx, currNode.x, currNode.y, currNode.z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } - if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { - return - } - - if (!MovementValidator.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt index 2466173..39a26dc 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( @@ -22,11 +19,11 @@ class DescendMovement( val y = currNode.y - 1 val z = currNode.z + dz - if (!MovementValidator.canWalkThrough(ctx, x, currNode.y, z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, x, currNode.y, z)) { return } - if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt index 6e7b5a3..8ea0bef 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class DiagonalMovement( val dx: Int, @@ -21,15 +18,15 @@ class DiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } - if (!MovementValidator.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!MovementValidator.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt index e824ee6..57b8585 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt @@ -1,11 +1,8 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator -import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.* +import org.cobalt.pathfinder.movement.rules.BlockTraversalRules class TraverseMovement( val dx: Int, @@ -21,7 +18,7 @@ class TraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { + if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt index 58a793f..fd3c7ff 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt @@ -1,12 +1,12 @@ package org.cobalt.pathfinder.state -import org.cobalt.pathfinder.PathExecutor -import org.cobalt.pathfinder.helper.PathInput +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.PathInput abstract class ExecutorState { protected val input: PathInput = - PathExecutor.pathInput + PathFindingFacade.pathInput open fun enter() {} open fun onTick() {} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt index 948d186..3e1b16d 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt @@ -5,47 +5,49 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.PathFindingFacade import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.ChatUtils -import org.cobalt.util.PlayerUtils class CalculatingState : ExecutorState() { - private var calculationJob: Job? = null override fun enter() { - val config = PathExecutor.config ?: run { - ChatUtils.sendSystemMessage("Cannot calculate path, no path config set!") - PathExecutor.stop() + val goal = PathFindingFacade.currentGoal + val availableMovements = PathFindingFacade.availableMovements + + if (goal == null) { + ChatUtils.sendSystemMessage("Cannot calculate path, no goal set!") + PathFindingFacade.stop() return } - val startPos = PlayerUtils.position - val goal = config.goal + if (availableMovements == null) { + ChatUtils.sendSystemMessage("Cannot calculate path, no movements set!") + PathFindingFacade.stop() + return + } calculationJob = CoroutineScope(Dispatchers.Default).launch { - val path = AStarPathfinder( - startPos.x, startPos.y, startPos.z, - goal, config.mode, - config.returnBestNode, - config.maxCalculationTime - ).findPath() - - if (!isActive) { - return@launch - } + val path = AStarPathfinder.findPath( + goal, + availableMovements, + PathFindingConfig.returnBestNode + ) + + if (!isActive) return@launch if (path == null) { ChatUtils.sendSystemMessage("Unable to find a path") - PathExecutor.stop() + PathFindingFacade.stop() return@launch } - PathExecutor.path = path - PathExecutor.changeState(PathingState()) + PathFindingFacade.path = path + PathFindingFacade.changeState(PathingState()) } } @@ -53,5 +55,4 @@ class CalculatingState : ExecutorState() { calculationJob?.cancel() calculationJob = null } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt index 354a689..ac27ae3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt @@ -1,6 +1,7 @@ package org.cobalt.pathfinder.state.fly -import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.helper.PathRenderer import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.PlayerUtils @@ -18,7 +19,11 @@ class StartFlyState : ExecutorState() { return } - PathExecutor.changeState(PathingState()) + PathFindingFacade.changeState(PathingState()) + } + + override fun onRender() { + PathRenderer.render() } private fun handleFlyStart(): Boolean { @@ -63,5 +68,4 @@ class StartFlyState : ExecutorState() { SECOND_JUMP, RELEASE_SECOND_JUMP, } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt new file mode 100644 index 0000000..04d4cd7 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt @@ -0,0 +1,47 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.core.BlockPos +import org.cobalt.Cobalt +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.movement.rules.BlockSupportRules +import org.cobalt.util.PlayerUtils +import org.cobalt.dsl.centerVec +import org.cobalt.util.helper.Clock +import kotlin.math.abs +import kotlin.random.Random + +class JumpDecision { + private val jumpDelay = Clock() + + fun shouldJump(playerPos: BlockPos, nodes: List, currentIndex: Int): Boolean { + val level = Cobalt.minecraft.level + requireNotNull(level) + + if (!PlayerUtils.onGround || !jumpDelay.passed()) { + return false + } + + val node = nodes[currentIndex] + + if (node.block.y - playerPos.y < 1 || + BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) + ) { + return false + } + + val nodeCenter = node.centerVec + val playerVec = playerPos.centerVec() + val dx = abs(nodeCenter.x - playerVec.x) + val dz = abs(nodeCenter.z - playerVec.z) + + if (dx + dz > 1.2 || minOf(dx, dz) > 0.2) { + return false + } + + if (PlayerUtils.canFly) { + jumpDelay.schedule(Random.nextLong(350, 450)) + } + + return true + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt new file mode 100644 index 0000000..d2304a2 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt @@ -0,0 +1,83 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.core.BlockPos +import net.minecraft.world.phys.Vec3 +import org.cobalt.dsl.centerVec +import org.cobalt.Cobalt +import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.movement.rules.BlockSupportRules +import org.cobalt.util.PlayerUtils + +class PathingAdvancer { + fun advanceIfReached(playerPos: BlockPos, nodes: List): Boolean { + val currentIndex = PathFindingFacade.pathIndex + + if (!hasReached(playerPos, nodes, currentIndex)) { + return false + } + + while ( + PathFindingFacade.pathIndex + 1 < nodes.size && + hasReached(playerPos, nodes, PathFindingFacade.pathIndex) + ) { + PathFindingFacade.pathIndex++ + } + + if (PathFindingFacade.pathIndex + 1 >= nodes.size) { + PathFindingFacade.stop() + } + + return true + } + + private fun hasReached( + playerPos: BlockPos, + nodes: List, + currentIndex: Int, + ): Boolean { + val node = nodes[currentIndex] + + val nodeCenter = node.centerVec + val playerVec = playerPos.centerVec() + + if (playerVec.distanceToSqr(nodeCenter) < PathFindingConfig.hasReachedThreshold) return true + + if (currentIndex + 1 >= nodes.size || !canAdvancePastNode(playerPos, node)) { + return false + } + + if (!isCloseEnoughToSegment(nodeCenter, playerVec, nodes[currentIndex + 1].centerVec)) { + return false + } + + return true + } + + private fun canAdvancePastNode(playerPos: BlockPos, node: PathNode): Boolean { + val level = Cobalt.minecraft.level + requireNotNull(level) + + return BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) || + (node.block.y <= playerPos.y && PlayerUtils.onGround) + } + + private fun isCloseEnoughToSegment( + nodeCenter: Vec3, + playerVec: Vec3, + nextNodeCenter: Vec3, + ): Boolean { + val segment = nextNodeCenter.subtract(nodeCenter) + val toPlayer = playerVec.subtract(nodeCenter) + + if (toPlayer.dot(segment) < 0.0) { + return false + } + + val squaredPerpendicularDistance = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() + return squaredPerpendicularDistance < 1.0 + } +} + + diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt new file mode 100644 index 0000000..9a33ec4 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt @@ -0,0 +1,96 @@ +package org.cobalt.pathfinder.state.pathing + +import net.minecraft.core.BlockPos +import org.cobalt.pathfinder.PathFindingConfig +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.helper.PlayerInput +import org.cobalt.pathfinder.movement.MovementInputMapper +import org.cobalt.util.PlayerUtils +import org.cobalt.util.RotationUtils + + +class PathingInputBuilder(private val jumpDecision: JumpDecision) { + fun build( + playerPos: BlockPos, + node: PathNode, + nodes: List, + sameXZ: Boolean, + currentIndex: Int, + ): PlayerInput { + return if (node.isFly && sameXZ) { + buildFlyVerticalInput(node, playerPos) + } else { + buildStandardInput(playerPos, node, nodes, sameXZ, currentIndex) + } + } + + private fun buildFlyVerticalInput( + node: PathNode, + playerPos: BlockPos, + ): PlayerInput { + val input = PlayerInput() + + val diffY = node.block.y - playerPos.y + when { + diffY > 0 -> input.jump = true + diffY < 0 -> input.sneak = true + } + + return input + } + + private fun buildStandardInput( + playerPos: BlockPos, + node: PathNode, + nodes: List, + sameXZ: Boolean, + currentIndex: Int, + ): PlayerInput { + val input = PlayerInput() + + applyHorizontalInput(input, node, sameXZ) + applySprintIfNeeded(input) + applyJumpIfNeeded(input, playerPos, nodes, currentIndex, node) + + return input + } + + private fun applyHorizontalInput( + input: PlayerInput, + node: PathNode, + sameXZ: Boolean, + ) { + if (sameXZ) { + return + } + + val neededKeys = MovementInputMapper.getNeededKeys( + PlayerUtils.rotation.yaw, + RotationUtils.getRotation(node.centerVec).yaw + ) + + input.apply(neededKeys) + } + + private fun applySprintIfNeeded(input: PlayerInput) { + if (PathFindingConfig.shouldSprint) { + input.sprint = true + } + } + + private fun applyJumpIfNeeded( + input: PlayerInput, + playerPos: BlockPos, + nodes: List, + currentIndex: Int, + node: PathNode, + ) { + if (node.isFly) { + return + } + + if (jumpDecision.shouldJump(playerPos, nodes, currentIndex)) { + input.jump = true + } + } +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt index 8462c57..ea09ed9 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt @@ -1,5 +1,99 @@ package org.cobalt.pathfinder.state.pathing +import net.minecraft.world.phys.AABB +import org.cobalt.module.impl.misc.Debug +import org.cobalt.module.impl.misc.Rotations +import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.calculate.PathNode +import org.cobalt.pathfinder.helper.PathRenderer +import org.cobalt.pathfinder.movement.PathLookTargetProjector import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.pathfinder.state.fly.StartFlyState +import org.cobalt.util.PlayerUtils +import org.cobalt.util.WorldRenderUtils +import org.cobalt.util.rotation.RotationTarget +import java.awt.Color -class PathingState : ExecutorState() +class PathingState : ExecutorState() { + private val path = PathFindingFacade.path + private val movements = PathFindingFacade.availableMovements + private val jumpDecision = JumpDecision() + private val inputBuilder = PathingInputBuilder(jumpDecision) + private val advancer = PathingAdvancer() + + private inline val pathIndex: Int + get() = PathFindingFacade.pathIndex + + override fun exit() { + input.stopMovement() + Rotations.stop() + } + + override fun onTick() { + if (path == null || movements == null) { + PathFindingFacade.stop() + return + } + + val player = PlayerUtils.player ?: return + val nodes = path.nodes + val node = nodes[pathIndex] + val playerPos = PlayerUtils.position + + if ( + advancer.advanceIfReached(playerPos, nodes) || + shouldStartFlying(node) + ) { + return + } + + val sameXZ = node.block.x == playerPos.x && node.block.z == playerPos.z + val movement = inputBuilder.build(playerPos, node, nodes, sameXZ, pathIndex) + val lookTarget = PathLookTargetProjector.getRotationTarget( + player.eyePosition, nodes, pathIndex + ) + + Rotations.track(RotationTarget(lookTarget)) + input.applyInput(movement) + } + + override fun onRender() { + if (path == null || movements == null) { + PathFindingFacade.stop() + return + } + + PathRenderer.render() + + if (!Debug.enabled) { + return + } + + val lookAhead = (PlayerUtils.velocity.length() * 6.0).coerceIn(5.0, 32.0) + val target = PathLookTargetProjector.getRotationTarget( + PlayerUtils.player!!.eyePosition, path.nodes, pathIndex, lookAhead + ) + + WorldRenderUtils.drawBox( + AABB( + target.x - 0.25, target.y - 0.25, target.z - 0.25, + target.x + 0.25, target.y + 0.25, target.z + 0.25 + ), Color.GREEN + ) + } + + private fun shouldStartFlying(node: PathNode): Boolean { + requireNotNull(movements) + + if ( + !node.isFly || + PlayerUtils.isFlying + ) { + return false + } + + PathFindingFacade.changeState(StartFlyState()) + return true + } + +} From ff5983af5aaa31370978cecdbac94fce38f24f66 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:12:28 -0400 Subject: [PATCH 22/28] chore: cleanup code..? --- .../org/cobalt/command/impl/MainCommand.kt | 14 +- .../org/cobalt/pathfinder/PathConfig.kt | 22 ++++ .../org/cobalt/pathfinder/PathExecutor.kt | 123 ++++++++++++++++++ .../org/cobalt/pathfinder/PathRenderer.kt | 52 ++++++++ .../cobalt/pathfinder/calculate/PathNode.kt | 3 +- .../calculate/path/AStarPathfinder.kt | 86 ++++++------ .../org/cobalt/pathfinder/helper/PathInput.kt | 55 ++++++++ .../pathfinder/movement/CalculationContext.kt | 1 + .../pathfinder/movement/MovementValidator.kt | 23 ++++ .../movement/fly/FlyAscendMovement.kt | 9 +- .../movement/fly/FlyDescendMovement.kt | 9 +- .../movement/fly/FlyDiagonalMovement.kt | 13 +- .../movement/fly/FlyTraverseMovement.kt | 9 +- .../movement/rules/PassthroughRules.kt | 6 +- .../movement/rules/StandingRules.kt | 111 ++++++++++++++++ .../movement/rules/data/BlockRule.kt | 2 + .../movement/rules/data/BlockSetRule.kt | 2 + .../movement/rules/data/BlockTypeRule.kt | 6 +- .../movement/walk/AscendMovement.kt | 15 ++- .../movement/walk/DescendMovement.kt | 11 +- .../movement/walk/DiagonalMovement.kt | 13 +- .../movement/walk/TraverseMovement.kt | 9 +- .../cobalt/pathfinder/state/ExecutorState.kt | 6 +- .../state/calculation/CalculatingState.kt | 45 ++++--- .../pathfinder/state/fly/StartFlyState.kt | 10 +- .../pathfinder/state/pathing/PathingState.kt | 96 +------------- src/main/kotlin/org/cobalt/util/BlockUtils.kt | 92 +++++++++++++ 27 files changed, 639 insertions(+), 204 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt create mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt create mode 100644 src/main/kotlin/org/cobalt/util/BlockUtils.kt diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 94593f3..a7ceb42 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -4,7 +4,9 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.command.Command import org.cobalt.command.annotation.DefaultHandler import org.cobalt.command.annotation.SubCommand -import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.PathConfig +import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen @@ -30,12 +32,18 @@ object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { @SubCommand fun goTo(x: Int, y: Int, z: Int, fly: Boolean) { - PathFindingFacade.goTo(GoalBlock(x, y, z), fly) + val pathMode = if (fly) PathMode.FLY else PathMode.WALK + val config = PathConfig( + goal = GoalBlock(x, y, z), + mode = pathMode + ) + + PathExecutor.goTo(config) } @SubCommand fun stopPathfinder() { - PathFindingFacade.stop() + PathExecutor.stop() } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt new file mode 100644 index 0000000..c99007f --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt @@ -0,0 +1,22 @@ +package org.cobalt.pathfinder + +import org.cobalt.pathfinder.calculate.PathMode +import org.cobalt.pathfinder.goal.Goal + +class PathConfig( + val goal: Goal, + val mode: PathMode = PathMode.WALK, + val shouldSprint: Boolean = true, + val returnBestNode: Boolean = false, + val maxCalculationTime: Long = MAX_CALCULATION_TIME, + val hasReachedThreshold: Double = 0.09, +) { + + val allowFly: Boolean = + mode == PathMode.FLY + + companion object { + const val MAX_CALCULATION_TIME = 10_000_000_000L + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt new file mode 100644 index 0000000..1656a99 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt @@ -0,0 +1,123 @@ +package org.cobalt.pathfinder + +import net.minecraft.client.player.KeyboardInput +import org.cobalt.Cobalt.minecraft +import org.cobalt.event.EventBus +import org.cobalt.event.annotation.SubscribeEvent +import org.cobalt.event.impl.TickEvent +import org.cobalt.event.impl.WorldEvent +import org.cobalt.pathfinder.calculate.Path +import org.cobalt.pathfinder.helper.PathInput +import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.pathfinder.state.calculation.CalculatingState +import org.cobalt.util.ChatUtils +import org.cobalt.util.MessageType +import org.cobalt.util.PlayerUtils + +object PathExecutor { + + var state: ExecutorState? = null + var config: PathConfig? = null + + var path: Path? = null + var pathIndex: Int = 0 + + var running = false + var pathInput = PathInput() + + init { + EventBus.register(this) + } + + fun goTo(config: PathConfig) { + val player = minecraft.player ?: run { + ChatUtils.sendSystemMessage( + "Tried running pathfinder, but minecraft.player is null!", + MessageType.DEBUG + ) + + return + } + + if (running) { + ChatUtils.sendSystemMessage( + "Stopping current pathfinder because a new one started.", + MessageType.DEBUG + ) + + stop() + } + + if (config.allowFly && !PlayerUtils.canFly) { + ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") + return + } + + player.input = pathInput + + this.config = config + this.running = true + + changeState(CalculatingState()) + } + + fun stop() { + state?.exit() + state = null + + minecraft.player?.let { + it.input = KeyboardInput(minecraft.options) + }.also { + pathInput.stopMovement() + } + + running = false + + path = null + pathIndex = 0 + } + + fun changeState(newState: ExecutorState) { + state?.exit() + state = newState + state?.enter() + + ChatUtils.sendSystemMessage( + "Entering ${newState.javaClass.simpleName} Executor State", + MessageType.DEBUG + ) + } + + @SubscribeEvent + fun onTick(ignored: TickEvent.Start) { + if (minecraft.level == null || minecraft.player == null) { + stop() + return + } + + if (minecraft.gui.screen() != null) { + pathInput.stopMovement() + return + } + + if (!running) { + return + } + + state?.onTick() + } + + @SubscribeEvent + fun onRender(ignored: WorldEvent.Render) { + if (!running) { + return + } + + if (state !is CalculatingState) { + PathRenderer.render() + } + + state?.onRender() + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt new file mode 100644 index 0000000..82d5005 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt @@ -0,0 +1,52 @@ +package org.cobalt.pathfinder + +import org.cobalt.dsl.centerVec +import org.cobalt.dsl.smallBox +import org.cobalt.module.impl.misc.Debug +import org.cobalt.ui.theme.ThemeManager +import org.cobalt.util.PlayerUtils +import org.cobalt.util.WorldRenderUtils +import java.awt.Color +import org.cobalt.pathfinder.calculate.Path +import org.cobalt.ui.theme.Theme + +object PathRenderer { + + private inline val theme: Theme + get() = ThemeManager.activeTheme + + fun render() { + val path: Path = PathExecutor.path ?: return + + val nodes = path.nodes + val keyNodes = path.keyNodes + + val targetNode = nodes[PathExecutor.pathIndex].centerVec + val playerPos = PlayerUtils.position.centerVec() + + if (Debug.enabled) { + WorldRenderUtils.drawBox(playerPos.smallBox(), Color.GREEN) + WorldRenderUtils.drawBox(targetNode.smallBox(), Color.RED) + } + + for (index in keyNodes.indices) { + val node = keyNodes[index] + + WorldRenderUtils.drawBlockPos( + if (node.useMovementFly) node.block else node.blockStandingOn, + color = theme.accentPrimary + ) + + if (index <= 0) continue + + val prev = keyNodes[index - 1] + + WorldRenderUtils.drawLine( + if (prev.useMovementFly) prev.centerVec else prev.topCenterVec, + if (node.useMovementFly) node.centerVec else node.topCenterVec, + theme.accentSecondary + ) + } + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt index dfac851..901013a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/PathNode.kt @@ -16,6 +16,7 @@ data class PathNode( val costToEnd = goal.heuristic(x, y, z) var totalCost = 1.0 var heapPosition = -1 + var movementType = MovementType.WALK var parent: PathNode? = null @@ -24,7 +25,7 @@ data class PathNode( val centerVec: Vec3 = Vec3(x + 0.5, y + 0.5, z + 0.5) val topCenterVec: Vec3 = Vec3(x + 0.5, y.toDouble(), z + 0.5) - val isFly: Boolean + val useMovementFly: Boolean get() = movementType == MovementType.FLY override fun compareTo(other: PathNode): Int { diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index 608aeb9..daaf817 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -1,9 +1,10 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.nanoseconds -import org.cobalt.pathfinder.PathFindingConfig import org.cobalt.pathfinder.calculate.Path +import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.calculate.openset.BinaryHeapOpenSet import org.cobalt.pathfinder.goal.Goal @@ -12,68 +13,77 @@ import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.util.ChatUtils import org.cobalt.util.MessageType -import org.cobalt.util.PlayerUtils -object AStarPathfinder { - private val nodeCache = Long2ObjectOpenHashMap() - private var goal: Goal? = null // This can't be null after findPath is called. - private var availableMovements: Array? = null // This can't be null after findPath is called. - - fun findPath(goal: Goal, movements: Array, returnBestNode: Boolean): Path? { - val startPos = PlayerUtils.position +class AStarPathfinder( + val startX: Int, + val startY: Int, + val startZ: Int, + val goal: Goal, + val mode: PathMode, + val returnBestNode: Boolean = false, + val maxCalculationTime: Long = 10_000_000L +) { + + private val closedSet = Long2ObjectOpenHashMap() + private val movements = mode.movements + private var startTime = 0L + + fun findPath(): Path? { val calculationContext = CalculationContext() val openSet = BinaryHeapOpenSet() val movementResult = MovementResult() - // After this, goal and movements should never be null again, so we can do !! in private functions safely. - // If they are null, someone nuked the code, and we should get angry at them. - AStarPathfinder.goal = goal - availableMovements = movements - - val startNode = PathNode(startPos.x, startPos.y, startPos.z, goal) - - startNode.costSoFar = 0.0 - startNode.totalCost = startNode.costToEnd + val startNode = PathNode( + startX, startY, startZ, goal + ).also { + it.costSoFar = 0.0 + it.totalCost = it.costToEnd + } openSet.add(startNode) + startTime = System.nanoTime() - val startTime = System.nanoTime() var bestNode = startNode + val deadline = startTime + maxCalculationTime - val deadline = startTime + PathFindingConfig.maxCalculationTime ChatUtils.sendSystemMessage( "Starting pathfinding from ${startNode.block}", MessageType.DEBUG ) - while (!openSet.isEmpty() - && System.nanoTime() < deadline - ) { + while (!openSet.isEmpty() && System.nanoTime() < deadline) { val currentNode = openSet.poll() - if (currentNode < bestNode) bestNode = currentNode + if (currentNode < bestNode) { + bestNode = currentNode + } if (goal.isAtGoal(currentNode.x, currentNode.y, currentNode.z)) { - return reconstruct(currentNode, startTime) + return reconstruct(currentNode) } - evaluateMovements(currentNode, calculationContext, movementResult, openSet) + evaluateMovements( + calculationContext, + currentNode, + movementResult, + openSet + ) } return if (returnBestNode) { - reconstruct(bestNode, startTime) + reconstruct(bestNode) } else { null } } private fun evaluateMovements( - currentNode: PathNode, calculationContext: CalculationContext, + currentNode: PathNode, movementResult: MovementResult, openSet: BinaryHeapOpenSet, ) { - for (move in availableMovements!!) { + for (move in movements) { movementResult.reset() move.calculateCost(calculationContext, currentNode, movementResult) @@ -90,12 +100,14 @@ object AStarPathfinder { openSet: BinaryHeapOpenSet, ) { val neighborCostSoFar = currentNode.costSoFar + movementResult.cost - val neighborNode = getOrCacheNode( + val neighborNode = getNode( movementResult.x, movementResult.y, movementResult.z, PathNode.longHash(movementResult.x, movementResult.y, movementResult.z) ) - if (neighborCostSoFar >= neighborNode.costSoFar) return + if (neighborCostSoFar >= neighborNode.costSoFar) { + return + } neighborNode.parent = currentNode neighborNode.costSoFar = neighborCostSoFar @@ -109,18 +121,18 @@ object AStarPathfinder { } } - fun getOrCacheNode(x: Int, y: Int, z: Int, hash: Long): PathNode { - var node: PathNode? = nodeCache.get(hash) + fun getNode(x: Int, y: Int, z: Int, hash: Long): PathNode { + var node: PathNode? = closedSet.get(hash) if (node == null) { - node = PathNode(x, y, z, goal!!) - nodeCache.put(hash, node) + node = PathNode(x, y, z, goal) + closedSet.put(hash, node) } return node } - private fun reconstruct(endNode: PathNode, startTime: Long): Path { + private fun reconstruct(endNode: PathNode): Path { val path = mutableListOf() var node: PathNode? = endNode @@ -132,7 +144,7 @@ object AStarPathfinder { return Path( nodes = path, timeElapsed = (System.nanoTime() - startTime).nanoseconds, - goal = goal!! + goal = goal ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt new file mode 100644 index 0000000..70d7886 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt @@ -0,0 +1,55 @@ +package org.cobalt.pathfinder.helper + +import net.minecraft.client.player.ClientInput +import net.minecraft.world.entity.player.Input +import net.minecraft.world.phys.Vec2 + +class PathInput : ClientInput() { + + var forward = false + var backward = false + var left = false + var right = false + var jump = false + var shift = false + var sprint = false + + fun applyInput(input: PlayerInput) { + forward = input.forward + backward = input.backward + left = input.left + right = input.right + jump = input.jump + sprint = input.sprint + shift = input.sneak + } + + fun stopMovement() { + applyInput(PlayerInput()) + } + + override fun tick() { + this.keyPresses = Input( + forward, + backward, + left, + right, + jump, + shift, + sprint + ) + + val forwardImpulse = calculateImpulse(forward, backward) + val leftImpulse = calculateImpulse(left, right) + this.moveVector = Vec2(leftImpulse, forwardImpulse).normalized() + } + + private fun calculateImpulse(positive: Boolean, negative: Boolean): Float { + return when { + positive == negative -> 0.0f + positive -> 1.0f + else -> -1.0f + } + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt index 3dfefae..5733196 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt @@ -15,4 +15,5 @@ class CalculationContext { val jumpAmplifier = player.getEffect(MobEffects.JUMP_BOOST)?.amplifier ?: -1 val blockStateAccessor = BlockStateAccessor(level) + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt new file mode 100644 index 0000000..5bc1f2d --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementValidator.kt @@ -0,0 +1,23 @@ +package org.cobalt.pathfinder.movement + +import org.cobalt.pathfinder.movement.rules.PassthroughRules.canPassThrough +import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn + +object MovementValidator { + + @JvmStatic + fun canWalkOn(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { + if (!canWalkThrough(calculationContext, x, y + 1, z)) { + return false + } + + return canStandOn(calculationContext.blockStateAccessor, x, y, z) + } + + @JvmStatic + fun canWalkThrough(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { + return canPassThrough(calculationContext.blockStateAccessor, x, y, z) && + canPassThrough(calculationContext.blockStateAccessor, x, y + 1, z) + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt index 9b33671..5514c36 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyAscendMovement : Movement(MovementType.FLY) { @@ -15,7 +18,7 @@ class FlyAscendMovement : Movement(MovementType.FLY) { val y = currNode.y + 1 val z = currNode.z - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt index 92f136a..46a94b0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyDescendMovement : Movement(MovementType.FLY) { @@ -15,7 +18,7 @@ class FlyDescendMovement : Movement(MovementType.FLY) { val y = currNode.y - 1 val z = currNode.z - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt index 5078010..75d35fd 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyDiagonalMovement( val dx: Int, @@ -18,15 +21,15 @@ class FlyDiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt index 89bf73a..86d319f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class FlyTraverseMovement( val dx: Int, @@ -18,7 +21,7 @@ class FlyTraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkThrough(ctx, x, y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, y, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt index a14c950..654b82f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt @@ -21,11 +21,11 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties import net.minecraft.world.level.material.WaterFluid import net.minecraft.world.level.pathfinder.PathComputationType import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.movement.FlowingCheck -import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn +import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary +import org.cobalt.util.BlockUtils object PassthroughRules { @@ -152,7 +152,7 @@ object PassthroughRules { !state.fluidState.isEmpty -> { val fluidState = state.fluidState - if (FlowingCheck.isFlowing(x, y, z, state, bsa)) { + if (BlockUtils.isFlowing(x, y, z, state, bsa)) { false } else { val upState = bsa.get(x, y + 1, z) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt new file mode 100644 index 0000000..a7d71e5 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt @@ -0,0 +1,111 @@ +package org.cobalt.pathfinder.movement.rules + +import net.minecraft.world.level.block.AzaleaBlock +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.CarpetBlock +import net.minecraft.world.level.block.SlabBlock +import net.minecraft.world.level.block.StainedGlassBlock +import net.minecraft.world.level.block.StairBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.Fluids +import org.cobalt.pathfinder.helper.BlockStateAccessor +import org.cobalt.pathfinder.movement.rules.data.BlockSetRule +import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule +import org.cobalt.pathfinder.movement.rules.data.Ternary +import org.cobalt.util.BlockUtils + +object StandingRules { + + val STANDING_RULES = listOf( + BlockSetRule( + setOf( + Blocks.LADDER, + Blocks.VINE, + Blocks.FARMLAND, + Blocks.DIRT_PATH, + Blocks.SOUL_SAND, + Blocks.ENDER_CHEST, + Blocks.CHEST, + Blocks.TRAPPED_CHEST, + Blocks.GLASS, + ), + Ternary.YES, + ), + + BlockTypeRule( + setOf( + StainedGlassBlock::class.java, + StairBlock::class.java, + SlabBlock::class.java, + AzaleaBlock::class.java, + ), + Ternary.YES, + ), + ) + + private val PRECOMPUTED = Array(Block.BLOCK_STATE_REGISTRY.size()) { + Ternary.NO + } + + init { + Block.BLOCK_STATE_REGISTRY.forEach { state -> + PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] = compute(state) + } + } + + private fun compute(state: BlockState): Ternary { + val block = state.block + + if (BlockUtils.isBlockNormalCube(state) && + block != Blocks.BUBBLE_COLUMN && + block != Blocks.HONEY_BLOCK + ) { + return Ternary.YES + } + + STANDING_RULES.firstOrNull { it.matches(state) } + ?.let { return it.result } + + return when { + BlockUtils.isWater(state) -> Ternary.MAYBE + BlockUtils.isLava(state) -> Ternary.MAYBE + else -> Ternary.NO + } + } + + fun canStandOn(bsa: BlockStateAccessor, x: Int, y: Int, z: Int): Boolean { + val state = bsa.get(x, y, z) + val result = PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] + + if (result == Ternary.YES) { + return true + } + + if (result == Ternary.NO) { + return false + } + + return resolveMaybe(bsa, x, y, z, state) + } + + private fun resolveMaybe(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { + if (BlockUtils.isWater(state)) { + val upState = bsa.get(x, y + 1, z) + val upBlock = upState.block + + if (upBlock == Blocks.LILY_PAD || upBlock is CarpetBlock) { + return true + } + + if (BlockUtils.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { + return BlockUtils.isWater(upState) + } + + return BlockUtils.isWater(upState) xor false + } + + return false + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt index a7c22da..09fe992 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockRule.kt @@ -3,7 +3,9 @@ package org.cobalt.pathfinder.movement.rules.data import net.minecraft.world.level.block.state.BlockState interface BlockRule { + val result: Ternary fun matches(state: BlockState): Boolean + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt index 0f8f0ca..1a949f4 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockSetRule.kt @@ -7,6 +7,8 @@ class BlockSetRule( private val blocks: Set, override val result: Ternary, ) : BlockRule { + override fun matches(state: BlockState): Boolean = state.block in blocks + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt index c720ee0..3d837a6 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/data/BlockTypeRule.kt @@ -4,9 +4,11 @@ import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState class BlockTypeRule( - private val types: Set>, - override val result: Ternary, + private val types: Set>, + override val result: Ternary, ) : BlockRule { + override fun matches(state: BlockState): Boolean = types.any { it.isInstance(state.block) } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt index 9954540..0a4ecb0 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( @@ -19,11 +22,15 @@ class AscendMovement( val y = currNode.y + 1 val z = currNode.z + dz - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, currNode.x, currNode.y, currNode.z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { + return + } + + if (!MovementValidator.canWalkThrough(ctx, currNode.x, currNode.y + 1, currNode.z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt index 39a26dc..2466173 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( @@ -19,11 +22,11 @@ class DescendMovement( val y = currNode.y - 1 val z = currNode.z + dz - if (!BlockTraversalRules.canWalkThrough(ctx, x, currNode.y, z)) { + if (!MovementValidator.canWalkThrough(ctx, x, currNode.y, z)) { return } - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt index 8ea0bef..6e7b5a3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class DiagonalMovement( val dx: Int, @@ -18,15 +21,15 @@ class DiagonalMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x + dx, y, currNode.z)) { return } - if (!BlockTraversalRules.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { + if (!MovementValidator.canWalkThrough(ctx, currNode.x, y, currNode.z + dz)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt index 57b8585..e824ee6 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt @@ -1,8 +1,11 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.* -import org.cobalt.pathfinder.movement.rules.BlockTraversalRules +import org.cobalt.pathfinder.movement.CalculationContext +import org.cobalt.pathfinder.movement.Movement +import org.cobalt.pathfinder.movement.MovementValidator +import org.cobalt.pathfinder.movement.MovementResult +import org.cobalt.pathfinder.movement.MovementType class TraverseMovement( val dx: Int, @@ -18,7 +21,7 @@ class TraverseMovement( val y = currNode.y val z = currNode.z + dz - if (!BlockTraversalRules.canWalkOn(ctx, x, y - 1, z)) { + if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt index fd3c7ff..58a793f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt @@ -1,12 +1,12 @@ package org.cobalt.pathfinder.state -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.PathInput +import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.helper.PathInput abstract class ExecutorState { protected val input: PathInput = - PathFindingFacade.pathInput + PathExecutor.pathInput open fun enter() {} open fun onTick() {} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt index 3e1b16d..948d186 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt @@ -5,49 +5,47 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.PathFindingFacade +import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.ChatUtils +import org.cobalt.util.PlayerUtils class CalculatingState : ExecutorState() { + private var calculationJob: Job? = null override fun enter() { - val goal = PathFindingFacade.currentGoal - val availableMovements = PathFindingFacade.availableMovements - - if (goal == null) { - ChatUtils.sendSystemMessage("Cannot calculate path, no goal set!") - PathFindingFacade.stop() + val config = PathExecutor.config ?: run { + ChatUtils.sendSystemMessage("Cannot calculate path, no path config set!") + PathExecutor.stop() return } - if (availableMovements == null) { - ChatUtils.sendSystemMessage("Cannot calculate path, no movements set!") - PathFindingFacade.stop() - return - } + val startPos = PlayerUtils.position + val goal = config.goal calculationJob = CoroutineScope(Dispatchers.Default).launch { - val path = AStarPathfinder.findPath( - goal, - availableMovements, - PathFindingConfig.returnBestNode - ) - - if (!isActive) return@launch + val path = AStarPathfinder( + startPos.x, startPos.y, startPos.z, + goal, config.mode, + config.returnBestNode, + config.maxCalculationTime + ).findPath() + + if (!isActive) { + return@launch + } if (path == null) { ChatUtils.sendSystemMessage("Unable to find a path") - PathFindingFacade.stop() + PathExecutor.stop() return@launch } - PathFindingFacade.path = path - PathFindingFacade.changeState(PathingState()) + PathExecutor.path = path + PathExecutor.changeState(PathingState()) } } @@ -55,4 +53,5 @@ class CalculatingState : ExecutorState() { calculationJob?.cancel() calculationJob = null } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt index ac27ae3..354a689 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt @@ -1,7 +1,6 @@ package org.cobalt.pathfinder.state.fly -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.helper.PathRenderer +import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState import org.cobalt.util.PlayerUtils @@ -19,11 +18,7 @@ class StartFlyState : ExecutorState() { return } - PathFindingFacade.changeState(PathingState()) - } - - override fun onRender() { - PathRenderer.render() + PathExecutor.changeState(PathingState()) } private fun handleFlyStart(): Boolean { @@ -68,4 +63,5 @@ class StartFlyState : ExecutorState() { SECOND_JUMP, RELEASE_SECOND_JUMP, } + } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt index ea09ed9..8462c57 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt @@ -1,99 +1,5 @@ package org.cobalt.pathfinder.state.pathing -import net.minecraft.world.phys.AABB -import org.cobalt.module.impl.misc.Debug -import org.cobalt.module.impl.misc.Rotations -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.helper.PathRenderer -import org.cobalt.pathfinder.movement.PathLookTargetProjector import org.cobalt.pathfinder.state.ExecutorState -import org.cobalt.pathfinder.state.fly.StartFlyState -import org.cobalt.util.PlayerUtils -import org.cobalt.util.WorldRenderUtils -import org.cobalt.util.rotation.RotationTarget -import java.awt.Color -class PathingState : ExecutorState() { - private val path = PathFindingFacade.path - private val movements = PathFindingFacade.availableMovements - private val jumpDecision = JumpDecision() - private val inputBuilder = PathingInputBuilder(jumpDecision) - private val advancer = PathingAdvancer() - - private inline val pathIndex: Int - get() = PathFindingFacade.pathIndex - - override fun exit() { - input.stopMovement() - Rotations.stop() - } - - override fun onTick() { - if (path == null || movements == null) { - PathFindingFacade.stop() - return - } - - val player = PlayerUtils.player ?: return - val nodes = path.nodes - val node = nodes[pathIndex] - val playerPos = PlayerUtils.position - - if ( - advancer.advanceIfReached(playerPos, nodes) || - shouldStartFlying(node) - ) { - return - } - - val sameXZ = node.block.x == playerPos.x && node.block.z == playerPos.z - val movement = inputBuilder.build(playerPos, node, nodes, sameXZ, pathIndex) - val lookTarget = PathLookTargetProjector.getRotationTarget( - player.eyePosition, nodes, pathIndex - ) - - Rotations.track(RotationTarget(lookTarget)) - input.applyInput(movement) - } - - override fun onRender() { - if (path == null || movements == null) { - PathFindingFacade.stop() - return - } - - PathRenderer.render() - - if (!Debug.enabled) { - return - } - - val lookAhead = (PlayerUtils.velocity.length() * 6.0).coerceIn(5.0, 32.0) - val target = PathLookTargetProjector.getRotationTarget( - PlayerUtils.player!!.eyePosition, path.nodes, pathIndex, lookAhead - ) - - WorldRenderUtils.drawBox( - AABB( - target.x - 0.25, target.y - 0.25, target.z - 0.25, - target.x + 0.25, target.y + 0.25, target.z + 0.25 - ), Color.GREEN - ) - } - - private fun shouldStartFlying(node: PathNode): Boolean { - requireNotNull(movements) - - if ( - !node.isFly || - PlayerUtils.isFlying - ) { - return false - } - - PathFindingFacade.changeState(StartFlyState()) - return true - } - -} +class PathingState : ExecutorState() diff --git a/src/main/kotlin/org/cobalt/util/BlockUtils.kt b/src/main/kotlin/org/cobalt/util/BlockUtils.kt new file mode 100644 index 0000000..b96769f --- /dev/null +++ b/src/main/kotlin/org/cobalt/util/BlockUtils.kt @@ -0,0 +1,92 @@ +package org.cobalt.util + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.EmptyBlockGetter +import net.minecraft.world.level.block.AmethystClusterBlock +import net.minecraft.world.level.block.BambooStalkBlock +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.PointedDripstoneBlock +import net.minecraft.world.level.block.ScaffoldingBlock +import net.minecraft.world.level.block.ShulkerBoxBlock +import net.minecraft.world.level.block.SlabBlock +import net.minecraft.world.level.block.piston.MovingPistonBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.block.state.properties.SlabType +import net.minecraft.world.level.material.FlowingFluid +import net.minecraft.world.level.material.Fluids +import org.cobalt.pathfinder.helper.BlockStateAccessor + +object BlockUtils { + + @JvmStatic + fun isWater(state: BlockState): Boolean { + val fluid = state.fluidState.type + return fluid == Fluids.WATER || fluid == Fluids.FLOWING_WATER + } + + @JvmStatic + fun isLava(state: BlockState): Boolean { + val fluid = state.fluidState.type + return fluid == Fluids.LAVA || fluid == Fluids.FLOWING_LAVA + } + + @JvmStatic + fun isBottomSlab(state: BlockState): Boolean { + return state.block is SlabBlock && + state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM + } + + @JvmStatic + fun isBlockNormalCube(state: BlockState): Boolean { + val block = state.block + + if ( + block is BambooStalkBlock || + block is MovingPistonBlock || + block is ScaffoldingBlock + ) { + return false + } + + if ( + block is ShulkerBoxBlock || + block is PointedDripstoneBlock || + block is AmethystClusterBlock + ) { + return false + } + + return try { + Block.isShapeFullBlock(state.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) + } catch (_: Exception) { + false + } + } + + @JvmStatic + fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { + val fluidState = state.fluidState + + if (fluidState.type !is FlowingFluid) { + return false + } + + if (fluidState.type.getAmount(fluidState) != 8) { + return true + } + + return possiblyFlowing(bsa.get(x + 1, y, z)) + || possiblyFlowing(bsa.get(x - 1, y, z)) + || possiblyFlowing(bsa.get(x, y, z + 1)) + || possiblyFlowing(bsa.get(x, y, z - 1)) + } + + @JvmStatic + fun possiblyFlowing(state: BlockState): Boolean { + val fluidState = state.fluidState + + return fluidState.type is FlowingFluid && + fluidState.type.getAmount(fluidState) != 8 + } + +} From c9701ffdff43be0759abc03110305854386d0965 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:14:53 -0400 Subject: [PATCH 23/28] chore: oops --- .../cobalt/pathfinder/PathFindingConfig.kt | 8 - .../cobalt/pathfinder/PathFindingFacade.kt | 126 -------- .../kotlin/org/cobalt/pathfinder/PathInput.kt | 56 ---- .../cobalt/pathfinder/helper/PathRenderer.kt | 46 --- .../pathfinder/movement/FlowingCheck.kt | 32 --- .../movement/MovementInputMapper.kt | 83 ------ .../movement/PathLookTargetProjector.kt | 271 ------------------ .../movement/rules/BlockSupportRules.kt | 56 ---- .../movement/rules/BlockTraversalRules.kt | 24 -- .../pathfinder/movement/rules/StandOnRules.kt | 110 ------- .../pathfinder/state/pathing/JumpDecision.kt | 47 --- .../state/pathing/PathingAdvancer.kt | 83 ------ .../state/pathing/PathingInputBuilder.kt | 96 ------- 13 files changed, 1038 deletions(-) delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/PathInput.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt deleted file mode 100644 index 02d351f..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFindingConfig.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.cobalt.pathfinder - -object PathFindingConfig { - var shouldSprint: Boolean = true - var returnBestNode: Boolean = false - var maxCalculationTime: Long = 10_000_000_000L - var hasReachedThreshold: Double = 0.09 // // = sqrt(0.3), so 0.3 blocks of distance is the threshold -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt b/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt deleted file mode 100644 index 4e94eb7..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/PathFindingFacade.kt +++ /dev/null @@ -1,126 +0,0 @@ -package org.cobalt.pathfinder - -import net.minecraft.client.player.KeyboardInput -import org.cobalt.Cobalt.minecraft -import org.cobalt.event.EventBus -import org.cobalt.event.annotation.SubscribeEvent -import org.cobalt.event.impl.TickEvent -import org.cobalt.event.impl.WorldEvent -import org.cobalt.pathfinder.calculate.Path -import org.cobalt.pathfinder.calculate.PathMode -import org.cobalt.pathfinder.goal.Goal -import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.state.ExecutorState -import org.cobalt.pathfinder.state.calculation.CalculatingState -import org.cobalt.util.ChatUtils -import org.cobalt.util.MessageType -import org.cobalt.util.PlayerUtils - -object PathFindingFacade { - var state: ExecutorState? = null - - var currentGoal: Goal? = null - var availableMovements: Array? = null - - var path: Path? = null - var pathIndex: Int = 0 - - var running = false - var pathInput = PathInput() - - init { - EventBus.register(this) - } - - fun goTo(goal: Goal, fly: Boolean = false) { - // Are we sure we want to do this? There's no warning or anything. - // I've kept it but with a debug message - if (running) { - ChatUtils.sendSystemMessage( - "Stopping current pathfinder because a new one started.", - MessageType.DEBUG - ) - stop() - } - - if (fly && !PlayerUtils.canFly) { - ChatUtils.sendSystemMessage("Invalid path config, since player cannot fly!") - return - } - - val player = minecraft.player - - if (player == null) { - ChatUtils.sendSystemMessage( - "Tried running pathfinder, but minecraft.player is null!", - MessageType.DEBUG - ) - return - } else { - player.input = pathInput - } - - availableMovements = if (fly) PathMode.FLY.movements else PathMode.WALK.movements - currentGoal = goal - - this.running = true - - changeState(CalculatingState()) - } - - fun stop() { - state?.exit() - state = null - - minecraft.player?.let { - it.input = KeyboardInput(minecraft.options) - }.also { - pathInput.stopMovement() - } - - running = false - - path = null - pathIndex = 0 - } - - fun changeState(newState: ExecutorState) { - state?.exit() - state = newState - state?.enter() - - ChatUtils.sendSystemMessage( - "Entering ${newState.javaClass.simpleName} Executor State", - MessageType.DEBUG - ) - } - - @SubscribeEvent - fun onTick(ignored: TickEvent.Start) { - if (minecraft.level == null || minecraft.player == null) { - stop() - return - } - - if (minecraft.gui.screen() != null) { - pathInput.stopMovement() - return - } - - if (!running) { - return - } - - state?.onTick() - } - - @SubscribeEvent - fun onRender(ignored: WorldEvent.Render) { - if (!running) { - return - } - - state?.onRender() - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt deleted file mode 100644 index 96152ec..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/PathInput.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.cobalt.pathfinder - -import net.minecraft.client.player.ClientInput -import net.minecraft.world.entity.player.Input -import net.minecraft.world.phys.Vec2 -import org.cobalt.pathfinder.helper.PlayerInput - -class PathInput : ClientInput() { - - var forward = false - var backward = false - var left = false - var right = false - var jump = false - var shift = false - var sprint = false - - fun applyInput(input: PlayerInput) { - forward = input.forward - backward = input.backward - left = input.left - right = input.right - jump = input.jump - sprint = input.sprint - shift = input.sneak - } - - fun stopMovement() { - applyInput(PlayerInput()) - } - - override fun tick() { - this.keyPresses = Input( - forward, - backward, - left, - right, - jump, - shift, - sprint - ) - - val forwardImpulse = calculateImpulse(forward, backward) - val leftImpulse = calculateImpulse(left, right) - this.moveVector = Vec2(leftImpulse, forwardImpulse).normalized() - } - - private fun calculateImpulse(positive: Boolean, negative: Boolean): Float { - return when { - positive == negative -> 0.0f - positive -> 1.0f - else -> -1.0f - } - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt deleted file mode 100644 index d503cd3..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PathRenderer.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.cobalt.pathfinder.helper - -import java.awt.Color -import org.cobalt.dsl.centerVec -import org.cobalt.dsl.smallBox -import org.cobalt.module.impl.misc.Debug -import org.cobalt.pathfinder.PathFindingFacade.path -import org.cobalt.pathfinder.PathFindingFacade.pathIndex -import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.PlayerUtils -import org.cobalt.util.WorldRenderUtils - -object PathRenderer { - fun render() { - val theme = ThemeManager.activeTheme - val nodes = path?.nodes ?: return - val keyNodes = path?.keyNodes ?: return - - val targetNode = nodes[pathIndex].centerVec - val playerPos = PlayerUtils.position.centerVec() - - if (Debug.enabled) { - WorldRenderUtils.drawBox(playerPos.smallBox(), Color.GREEN) - WorldRenderUtils.drawBox(targetNode.smallBox(), Color.RED) - } - - for (index in keyNodes.indices) { - val node = keyNodes[index] - - WorldRenderUtils.drawBlockPos( - if (node.isFly) node.block else node.blockStandingOn, - color = theme.accentPrimary - ) - - if (index <= 0) continue - - val prev = keyNodes[index - 1] - - WorldRenderUtils.drawLine( - if (prev.isFly) prev.centerVec else prev.topCenterVec, - if (node.isFly) node.centerVec else node.topCenterVec, - theme.accentSecondary - ) - } - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt deleted file mode 100644 index d30886f..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/FlowingCheck.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.cobalt.pathfinder.movement - -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.level.material.FlowingFluid -import org.cobalt.pathfinder.helper.BlockStateAccessor - -object FlowingCheck { - private fun possiblyFlowing(state: BlockState): Boolean { - val fluidState = state.fluidState - - return fluidState.type is FlowingFluid && - fluidState.type.getAmount(fluidState) != 8 - } - - fun isFlowing(x: Int, y: Int, z: Int, state: BlockState, bsa: BlockStateAccessor): Boolean { - val fluidState = state.fluidState - - if (fluidState.type !is FlowingFluid) { - return false - } - - if (fluidState.type.getAmount(fluidState) != 8) { - return true - } - - return possiblyFlowing(bsa.get(x + 1, y, z)) - || possiblyFlowing(bsa.get(x - 1, y, z)) - || possiblyFlowing(bsa.get(x, y, z + 1)) - || possiblyFlowing(bsa.get(x, y, z - 1)) - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt deleted file mode 100644 index bf406c0..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/MovementInputMapper.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.cobalt.pathfinder.movement - -import net.minecraft.util.Mth -import org.cobalt.pathfinder.helper.PlayerInput - -object MovementInputMapper { - private const val DIRECTIONS_COUNT = 8 - private const val FULL_CIRCLE_DEGREES = 360f - private const val SECTOR_WIDTH_DEGREES = FULL_CIRCLE_DEGREES / DIRECTIONS_COUNT - private const val SECTOR_HALF_WIDTH_DEGREES = SECTOR_WIDTH_DEGREES / 2f - - private const val DEAD_BAND_DEGREES = 0.05f - - private const val FORWARD_RIGHT_START_DEGREES = SECTOR_HALF_WIDTH_DEGREES - private const val RIGHT_START_DEGREES = FORWARD_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val BACK_RIGHT_START_DEGREES = RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - private const val BACKWARD_START_DEGREES = BACK_RIGHT_START_DEGREES + SECTOR_WIDTH_DEGREES - - private const val FORWARD_LEFT_START_DEGREES = -FORWARD_RIGHT_START_DEGREES - private const val LEFT_START_DEGREES = -RIGHT_START_DEGREES - private const val BACK_LEFT_START_DEGREES = -BACK_RIGHT_START_DEGREES - private const val BACKWARD_NEG_START_DEGREES = -BACKWARD_START_DEGREES - - private const val FORWARD_RIGHT_HOLD_DEGREES = FORWARD_RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val RIGHT_HOLD_DEGREES = RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val BACK_RIGHT_HOLD_DEGREES = BACK_RIGHT_START_DEGREES + DEAD_BAND_DEGREES - private const val BACKWARD_HOLD_DEGREES = BACKWARD_START_DEGREES + DEAD_BAND_DEGREES - - private const val FORWARD_LEFT_HOLD_DEGREES = FORWARD_LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val LEFT_HOLD_DEGREES = LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val BACK_LEFT_HOLD_DEGREES = BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES - private const val BACKWARD_NEG_HOLD_DEGREES = BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES - - private enum class Sector { FORWARD, FORWARD_RIGHT, RIGHT, BACK_RIGHT, BACKWARD, BACK_LEFT, LEFT, FORWARD_LEFT } - - private var lastSector: Sector = Sector.FORWARD - - @JvmStatic - fun getNeededKeys(playerYaw: Float, idealYaw: Float): PlayerInput { - val diff = Mth.wrapDegrees(idealYaw - playerYaw) - - if (staysInSector(lastSector, diff)) { - return sectorToInput(lastSector) - } - - val newSector = classify(diff) - lastSector = newSector - return sectorToInput(newSector) - } - - private fun staysInSector(sector: Sector, diff: Float): Boolean = when (sector) { - Sector.FORWARD -> diff in FORWARD_LEFT_HOLD_DEGREES.. diff >= FORWARD_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < RIGHT_HOLD_DEGREES - Sector.RIGHT -> diff >= RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_RIGHT_HOLD_DEGREES - Sector.BACK_RIGHT -> diff >= BACK_RIGHT_START_DEGREES - DEAD_BAND_DEGREES && diff < BACKWARD_HOLD_DEGREES - Sector.BACKWARD -> diff >= BACKWARD_START_DEGREES - DEAD_BAND_DEGREES || diff < BACKWARD_NEG_HOLD_DEGREES - Sector.BACK_LEFT -> diff >= BACKWARD_NEG_START_DEGREES - DEAD_BAND_DEGREES && diff < BACK_LEFT_HOLD_DEGREES - Sector.LEFT -> diff >= BACK_LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < LEFT_HOLD_DEGREES - Sector.FORWARD_LEFT -> diff >= LEFT_START_DEGREES - DEAD_BAND_DEGREES && diff < FORWARD_LEFT_HOLD_DEGREES - } - - private fun classify(diff: Float): Sector = when (diff) { - in FORWARD_LEFT_START_DEGREES.. Sector.FORWARD - in FORWARD_RIGHT_START_DEGREES.. Sector.FORWARD_RIGHT - in RIGHT_START_DEGREES.. Sector.RIGHT - in BACK_RIGHT_START_DEGREES.. Sector.BACK_RIGHT - !in BACKWARD_NEG_START_DEGREES.. Sector.BACKWARD - in BACKWARD_NEG_START_DEGREES.. Sector.BACK_LEFT - in BACK_LEFT_START_DEGREES.. Sector.LEFT - else -> Sector.FORWARD_LEFT - } - - private fun sectorToInput(sector: Sector): PlayerInput = when (sector) { - Sector.FORWARD -> PlayerInput(forward = true) - Sector.FORWARD_RIGHT -> PlayerInput(forward = true, right = true) - Sector.RIGHT -> PlayerInput(right = true) - Sector.BACK_RIGHT -> PlayerInput(backward = true, right = true) - Sector.BACKWARD -> PlayerInput(backward = true) - Sector.BACK_LEFT -> PlayerInput(backward = true, left = true) - Sector.LEFT -> PlayerInput(left = true) - Sector.FORWARD_LEFT -> PlayerInput(forward = true, left = true) - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt deleted file mode 100644 index 887bcfc..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/PathLookTargetProjector.kt +++ /dev/null @@ -1,271 +0,0 @@ -package org.cobalt.pathfinder.movement - -import net.minecraft.world.phys.Vec3 -import org.cobalt.pathfinder.calculate.PathNode - -object PathLookTargetProjector { - private const val SEARCH_BACKWARD_SEGMENTS = 1 - private const val SEARCH_FORWARD_SEGMENTS = 3 - private const val ZERO = 0.0 - private const val FULL_SEGMENT = 1.0 - - private data class ClosestSegment( - val index: Int, - val progress: Double, - ) - - private data class SegmentProjection( - val progress: Double, - val distanceSquared: Double, - ) - - @JvmStatic - fun getRotationTarget( - playerEyePosition: Vec3, - pathNodes: List, - currentPathIndex: Int, - lookAheadDistance: Double = 3.0, - ): Vec3 { - val closestSegment = findClosestSegment( - playerEyePosition, - pathNodes, - currentPathIndex - ) - - val horizontalTarget = moveAlongPath( - pathNodes, - closestSegment, - lookAheadDistance - ) - - return Vec3( - horizontalTarget.x, - playerEyePosition.y, - horizontalTarget.z - ) - } - - private fun findClosestSegment( - playerEyePosition: Vec3, - pathNodes: List, - currentPathIndex: Int, - ): ClosestSegment { - val playerPosition = Vec3( - playerEyePosition.x, - ZERO, - playerEyePosition.z - ) - - var closestSegment = ClosestSegment( - currentPathIndex, - ZERO - ) - - var closestDistanceSquared = Double.MAX_VALUE - - val startIndex = maxOf( - ZERO.toInt(), - currentPathIndex - SEARCH_BACKWARD_SEGMENTS - ) - - val endIndex = minOf( - pathNodes.lastIndex, - currentPathIndex + SEARCH_FORWARD_SEGMENTS - ) - - for (segmentIndex in startIndex until endIndex) { - val projection = findClosestPointOnSegment( - playerPosition, - pathNodes[segmentIndex].centerVec, - pathNodes[segmentIndex + 1].centerVec - ) - - if (projection == null) { - continue - } - - if (projection.distanceSquared < closestDistanceSquared) { - closestDistanceSquared = projection.distanceSquared - closestSegment = ClosestSegment( - segmentIndex, - projection.progress - ) - } - } - - return closestSegment - } - - private fun findClosestPointOnSegment( - point: Vec3, - start: Vec3, - end: Vec3, - ): SegmentProjection? { - val direction = Vec3( - end.x - start.x, - ZERO, - end.z - start.z - ) - - val lengthSquared = - direction.x * direction.x + - direction.z * direction.z - - if (lengthSquared == ZERO) { - return null - } - - val progress = - ( - (point.x - start.x) * direction.x + - (point.z - start.z) * direction.z - ) - .coerceIn(ZERO, lengthSquared) / lengthSquared - - val closestX = start.x + direction.x * progress - val closestZ = start.z + direction.z * progress - - val distanceX = point.x - closestX - val distanceZ = point.z - closestZ - - return SegmentProjection( - progress, - distanceX * distanceX + distanceZ * distanceZ - ) - } - - private fun moveAlongPath( - pathNodes: List, - startingSegment: ClosestSegment, - distance: Double, - ): Vec3 { - var remainingDistance = distance - - val currentSegmentResult = moveOnSegment( - pathNodes, - startingSegment.index, - startingSegment.progress, - remainingDistance - ) - - if (currentSegmentResult != null) { - return currentSegmentResult - } - - remainingDistance -= distanceRemainingOnSegment( - pathNodes, - startingSegment - ) - - for (segmentIndex in startingSegment.index + 1 until pathNodes.lastIndex) { - val result = moveOnSegment( - pathNodes, - segmentIndex, - ZERO, - remainingDistance - ) - - if (result != null) { - return result - } - - remainingDistance -= segmentLength( - pathNodes, - segmentIndex - ) - } - - return extrapolatePastEnd( - pathNodes, - remainingDistance - ) - } - - private fun moveOnSegment( - pathNodes: List, - segmentIndex: Int, - startProgress: Double, - distance: Double, - ): Vec3? { - val start = pathNodes[segmentIndex].centerVec - val end = pathNodes[segmentIndex + 1].centerVec - - val direction = Vec3( - end.x - start.x, - ZERO, - end.z - start.z - ) - - val length = direction.length() - - if (length == ZERO) { - return null - } - - val availableDistance = - (FULL_SEGMENT - startProgress) * length - - if (distance > availableDistance) { - return null - } - - val progress = - startProgress + distance / length - - return start.add( - direction.scale(progress) - ) - } - - private fun distanceRemainingOnSegment( - pathNodes: List, - segment: ClosestSegment, - ): Double { - return (FULL_SEGMENT - segment.progress) * - segmentLength(pathNodes, segment.index) - } - - private fun segmentLength( - pathNodes: List, - segmentIndex: Int, - ): Double { - val start = pathNodes[segmentIndex].centerVec - val end = pathNodes[segmentIndex + 1].centerVec - - return Vec3( - end.x - start.x, - ZERO, - end.z - start.z - ).length() - } - - private fun extrapolatePastEnd( - pathNodes: List, - distance: Double, - ): Vec3 { - val last = pathNodes.last() - - val previous = - if (pathNodes.size > 1) { - pathNodes[pathNodes.lastIndex - 1] - } else { - last - } - - val direction = Vec3( - (last.x - previous.x).toDouble(), - ZERO, - (last.z - previous.z).toDouble() - ) - - val length = direction.length() - - if (length == ZERO) { - return last.centerVec - } - - return last.centerVec.add( - direction.scale(distance / length) - ) - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt deleted file mode 100644 index cb78970..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockSupportRules.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.cobalt.pathfinder.movement.rules - -import net.minecraft.core.BlockPos -import net.minecraft.world.level.EmptyBlockGetter -import net.minecraft.world.level.block.* -import net.minecraft.world.level.block.piston.MovingPistonBlock -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.level.block.state.properties.SlabType -import net.minecraft.world.level.material.Fluids - -object BlockSupportRules { - @JvmStatic - fun isWater(state: BlockState): Boolean { - val fluid = state.fluidState.type - return fluid == Fluids.WATER || fluid == Fluids.FLOWING_WATER - } - - @JvmStatic - fun isLava(state: BlockState): Boolean { - val fluid = state.fluidState.type - return fluid == Fluids.LAVA || fluid == Fluids.FLOWING_LAVA - } - - @JvmStatic - fun isBottomSlab(state: BlockState): Boolean { - return state.block is SlabBlock && - state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM - } - - @JvmStatic - fun isBlockNormalCube(state: BlockState): Boolean { - val block = state.block - - if ( - block is BambooStalkBlock || - block is MovingPistonBlock || - block is ScaffoldingBlock - ) { - return false - } - - if ( - block is ShulkerBoxBlock || - block is PointedDripstoneBlock || - block is AmethystClusterBlock - ) { - return false - } - - return try { - Block.isShapeFullBlock(state.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) - } catch (_: Exception) { - false - } - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt deleted file mode 100644 index 75bbe2e..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/BlockTraversalRules.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.cobalt.pathfinder.movement.rules - -import org.cobalt.pathfinder.movement.CalculationContext -import org.cobalt.pathfinder.movement.rules.PassthroughRules.canPassThrough -import org.cobalt.pathfinder.movement.rules.StandOnRules.canStandOn - -object BlockTraversalRules { - @JvmStatic - fun canWalkOn(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { - if ( - !canWalkThrough(calculationContext, x, y + 1, z) - ) { - return false - } - - return canStandOn(calculationContext.blockStateAccessor, x, y, z) - } - - @JvmStatic - fun canWalkThrough(calculationContext: CalculationContext, x: Int, y: Int, z: Int): Boolean { - return canPassThrough(calculationContext.blockStateAccessor, x, y, z) && - canPassThrough(calculationContext.blockStateAccessor, x, y + 1, z) - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt deleted file mode 100644 index 07a24a4..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandOnRules.kt +++ /dev/null @@ -1,110 +0,0 @@ -package org.cobalt.pathfinder.movement.rules - -import net.minecraft.world.level.block.AzaleaBlock -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.Blocks -import net.minecraft.world.level.block.CarpetBlock -import net.minecraft.world.level.block.SlabBlock -import net.minecraft.world.level.block.StainedGlassBlock -import net.minecraft.world.level.block.StairBlock -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.level.material.Fluids -import org.cobalt.pathfinder.helper.BlockStateAccessor -import org.cobalt.pathfinder.movement.FlowingCheck -import org.cobalt.pathfinder.movement.rules.data.BlockSetRule -import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule -import org.cobalt.pathfinder.movement.rules.data.Ternary - -object StandOnRules { - - val STAND_ON_RULES = listOf( - BlockSetRule( - setOf( - Blocks.LADDER, - Blocks.VINE, - Blocks.FARMLAND, - Blocks.DIRT_PATH, - Blocks.SOUL_SAND, - Blocks.ENDER_CHEST, - Blocks.CHEST, - Blocks.TRAPPED_CHEST, - Blocks.GLASS, - ), - Ternary.YES, - ), - - BlockTypeRule( - setOf( - StainedGlassBlock::class.java, - StairBlock::class.java, - SlabBlock::class.java, - AzaleaBlock::class.java, - ), - Ternary.YES, - ), - ) - - private val PRECOMPUTED = Array(Block.BLOCK_STATE_REGISTRY.size()) { - Ternary.NO - } - - init { - Block.BLOCK_STATE_REGISTRY.forEach { state -> - PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] = compute(state) - } - } - - private fun compute(state: BlockState): Ternary { - val block = state.block - - if (BlockSupportRules.isBlockNormalCube(state) && - block != Blocks.BUBBLE_COLUMN && - block != Blocks.HONEY_BLOCK - ) { - return Ternary.YES - } - - STAND_ON_RULES.firstOrNull { it.matches(state) } - ?.let { return it.result } - - return when { - BlockSupportRules.isWater(state) -> Ternary.MAYBE - BlockSupportRules.isLava(state) -> Ternary.MAYBE - else -> Ternary.NO - } - } - - fun canStandOn(bsa: BlockStateAccessor, x: Int, y: Int, z: Int): Boolean { - val state = bsa.get(x, y, z) - val result = PRECOMPUTED[Block.BLOCK_STATE_REGISTRY.getId(state)] - - if (result == Ternary.YES) { - return true - } - - if (result == Ternary.NO) { - return false - } - - return resolveMaybe(bsa, x, y, z, state) - } - - private fun resolveMaybe(bsa: BlockStateAccessor, x: Int, y: Int, z: Int, state: BlockState): Boolean { - if (BlockSupportRules.isWater(state)) { - val upState = bsa.get(x, y + 1, z) - val upBlock = upState.block - - if (upBlock == Blocks.LILY_PAD || upBlock is CarpetBlock) { - return true - } - - if (FlowingCheck.isFlowing(x, y, z, state, bsa) || upState.fluidState.type == Fluids.FLOWING_WATER) { - return BlockSupportRules.isWater(upState) - } - - return BlockSupportRules.isWater(upState) xor false - } - - return false - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt deleted file mode 100644 index 04d4cd7..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/JumpDecision.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.cobalt.pathfinder.state.pathing - -import net.minecraft.core.BlockPos -import org.cobalt.Cobalt -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.rules.BlockSupportRules -import org.cobalt.util.PlayerUtils -import org.cobalt.dsl.centerVec -import org.cobalt.util.helper.Clock -import kotlin.math.abs -import kotlin.random.Random - -class JumpDecision { - private val jumpDelay = Clock() - - fun shouldJump(playerPos: BlockPos, nodes: List, currentIndex: Int): Boolean { - val level = Cobalt.minecraft.level - requireNotNull(level) - - if (!PlayerUtils.onGround || !jumpDelay.passed()) { - return false - } - - val node = nodes[currentIndex] - - if (node.block.y - playerPos.y < 1 || - BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) - ) { - return false - } - - val nodeCenter = node.centerVec - val playerVec = playerPos.centerVec() - val dx = abs(nodeCenter.x - playerVec.x) - val dz = abs(nodeCenter.z - playerVec.z) - - if (dx + dz > 1.2 || minOf(dx, dz) > 0.2) { - return false - } - - if (PlayerUtils.canFly) { - jumpDelay.schedule(Random.nextLong(350, 450)) - } - - return true - } -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt deleted file mode 100644 index d2304a2..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingAdvancer.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.cobalt.pathfinder.state.pathing - -import net.minecraft.core.BlockPos -import net.minecraft.world.phys.Vec3 -import org.cobalt.dsl.centerVec -import org.cobalt.Cobalt -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.PathFindingFacade -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.movement.rules.BlockSupportRules -import org.cobalt.util.PlayerUtils - -class PathingAdvancer { - fun advanceIfReached(playerPos: BlockPos, nodes: List): Boolean { - val currentIndex = PathFindingFacade.pathIndex - - if (!hasReached(playerPos, nodes, currentIndex)) { - return false - } - - while ( - PathFindingFacade.pathIndex + 1 < nodes.size && - hasReached(playerPos, nodes, PathFindingFacade.pathIndex) - ) { - PathFindingFacade.pathIndex++ - } - - if (PathFindingFacade.pathIndex + 1 >= nodes.size) { - PathFindingFacade.stop() - } - - return true - } - - private fun hasReached( - playerPos: BlockPos, - nodes: List, - currentIndex: Int, - ): Boolean { - val node = nodes[currentIndex] - - val nodeCenter = node.centerVec - val playerVec = playerPos.centerVec() - - if (playerVec.distanceToSqr(nodeCenter) < PathFindingConfig.hasReachedThreshold) return true - - if (currentIndex + 1 >= nodes.size || !canAdvancePastNode(playerPos, node)) { - return false - } - - if (!isCloseEnoughToSegment(nodeCenter, playerVec, nodes[currentIndex + 1].centerVec)) { - return false - } - - return true - } - - private fun canAdvancePastNode(playerPos: BlockPos, node: PathNode): Boolean { - val level = Cobalt.minecraft.level - requireNotNull(level) - - return BlockSupportRules.isBottomSlab(level.getBlockState(node.blockStandingOn)) || - (node.block.y <= playerPos.y && PlayerUtils.onGround) - } - - private fun isCloseEnoughToSegment( - nodeCenter: Vec3, - playerVec: Vec3, - nextNodeCenter: Vec3, - ): Boolean { - val segment = nextNodeCenter.subtract(nodeCenter) - val toPlayer = playerVec.subtract(nodeCenter) - - if (toPlayer.dot(segment) < 0.0) { - return false - } - - val squaredPerpendicularDistance = toPlayer.cross(segment).lengthSqr() / segment.lengthSqr() - return squaredPerpendicularDistance < 1.0 - } -} - - diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt deleted file mode 100644 index 9a33ec4..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingInputBuilder.kt +++ /dev/null @@ -1,96 +0,0 @@ -package org.cobalt.pathfinder.state.pathing - -import net.minecraft.core.BlockPos -import org.cobalt.pathfinder.PathFindingConfig -import org.cobalt.pathfinder.calculate.PathNode -import org.cobalt.pathfinder.helper.PlayerInput -import org.cobalt.pathfinder.movement.MovementInputMapper -import org.cobalt.util.PlayerUtils -import org.cobalt.util.RotationUtils - - -class PathingInputBuilder(private val jumpDecision: JumpDecision) { - fun build( - playerPos: BlockPos, - node: PathNode, - nodes: List, - sameXZ: Boolean, - currentIndex: Int, - ): PlayerInput { - return if (node.isFly && sameXZ) { - buildFlyVerticalInput(node, playerPos) - } else { - buildStandardInput(playerPos, node, nodes, sameXZ, currentIndex) - } - } - - private fun buildFlyVerticalInput( - node: PathNode, - playerPos: BlockPos, - ): PlayerInput { - val input = PlayerInput() - - val diffY = node.block.y - playerPos.y - when { - diffY > 0 -> input.jump = true - diffY < 0 -> input.sneak = true - } - - return input - } - - private fun buildStandardInput( - playerPos: BlockPos, - node: PathNode, - nodes: List, - sameXZ: Boolean, - currentIndex: Int, - ): PlayerInput { - val input = PlayerInput() - - applyHorizontalInput(input, node, sameXZ) - applySprintIfNeeded(input) - applyJumpIfNeeded(input, playerPos, nodes, currentIndex, node) - - return input - } - - private fun applyHorizontalInput( - input: PlayerInput, - node: PathNode, - sameXZ: Boolean, - ) { - if (sameXZ) { - return - } - - val neededKeys = MovementInputMapper.getNeededKeys( - PlayerUtils.rotation.yaw, - RotationUtils.getRotation(node.centerVec).yaw - ) - - input.apply(neededKeys) - } - - private fun applySprintIfNeeded(input: PlayerInput) { - if (PathFindingConfig.shouldSprint) { - input.sprint = true - } - } - - private fun applyJumpIfNeeded( - input: PlayerInput, - playerPos: BlockPos, - nodes: List, - currentIndex: Int, - node: PathNode, - ) { - if (node.isFly) { - return - } - - if (jumpDecision.shouldJump(playerPos, nodes, currentIndex)) { - input.jump = true - } - } -} From b46841e03076d82cd2023f5bf6cdee653bc3a356 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:22:13 -0400 Subject: [PATCH 24/28] chore: rearrange files in org.cobalt.util + cleanup --- config/detekt/detekt.yml | 6 +- .../cobalt/mixin/client/MinecraftMixin.java | 2 +- .../mixin/client/MouseHandlerMixin.java | 14 +-- .../java/org/cobalt/mixin/gui/FontMixin.java | 2 +- src/main/kotlin/org/cobalt/Cobalt.kt | 2 +- src/main/kotlin/org/cobalt/command/Command.kt | 7 +- .../org/cobalt/command/CommandManager.kt | 2 +- .../org/cobalt/command/impl/MainCommand.kt | 2 +- src/main/kotlin/org/cobalt/dsl/Block.kt | 20 ---- .../org/cobalt/event/impl/MouseEvent.kt | 4 +- .../kotlin/org/cobalt/module/ModuleManager.kt | 27 +++-- .../cobalt/module/impl/combat/AutoClicker.kt | 20 ++-- .../org/cobalt/module/impl/misc/AutoHarp.kt | 4 +- .../org/cobalt/module/impl/misc/Rotations.kt | 30 ++--- .../org/cobalt/module/impl/skills/Fishing.kt | 16 ++- .../module/impl/visual/PerformanceHUD.kt | 28 ++--- .../kotlin/org/cobalt/module/type/Script.kt | 2 +- .../org/cobalt/pathfinder/PathExecutor.kt | 6 +- .../org/cobalt/pathfinder/PathRenderer.kt | 20 ++-- .../calculate/path/AStarPathfinder.kt | 7 +- .../org/cobalt/pathfinder/helper/PathInput.kt | 14 +-- .../movement/fly/FlyAscendMovement.kt | 2 +- .../movement/fly/FlyDescendMovement.kt | 2 +- .../movement/fly/FlyDiagonalMovement.kt | 2 +- .../movement/fly/FlyTraverseMovement.kt | 2 +- .../movement/rules/PassthroughRules.kt | 19 +-- .../movement/rules/StandingRules.kt | 10 +- .../movement/walk/AscendMovement.kt | 2 +- .../movement/walk/DescendMovement.kt | 2 +- .../movement/walk/DiagonalMovement.kt | 2 +- .../movement/walk/TraverseMovement.kt | 2 +- .../state/calculation/CalculatingState.kt | 12 +- .../pathfinder/state/fly/StartFlyState.kt | 2 +- src/main/kotlin/org/cobalt/ui/UIScreen.kt | 2 +- .../cobalt/ui/component/ModuleComponent.kt | 16 +-- .../cobalt/ui/component/ScriptComponent.kt | 34 +++--- .../cobalt/ui/component/SidebarComponent.kt | 36 +++--- .../cobalt/ui/component/SwitchComponent.kt | 10 +- .../cobalt/ui/component/TextInputComponent.kt | 34 +++--- .../org/cobalt/ui/component/ThemeComponent.kt | 24 ++-- .../cobalt/ui/component/TopbarComponent.kt | 12 +- .../cobalt/ui/component/button/IconButton.kt | 20 ++-- .../ui/component/button/SidebarButton.kt | 24 ++-- .../cobalt/ui/component/setting/Setting.kt | 10 +- .../component/setting/impl/ButtonSetting.kt | 24 ++-- .../component/setting/impl/CheckboxSetting.kt | 14 +-- .../ui/component/setting/impl/InfoSetting.kt | 16 +-- .../component/setting/impl/KeyBindSetting.kt | 18 +-- .../ui/component/setting/impl/ModeSetting.kt | 20 ++-- .../ui/component/setting/impl/RangeSetting.kt | 30 ++--- .../component/setting/impl/SliderSetting.kt | 26 ++-- .../org/cobalt/ui/helper/DragHandler.kt | 16 +-- .../kotlin/org/cobalt/ui/helper/SnapHelper.kt | 2 +- .../org/cobalt/ui/helper/TextInputHelper.kt | 10 +- .../cobalt/ui/notification/Notification.kt | 32 ++--- .../ui/notification/NotificationManager.kt | 4 +- src/main/kotlin/org/cobalt/ui/page/Page.kt | 6 +- .../org/cobalt/ui/page/impl/ModulesPage.kt | 10 +- .../org/cobalt/ui/page/impl/ScriptsPage.kt | 14 +-- .../org/cobalt/ui/page/impl/ThemesPage.kt | 10 +- .../org/cobalt/ui/screen/ConfigScreen.kt | 24 ++-- .../org/cobalt/ui/screen/HudEditorScreen.kt | 36 +++--- .../org/cobalt/ui/theme/ThemeManager.kt | 4 +- src/main/kotlin/org/cobalt/util/ColorUtils.kt | 60 ---------- .../org/cobalt/util/{ => block}/BlockUtils.kt | 27 +++-- .../cobalt/util/{ => block}/FrustumUtils.kt | 2 +- .../util/{helper => chat}/ChatFormatter.kt | 5 +- .../org/cobalt/util/{ => chat}/ChatUtils.kt | 53 +++++++-- .../cobalt/util/{ => client}/PlayerUtils.kt | 4 +- .../cobalt/util/{ => client}/WindowUtils.kt | 2 +- .../color/ColorExtensions.kt} | 6 +- .../{BasicConfig.kt => SimpleConfig.kt} | 2 +- .../{KeybindUtils.kt => input/Keyboard.kt} | 4 +- .../util/{MouseUtils.kt => input/Mouse.kt} | 28 ++--- .../util/{ => inventory}/InventoryUtils.kt | 112 +++++------------- .../cobalt/util/{ => inventory}/ItemUtils.kt | 2 +- .../GizmoRenderer.kt} | 5 +- .../{skia/Skia.kt => render/SkiaRenderer.kt} | 22 +--- .../util/{ => render/skia}/ResourceUtils.kt | 2 +- .../util/{ => render}/skia/SkiaBootstrap.kt | 2 +- .../cobalt/util/{ => render}/skia/SkiaPIP.kt | 4 +- .../helper => render/skia/data}/SkiaCorner.kt | 2 +- .../helper => render/skia/data}/SkiaFont.kt | 4 +- .../helper => render/skia/data}/SkiaImage.kt | 4 +- .../{ => render}/skia/surface/GlSurface.kt | 8 +- .../{ => render}/skia/surface/SkiaSurface.kt | 2 +- .../skia/surface/VulkanSurface.kt | 8 +- .../RotationMath.kt} | 13 +- .../util/rotation/{ => data}/Rotation.kt | 12 +- .../util/rotation/{ => data}/RotationDelta.kt | 2 +- .../rotation/{ => data}/RotationTarget.kt | 9 +- .../util/{helper => scheduling}/Clock.kt | 7 +- .../{helper => scheduling}/Multithreading.kt | 2 +- .../{helper => scheduling}/TickScheduler.kt | 4 +- .../ConnectionTracker.kt} | 4 +- .../Scoreboard.kt} | 4 +- .../{TablistUtils.kt => server/Tablist.kt} | 4 +- src/main/resources/fabric.mod.json | 2 +- 98 files changed, 578 insertions(+), 687 deletions(-) delete mode 100644 src/main/kotlin/org/cobalt/dsl/Block.kt delete mode 100644 src/main/kotlin/org/cobalt/util/ColorUtils.kt rename src/main/kotlin/org/cobalt/util/{ => block}/BlockUtils.kt (84%) rename src/main/kotlin/org/cobalt/util/{ => block}/FrustumUtils.kt (95%) rename src/main/kotlin/org/cobalt/util/{helper => chat}/ChatFormatter.kt (98%) rename src/main/kotlin/org/cobalt/util/{ => chat}/ChatUtils.kt (64%) rename src/main/kotlin/org/cobalt/util/{ => client}/PlayerUtils.kt (97%) rename src/main/kotlin/org/cobalt/util/{ => client}/WindowUtils.kt (92%) rename src/main/kotlin/org/cobalt/{dsl/Color.kt => util/color/ColorExtensions.kt} (77%) rename src/main/kotlin/org/cobalt/util/config/{BasicConfig.kt => SimpleConfig.kt} (87%) rename src/main/kotlin/org/cobalt/util/{KeybindUtils.kt => input/Keyboard.kt} (97%) rename src/main/kotlin/org/cobalt/util/{MouseUtils.kt => input/Mouse.kt} (96%) rename src/main/kotlin/org/cobalt/util/{ => inventory}/InventoryUtils.kt (56%) rename src/main/kotlin/org/cobalt/util/{ => inventory}/ItemUtils.kt (89%) rename src/main/kotlin/org/cobalt/util/{WorldRenderUtils.kt => render/GizmoRenderer.kt} (97%) rename src/main/kotlin/org/cobalt/util/{skia/Skia.kt => render/SkiaRenderer.kt} (95%) rename src/main/kotlin/org/cobalt/util/{ => render/skia}/ResourceUtils.kt (96%) rename src/main/kotlin/org/cobalt/util/{ => render}/skia/SkiaBootstrap.kt (98%) rename src/main/kotlin/org/cobalt/util/{ => render}/skia/SkiaPIP.kt (96%) rename src/main/kotlin/org/cobalt/util/{skia/helper => render/skia/data}/SkiaCorner.kt (92%) rename src/main/kotlin/org/cobalt/util/{skia/helper => render/skia/data}/SkiaFont.kt (57%) rename src/main/kotlin/org/cobalt/util/{skia/helper => render/skia/data}/SkiaImage.kt (67%) rename src/main/kotlin/org/cobalt/util/{ => render}/skia/surface/GlSurface.kt (95%) rename src/main/kotlin/org/cobalt/util/{ => render}/skia/surface/SkiaSurface.kt (93%) rename src/main/kotlin/org/cobalt/util/{ => render}/skia/surface/VulkanSurface.kt (95%) rename src/main/kotlin/org/cobalt/util/{RotationUtils.kt => rotation/RotationMath.kt} (83%) rename src/main/kotlin/org/cobalt/util/rotation/{ => data}/Rotation.kt (73%) rename src/main/kotlin/org/cobalt/util/rotation/{ => data}/RotationDelta.kt (66%) rename src/main/kotlin/org/cobalt/util/rotation/{ => data}/RotationTarget.kt (82%) rename src/main/kotlin/org/cobalt/util/{helper => scheduling}/Clock.kt (68%) rename src/main/kotlin/org/cobalt/util/{helper => scheduling}/Multithreading.kt (92%) rename src/main/kotlin/org/cobalt/util/{helper => scheduling}/TickScheduler.kt (92%) rename src/main/kotlin/org/cobalt/util/{ServerUtils.kt => server/ConnectionTracker.kt} (96%) rename src/main/kotlin/org/cobalt/util/{ScoreboardUtils.kt => server/Scoreboard.kt} (96%) rename src/main/kotlin/org/cobalt/util/{TablistUtils.kt => server/Tablist.kt} (92%) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 037b60d..0232507 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -54,7 +54,11 @@ style: active: true maxJumpCount: 3 WildcardImport: - active: false + active: true + excludeImports: + - 'org.lwjgl.vulkan.*' + - 'io.github.humbleui.skija.*' + - 'net.minecraft.world.level.block.*' ForbiddenComment: active: false UnusedPrivateProperty: diff --git a/src/main/java/org/cobalt/mixin/client/MinecraftMixin.java b/src/main/java/org/cobalt/mixin/client/MinecraftMixin.java index 81a44a8..4b379ad 100644 --- a/src/main/java/org/cobalt/mixin/client/MinecraftMixin.java +++ b/src/main/java/org/cobalt/mixin/client/MinecraftMixin.java @@ -11,7 +11,7 @@ import org.cobalt.event.impl.TickEvent; import org.cobalt.module.ModuleManager; import org.cobalt.util.config.SettingContainer; -import org.cobalt.util.helper.Multithreading; +import org.cobalt.util.scheduling.Multithreading; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/org/cobalt/mixin/client/MouseHandlerMixin.java b/src/main/java/org/cobalt/mixin/client/MouseHandlerMixin.java index a91fe3b..a293cb3 100644 --- a/src/main/java/org/cobalt/mixin/client/MouseHandlerMixin.java +++ b/src/main/java/org/cobalt/mixin/client/MouseHandlerMixin.java @@ -5,10 +5,10 @@ import org.cobalt.event.EventBus; import org.cobalt.event.impl.MouseEvent; import org.cobalt.event.impl.MouseScrollEvent; -import org.cobalt.util.MouseAction; -import org.cobalt.util.MouseButton; -import org.cobalt.util.MouseMode; -import org.cobalt.util.MouseUtils; +import org.cobalt.util.input.Mouse; +import org.cobalt.util.input.MouseAction; +import org.cobalt.util.input.MouseButton; +import org.cobalt.util.input.MouseMode; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -74,14 +74,14 @@ private void onMouseScroll(long handle, double xoffset, double yoffset, Callback @Inject(method = "turnPlayer", at = @At("HEAD"), cancellable = true) private void onUpdateMouse(CallbackInfo callbackInfo) { - if (MouseUtils.getMouseMode() == MouseMode.LOCK_MOUSE) { + if (Mouse.getMouseMode() == MouseMode.LOCK_MOUSE) { callbackInfo.cancel(); } } @Inject(method = "isMouseGrabbed", at = @At("HEAD"), cancellable = true) private void onIsCursorLocked(CallbackInfoReturnable callbackInfoReturnable) { - if (MouseUtils.getMouseMode() == MouseMode.UNGRAB_MOUSE) { + if (Mouse.getMouseMode() == MouseMode.UNGRAB_MOUSE) { if (this.mouseGrabbed) { this.releaseMouse(); } @@ -92,7 +92,7 @@ private void onIsCursorLocked(CallbackInfoReturnable callbackInfoReturn @Inject(method = "grabMouse", at = @At("HEAD"), cancellable = true) private void onLockCursor(CallbackInfo callbackInfo) { - if (MouseUtils.getMouseMode() == MouseMode.UNGRAB_MOUSE) { + if (Mouse.getMouseMode() == MouseMode.UNGRAB_MOUSE) { callbackInfo.cancel(); } } diff --git a/src/main/java/org/cobalt/mixin/gui/FontMixin.java b/src/main/java/org/cobalt/mixin/gui/FontMixin.java index 141c3cf..99316ac 100644 --- a/src/main/java/org/cobalt/mixin/gui/FontMixin.java +++ b/src/main/java/org/cobalt/mixin/gui/FontMixin.java @@ -8,7 +8,7 @@ import net.minecraft.network.chat.Style; import net.minecraft.util.FormattedCharSequence; import org.cobalt.module.impl.misc.NickHider; -import org.cobalt.util.PlayerUtils; +import org.cobalt.util.client.PlayerUtils; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/kotlin/org/cobalt/Cobalt.kt b/src/main/kotlin/org/cobalt/Cobalt.kt index b8865f2..91db2af 100644 --- a/src/main/kotlin/org/cobalt/Cobalt.kt +++ b/src/main/kotlin/org/cobalt/Cobalt.kt @@ -15,7 +15,7 @@ import org.cobalt.event.EventBus import org.cobalt.event.impl.WorldEvent import org.cobalt.module.ModuleManager import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.skia.SkiaPIP +import org.cobalt.util.render.skia.SkiaPIP import org.slf4j.LoggerFactory object Cobalt : ClientModInitializer { diff --git a/src/main/kotlin/org/cobalt/command/Command.kt b/src/main/kotlin/org/cobalt/command/Command.kt index c1451fd..765f538 100644 --- a/src/main/kotlin/org/cobalt/command/Command.kt +++ b/src/main/kotlin/org/cobalt/command/Command.kt @@ -1,6 +1,11 @@ package org.cobalt.command -import com.mojang.brigadier.arguments.* +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.arguments.BoolArgumentType +import com.mojang.brigadier.arguments.DoubleArgumentType +import com.mojang.brigadier.arguments.FloatArgumentType +import com.mojang.brigadier.arguments.IntegerArgumentType +import com.mojang.brigadier.arguments.StringArgumentType import com.mojang.brigadier.builder.LiteralArgumentBuilder import com.mojang.brigadier.builder.RequiredArgumentBuilder import com.mojang.brigadier.context.CommandContext diff --git a/src/main/kotlin/org/cobalt/command/CommandManager.kt b/src/main/kotlin/org/cobalt/command/CommandManager.kt index d72607e..f6dc80a 100644 --- a/src/main/kotlin/org/cobalt/command/CommandManager.kt +++ b/src/main/kotlin/org/cobalt/command/CommandManager.kt @@ -5,7 +5,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException import net.minecraft.client.multiplayer.ClientSuggestionProvider import org.cobalt.Cobalt.minecraft import org.cobalt.command.impl.MainCommand -import org.cobalt.util.ChatUtils +import org.cobalt.util.chat.ChatUtils object CommandManager { diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index a7ceb42..570c428 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -10,7 +10,7 @@ import org.cobalt.pathfinder.calculate.PathMode import org.cobalt.pathfinder.goal.GoalBlock import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen -import org.cobalt.util.helper.TickScheduler +import org.cobalt.util.scheduling.TickScheduler object MainCommand : Command(name = "cobalt", aliases = listOf("cb")) { diff --git a/src/main/kotlin/org/cobalt/dsl/Block.kt b/src/main/kotlin/org/cobalt/dsl/Block.kt deleted file mode 100644 index 152afad..0000000 --- a/src/main/kotlin/org/cobalt/dsl/Block.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.cobalt.dsl - -import net.minecraft.core.BlockPos -import net.minecraft.world.phys.AABB -import net.minecraft.world.phys.Vec3 - -fun BlockPos.centerVec(): Vec3 { - return Vec3(x + 0.5, y + 0.5, z + 0.5) -} - -fun Vec3.smallBox(): AABB { - return AABB( - x - 0.25, - y - 0.25, - z - 0.25, - x + 0.25, - y + 0.25, - z + 0.25 - ) -} diff --git a/src/main/kotlin/org/cobalt/event/impl/MouseEvent.kt b/src/main/kotlin/org/cobalt/event/impl/MouseEvent.kt index 34069b3..30505fe 100644 --- a/src/main/kotlin/org/cobalt/event/impl/MouseEvent.kt +++ b/src/main/kotlin/org/cobalt/event/impl/MouseEvent.kt @@ -1,8 +1,8 @@ package org.cobalt.event.impl import org.cobalt.event.Event -import org.cobalt.util.MouseAction -import org.cobalt.util.MouseButton +import org.cobalt.util.input.MouseAction +import org.cobalt.util.input.MouseButton class MouseEvent( val button: MouseButton, diff --git a/src/main/kotlin/org/cobalt/module/ModuleManager.kt b/src/main/kotlin/org/cobalt/module/ModuleManager.kt index 157619c..ac287b1 100644 --- a/src/main/kotlin/org/cobalt/module/ModuleManager.kt +++ b/src/main/kotlin/org/cobalt/module/ModuleManager.kt @@ -7,17 +7,22 @@ import org.cobalt.event.EventBus import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.RenderEvent import org.cobalt.module.impl.combat.AutoClicker -import org.cobalt.module.impl.misc.* +import org.cobalt.module.impl.misc.AutoHarp +import org.cobalt.module.impl.misc.AutoSprint +import org.cobalt.module.impl.misc.Debug +import org.cobalt.module.impl.misc.DiscordRPC +import org.cobalt.module.impl.misc.NickHider +import org.cobalt.module.impl.misc.Rotations import org.cobalt.module.impl.skills.Fishing import org.cobalt.module.impl.visual.PerformanceHUD import org.cobalt.module.type.RenderableModule import org.cobalt.module.type.Script import org.cobalt.ui.screen.HudEditorScreen -import org.cobalt.util.ChatUtils -import org.cobalt.util.WindowUtils.scaleX -import org.cobalt.util.WindowUtils.scaleY -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.SkiaPIP +import org.cobalt.util.chat.ChatUtils +import org.cobalt.util.client.WindowUtils.scaleX +import org.cobalt.util.client.WindowUtils.scaleY +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.SkiaPIP object ModuleManager { @@ -132,19 +137,19 @@ object ModuleManager { modules.filterIsInstance() .filter { module -> module.enabled } .forEach { module -> - Skia.push() + SkiaRenderer.push() val renderX = module.xPos * scaleX val renderY = module.yPos * scaleY val finalScale = module.scale * scaleY - Skia.translate(renderX, renderY) - Skia.scale(finalScale, finalScale) - Skia.translate(-module.xPos, -module.yPos) + SkiaRenderer.translate(renderX, renderY) + SkiaRenderer.scale(finalScale, finalScale) + SkiaRenderer.translate(-module.xPos, -module.yPos) module.renderComponent() - Skia.pop() + SkiaRenderer.pop() } } } diff --git a/src/main/kotlin/org/cobalt/module/impl/combat/AutoClicker.kt b/src/main/kotlin/org/cobalt/module/impl/combat/AutoClicker.kt index bf73e27..2cf583a 100644 --- a/src/main/kotlin/org/cobalt/module/impl/combat/AutoClicker.kt +++ b/src/main/kotlin/org/cobalt/module/impl/combat/AutoClicker.kt @@ -7,10 +7,14 @@ import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.WorldEvent import org.cobalt.module.Module import org.cobalt.module.ModuleCategory -import org.cobalt.ui.component.setting.impl.* -import org.cobalt.util.KeybindUtils -import org.cobalt.util.MouseUtils -import org.cobalt.util.helper.Clock +import org.cobalt.ui.component.setting.impl.CheckboxSetting +import org.cobalt.ui.component.setting.impl.KeyBindSetting +import org.cobalt.ui.component.setting.impl.ModeSetting +import org.cobalt.ui.component.setting.impl.SliderSetting +import org.cobalt.ui.component.setting.impl.TextSetting +import org.cobalt.util.input.Keyboard +import org.cobalt.util.input.Mouse +import org.cobalt.util.scheduling.Clock object AutoClicker : Module( name = "AutoClicker", @@ -80,18 +84,18 @@ object AutoClicker : Module( if ( leftClickDelay.passed() && - KeybindUtils.isKeyDown(leftClickKeybind) && + Keyboard.isKeyDown(leftClickKeybind) && canLeftClick() ) { - MouseUtils.leftClick() + Mouse.leftClick() leftClickDelay.schedule(nextDelay(leftClickCps)) } if ( rightClickDelay.passed() && - KeybindUtils.isKeyDown(rightClickKeybind) + Keyboard.isKeyDown(rightClickKeybind) ) { - KeybindUtils.press(minecraft.options.keyUse) + Keyboard.press(minecraft.options.keyUse) rightClickDelay.schedule(nextDelay(rightClickCps)) } } diff --git a/src/main/kotlin/org/cobalt/module/impl/misc/AutoHarp.kt b/src/main/kotlin/org/cobalt/module/impl/misc/AutoHarp.kt index da2a2a0..834f99b 100644 --- a/src/main/kotlin/org/cobalt/module/impl/misc/AutoHarp.kt +++ b/src/main/kotlin/org/cobalt/module/impl/misc/AutoHarp.kt @@ -8,8 +8,8 @@ import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.TickEvent import org.cobalt.module.Module import org.cobalt.module.ModuleCategory -import org.cobalt.util.InventoryUtils -import org.cobalt.util.MouseButton +import org.cobalt.util.input.MouseButton +import org.cobalt.util.inventory.InventoryUtils object AutoHarp : Module( name = "AutoHarp", diff --git a/src/main/kotlin/org/cobalt/module/impl/misc/Rotations.kt b/src/main/kotlin/org/cobalt/module/impl/misc/Rotations.kt index e1ae809..da85d62 100644 --- a/src/main/kotlin/org/cobalt/module/impl/misc/Rotations.kt +++ b/src/main/kotlin/org/cobalt/module/impl/misc/Rotations.kt @@ -10,12 +10,12 @@ import org.cobalt.event.impl.WorldEvent import org.cobalt.module.Module import org.cobalt.module.ModuleCategory import org.cobalt.ui.component.setting.impl.SliderSetting -import org.cobalt.util.MouseMode -import org.cobalt.util.MouseUtils -import org.cobalt.util.PlayerUtils -import org.cobalt.util.RotationUtils -import org.cobalt.util.rotation.Rotation -import org.cobalt.util.rotation.RotationTarget +import org.cobalt.util.client.PlayerUtils +import org.cobalt.util.input.Mouse +import org.cobalt.util.input.MouseMode +import org.cobalt.util.rotation.RotationMath +import org.cobalt.util.rotation.data.Rotation +import org.cobalt.util.rotation.data.RotationTarget object Rotations : Module( name = "Rotations", @@ -91,8 +91,8 @@ object Rotations : Module( lastFrameMs = System.currentTimeMillis() running = true - if (MouseUtils.mouseMode == MouseMode.DEFAULT) { - MouseUtils.mouseMode = MouseMode.LOCK_MOUSE + if (Mouse.mouseMode == MouseMode.DEFAULT) { + Mouse.mouseMode = MouseMode.LOCK_MOUSE returnMouseMode = true } } @@ -102,8 +102,8 @@ object Rotations : Module( target = rotationTarget running = true - if (MouseUtils.mouseMode == MouseMode.DEFAULT) { - MouseUtils.mouseMode = MouseMode.LOCK_MOUSE + if (Mouse.mouseMode == MouseMode.DEFAULT) { + Mouse.mouseMode = MouseMode.LOCK_MOUSE returnMouseMode = true } } @@ -113,7 +113,7 @@ object Rotations : Module( target = null if (returnMouseMode) { - MouseUtils.mouseMode = MouseMode.DEFAULT + Mouse.mouseMode = MouseMode.DEFAULT returnMouseMode = false } } @@ -140,7 +140,7 @@ object Rotations : Module( } private fun handleRotate(current: Rotation, target: Rotation, deltaTime: Float) { - if (RotationUtils.approximatelyEquals(current, target, endTolerance.toFloat())) { + if (RotationMath.approximatelyEquals(current, target, endTolerance.toFloat())) { stop() return } @@ -157,12 +157,12 @@ object Rotations : Module( } private fun handleTrack(current: Rotation, target: Rotation, deltaTime: Float) { - if (RotationUtils.approximatelyEquals(current, target, endTolerance.toFloat())) { + if (RotationMath.approximatelyEquals(current, target, endTolerance.toFloat())) { return } - var needYaw = RotationUtils.angleDifference(target.yaw, current.yaw) - var needPitch = RotationUtils.angleDifference(target.pitch, current.pitch) + var needYaw = RotationMath.angleDifference(target.yaw, current.yaw) + var needPitch = RotationMath.angleDifference(target.pitch, current.pitch) val distance = abs(needYaw) + abs(needPitch) val randomFactor = (0.8f + Random.nextFloat() * 0.4f) diff --git a/src/main/kotlin/org/cobalt/module/impl/skills/Fishing.kt b/src/main/kotlin/org/cobalt/module/impl/skills/Fishing.kt index 004b67c..202da34 100644 --- a/src/main/kotlin/org/cobalt/module/impl/skills/Fishing.kt +++ b/src/main/kotlin/org/cobalt/module/impl/skills/Fishing.kt @@ -13,8 +13,12 @@ import org.cobalt.module.impl.misc.Rotations import org.cobalt.module.type.Script import org.cobalt.ui.component.setting.impl.TextSetting import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.* -import org.cobalt.util.helper.Clock +import org.cobalt.util.chat.ChatUtils +import org.cobalt.util.input.Mouse +import org.cobalt.util.inventory.InventoryUtils +import org.cobalt.util.render.GizmoRenderer +import org.cobalt.util.rotation.RotationMath +import org.cobalt.util.scheduling.Clock object Fishing : Script( name = "Fishing", @@ -85,7 +89,7 @@ object Fishing : Script( State.CAST_ROD -> { if (minecraft.player?.fishing == null) { - MouseUtils.rightClick() + Mouse.rightClick() } state = State.WAIT_FOR_CATCH @@ -98,7 +102,7 @@ object Fishing : Script( Random.nextDouble(-0.25, 0.25), Random.nextDouble(-0.25, 0.25) )?.let { - Rotations.start(RotationUtils.getRotation(it)) + Rotations.start(RotationMath.getRotation(it)) } antiAfkDelay.schedule(Random.nextLong(10_000, 15_000)) @@ -113,7 +117,7 @@ object Fishing : Script( } State.REEL_IN -> { - MouseUtils.rightClick() + Mouse.rightClick() delayClock.schedule(Random.nextLong(100, 250)) state = State.CAST_ROD } @@ -127,7 +131,7 @@ object Fishing : Script( } lookPos?.let { - WorldRenderUtils.drawBox( + GizmoRenderer.drawBox( AABB( it.x - 0.25, it.y - 0.25, diff --git a/src/main/kotlin/org/cobalt/module/impl/visual/PerformanceHUD.kt b/src/main/kotlin/org/cobalt/module/impl/visual/PerformanceHUD.kt index 345488d..375bc66 100644 --- a/src/main/kotlin/org/cobalt/module/impl/visual/PerformanceHUD.kt +++ b/src/main/kotlin/org/cobalt/module/impl/visual/PerformanceHUD.kt @@ -3,8 +3,8 @@ package org.cobalt.module.impl.visual import kotlin.math.roundToInt import org.cobalt.module.ModuleCategory import org.cobalt.module.type.RenderableModule -import org.cobalt.util.ServerUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.server.ConnectionTracker object PerformanceHUD : RenderableModule( name = "PerformanceHUD", @@ -20,8 +20,8 @@ object PerformanceHUD : RenderableModule( width += PADDING + 2 * TEXT_SPACING } - width += Skia.textWidth(Skia.boldFont, stat.value, FONT_SIZE) + TEXT_SPACING - width += Skia.textWidth(Skia.boldFont, stat.unit, FONT_SIZE) + width += SkiaRenderer.textWidth(SkiaRenderer.boldFont, stat.value, FONT_SIZE) + TEXT_SPACING + width += SkiaRenderer.textWidth(SkiaRenderer.boldFont, stat.unit, FONT_SIZE) } return width @@ -31,7 +31,7 @@ object PerformanceHUD : RenderableModule( get() = 50f override fun renderComponent() { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, theme.backgroundPrimary ) @@ -45,7 +45,7 @@ object PerformanceHUD : RenderableModule( val dividerX = currentX + DIVIDER_GAP val midY = yPos + height / 2 - Skia.line( + SkiaRenderer.line( dividerX, midY - DIVIDER_HALF_HEIGHT, dividerX, midY + DIVIDER_HALF_HEIGHT, 2f, theme.border @@ -56,30 +56,30 @@ object PerformanceHUD : RenderableModule( var textX = currentX - Skia.text( - Skia.boldFont, + SkiaRenderer.text( + SkiaRenderer.boldFont, stat.value, textX, textY, FONT_SIZE, theme.textPrimary ) - textX += Skia.textWidth(Skia.boldFont, stat.value, FONT_SIZE) + TEXT_SPACING + textX += SkiaRenderer.textWidth(SkiaRenderer.boldFont, stat.value, FONT_SIZE) + TEXT_SPACING - Skia.text( - Skia.boldFont, + SkiaRenderer.text( + SkiaRenderer.boldFont, stat.unit, textX, textY, FONT_SIZE, theme.textDisabled ) - currentX = textX + Skia.textWidth(Skia.boldFont, stat.unit, FONT_SIZE) + currentX = textX + SkiaRenderer.textWidth(SkiaRenderer.boldFont, stat.unit, FONT_SIZE) } } private fun getStats() = listOf( Stat(minecraft.fps.toString(), "FPS"), - Stat(ServerUtils.averageTps.roundToInt().toString(), "TPS"), - Stat(ServerUtils.averagePing.toString(), "MS"), + Stat(ConnectionTracker.averageTps.roundToInt().toString(), "TPS"), + Stat(ConnectionTracker.averagePing.toString(), "MS"), ) private const val PADDING = 25f diff --git a/src/main/kotlin/org/cobalt/module/type/Script.kt b/src/main/kotlin/org/cobalt/module/type/Script.kt index d1540f4..9451e55 100644 --- a/src/main/kotlin/org/cobalt/module/type/Script.kt +++ b/src/main/kotlin/org/cobalt/module/type/Script.kt @@ -2,7 +2,7 @@ package org.cobalt.module.type import org.cobalt.module.Module import org.cobalt.module.ModuleCategory -import org.cobalt.util.ChatUtils +import org.cobalt.util.chat.ChatUtils abstract class Script( name: String, diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt index 1656a99..4fbb03f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt @@ -10,9 +10,9 @@ import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.helper.PathInput import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.calculation.CalculatingState -import org.cobalt.util.ChatUtils -import org.cobalt.util.MessageType -import org.cobalt.util.PlayerUtils +import org.cobalt.util.chat.ChatUtils +import org.cobalt.util.chat.MessageType +import org.cobalt.util.client.PlayerUtils object PathExecutor { diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt index 82d5005..24a3b15 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt @@ -1,14 +1,14 @@ package org.cobalt.pathfinder -import org.cobalt.dsl.centerVec -import org.cobalt.dsl.smallBox -import org.cobalt.module.impl.misc.Debug -import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.PlayerUtils -import org.cobalt.util.WorldRenderUtils import java.awt.Color +import org.cobalt.module.impl.misc.Debug import org.cobalt.pathfinder.calculate.Path import org.cobalt.ui.theme.Theme +import org.cobalt.ui.theme.ThemeManager +import org.cobalt.util.block.centerVec +import org.cobalt.util.block.smallBox +import org.cobalt.util.client.PlayerUtils +import org.cobalt.util.render.GizmoRenderer object PathRenderer { @@ -25,14 +25,14 @@ object PathRenderer { val playerPos = PlayerUtils.position.centerVec() if (Debug.enabled) { - WorldRenderUtils.drawBox(playerPos.smallBox(), Color.GREEN) - WorldRenderUtils.drawBox(targetNode.smallBox(), Color.RED) + GizmoRenderer.drawBox(playerPos.smallBox(), Color.GREEN) + GizmoRenderer.drawBox(targetNode.smallBox(), Color.RED) } for (index in keyNodes.indices) { val node = keyNodes[index] - WorldRenderUtils.drawBlockPos( + GizmoRenderer.drawBlockPos( if (node.useMovementFly) node.block else node.blockStandingOn, color = theme.accentPrimary ) @@ -41,7 +41,7 @@ object PathRenderer { val prev = keyNodes[index - 1] - WorldRenderUtils.drawLine( + GizmoRenderer.drawLine( if (prev.useMovementFly) prev.centerVec else prev.topCenterVec, if (node.useMovementFly) node.centerVec else node.topCenterVec, theme.accentSecondary diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index daaf817..ea9f023 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -1,7 +1,6 @@ package org.cobalt.pathfinder.calculate.path import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap -import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.nanoseconds import org.cobalt.pathfinder.calculate.Path import org.cobalt.pathfinder.calculate.PathMode @@ -11,8 +10,8 @@ import org.cobalt.pathfinder.goal.Goal import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement import org.cobalt.pathfinder.movement.MovementResult -import org.cobalt.util.ChatUtils -import org.cobalt.util.MessageType +import org.cobalt.util.chat.ChatUtils +import org.cobalt.util.chat.MessageType class AStarPathfinder( val startX: Int, @@ -21,7 +20,7 @@ class AStarPathfinder( val goal: Goal, val mode: PathMode, val returnBestNode: Boolean = false, - val maxCalculationTime: Long = 10_000_000L + val maxCalculationTime: Long = 10_000_000L, ) { private val closedSet = Long2ObjectOpenHashMap() diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt index 70d7886..2b0f2b8 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt @@ -30,13 +30,13 @@ class PathInput : ClientInput() { override fun tick() { this.keyPresses = Input( - forward, - backward, - left, - right, - jump, - shift, - sprint + forward, + backward, + left, + right, + jump, + shift, + sprint ) val forwardImpulse = calculateImpulse(forward, backward) diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt index 5514c36..d631853 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyAscendMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator class FlyAscendMovement : Movement(MovementType.FLY) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt index 46a94b0..0158e34 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDescendMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator class FlyDescendMovement : Movement(MovementType.FLY) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt index 75d35fd..58e63e3 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyDiagonalMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator class FlyDiagonalMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt index 86d319f..35d5145 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/fly/FlyTraverseMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.fly import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator class FlyTraverseMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt index 654b82f..30ada3a 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/PassthroughRules.kt @@ -1,21 +1,6 @@ package org.cobalt.pathfinder.movement.rules -import net.minecraft.world.level.block.AbstractSkullBlock -import net.minecraft.world.level.block.AirBlock -import net.minecraft.world.level.block.AmethystClusterBlock -import net.minecraft.world.level.block.AzaleaBlock -import net.minecraft.world.level.block.BaseFireBlock -import net.minecraft.world.level.block.Blocks -import net.minecraft.world.level.block.CarpetBlock -import net.minecraft.world.level.block.CauldronBlock -import net.minecraft.world.level.block.DoorBlock -import net.minecraft.world.level.block.FenceGateBlock -import net.minecraft.world.level.block.ShulkerBoxBlock -import net.minecraft.world.level.block.SlabBlock -import net.minecraft.world.level.block.SnowLayerBlock -import net.minecraft.world.level.block.TrapDoorBlock -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.LilyPadBlock +import net.minecraft.world.level.block.* import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.properties.BlockStateProperties import net.minecraft.world.level.material.WaterFluid @@ -25,7 +10,7 @@ import org.cobalt.pathfinder.movement.rules.StandingRules.canStandOn import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary -import org.cobalt.util.BlockUtils +import org.cobalt.util.block.BlockUtils object PassthroughRules { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt index a7d71e5..17d0b94 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/rules/StandingRules.kt @@ -1,19 +1,13 @@ package org.cobalt.pathfinder.movement.rules -import net.minecraft.world.level.block.AzaleaBlock -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.Blocks -import net.minecraft.world.level.block.CarpetBlock -import net.minecraft.world.level.block.SlabBlock -import net.minecraft.world.level.block.StainedGlassBlock -import net.minecraft.world.level.block.StairBlock +import net.minecraft.world.level.block.* import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.material.Fluids import org.cobalt.pathfinder.helper.BlockStateAccessor import org.cobalt.pathfinder.movement.rules.data.BlockSetRule import org.cobalt.pathfinder.movement.rules.data.BlockTypeRule import org.cobalt.pathfinder.movement.rules.data.Ternary -import org.cobalt.util.BlockUtils +import org.cobalt.util.block.BlockUtils object StandingRules { diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt index 0a4ecb0..f156c65 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator // TODO: Handle ladder & vine climbing & jump boost class AscendMovement( diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt index 2466173..cf33606 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DescendMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator // TODO: Handle ladder & vine climbing downwards & falling down multiple blocks class DescendMovement( diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt index 6e7b5a3..182830e 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/DiagonalMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator class DiagonalMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt index e824ee6..3aef99b 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/TraverseMovement.kt @@ -3,9 +3,9 @@ package org.cobalt.pathfinder.movement.walk import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.movement.CalculationContext import org.cobalt.pathfinder.movement.Movement -import org.cobalt.pathfinder.movement.MovementValidator import org.cobalt.pathfinder.movement.MovementResult import org.cobalt.pathfinder.movement.MovementType +import org.cobalt.pathfinder.movement.MovementValidator class TraverseMovement( val dx: Int, diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt index 948d186..9f4ada2 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/calculation/CalculatingState.kt @@ -9,8 +9,8 @@ import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.calculate.path.AStarPathfinder import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState -import org.cobalt.util.ChatUtils -import org.cobalt.util.PlayerUtils +import org.cobalt.util.chat.ChatUtils +import org.cobalt.util.client.PlayerUtils class CalculatingState : ExecutorState() { @@ -28,10 +28,10 @@ class CalculatingState : ExecutorState() { calculationJob = CoroutineScope(Dispatchers.Default).launch { val path = AStarPathfinder( - startPos.x, startPos.y, startPos.z, - goal, config.mode, - config.returnBestNode, - config.maxCalculationTime + startPos.x, startPos.y, startPos.z, + goal, config.mode, + config.returnBestNode, + config.maxCalculationTime ).findPath() if (!isActive) { diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt index 354a689..2c39546 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt @@ -3,7 +3,7 @@ package org.cobalt.pathfinder.state.fly import org.cobalt.pathfinder.PathExecutor import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.pathing.PathingState -import org.cobalt.util.PlayerUtils +import org.cobalt.util.client.PlayerUtils class StartFlyState : ExecutorState() { diff --git a/src/main/kotlin/org/cobalt/ui/UIScreen.kt b/src/main/kotlin/org/cobalt/ui/UIScreen.kt index ee8e0cb..63944d2 100644 --- a/src/main/kotlin/org/cobalt/ui/UIScreen.kt +++ b/src/main/kotlin/org/cobalt/ui/UIScreen.kt @@ -6,7 +6,7 @@ import net.minecraft.client.input.CharacterEvent import net.minecraft.client.input.KeyEvent import net.minecraft.client.input.MouseButtonEvent import net.minecraft.network.chat.Component -import org.cobalt.util.skia.SkiaPIP +import org.cobalt.util.render.skia.SkiaPIP abstract class UIScreen : Screen(Component.empty()) { diff --git a/src/main/kotlin/org/cobalt/ui/component/ModuleComponent.kt b/src/main/kotlin/org/cobalt/ui/component/ModuleComponent.kt index 35c1701..49032b7 100644 --- a/src/main/kotlin/org/cobalt/ui/component/ModuleComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/ModuleComponent.kt @@ -3,7 +3,7 @@ package org.cobalt.ui.component import org.cobalt.module.Module import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.EaseOutAnimation -import org.cobalt.util.skia.Skia +import org.cobalt.util.render.SkiaRenderer class ModuleComponent( val module: Module, @@ -43,7 +43,7 @@ class ModuleComponent( } override fun renderComponent() { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, theme.backgroundSecondary ) @@ -58,13 +58,13 @@ class ModuleComponent( } if (settings.isNotEmpty() && expandProgress > 0f) { - Skia.line( + SkiaRenderer.line( xPos + PADDING, yPos + BASE_HEIGHT, xPos + width - PADDING, yPos + BASE_HEIGHT, 1f, theme.border ) - Skia.pushScissor(xPos, yPos + BASE_HEIGHT, width, expandedSettingsHeight * expandProgress) + SkiaRenderer.pushScissor(xPos, yPos + BASE_HEIGHT, width, expandedSettingsHeight * expandProgress) var settingY = yPos + BASE_HEIGHT + 6f @@ -75,17 +75,17 @@ class ModuleComponent( settingY += setting.height } - Skia.popScissor() + SkiaRenderer.popScissor() } - Skia.roundedOutline( + SkiaRenderer.roundedOutline( xPos, yPos, width, height, 1f, 5f, theme.border ) - Skia.text( - Skia.regularFont, module.name, + SkiaRenderer.text( + SkiaRenderer.regularFont, module.name, xPos + PADDING, yPos + (BASE_HEIGHT - FONT_SIZE) / 2, FONT_SIZE, theme.textPrimary ) diff --git a/src/main/kotlin/org/cobalt/ui/component/ScriptComponent.kt b/src/main/kotlin/org/cobalt/ui/component/ScriptComponent.kt index 178b00f..9b7e164 100644 --- a/src/main/kotlin/org/cobalt/ui/component/ScriptComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/ScriptComponent.kt @@ -1,15 +1,15 @@ package org.cobalt.ui.component -import org.cobalt.dsl.updateAlpha import org.cobalt.module.type.Script import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.ColorAnimation import org.cobalt.ui.animation.EaseOutAnimation import org.cobalt.ui.screen.ConfigScreen -import org.cobalt.util.MouseUtils -import org.cobalt.util.helper.TickScheduler -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.helper.SkiaImage +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.data.SkiaImage +import org.cobalt.util.scheduling.TickScheduler class ScriptComponent(val script: Script) : UIComponent( width = WIDTH, @@ -19,44 +19,44 @@ class ScriptComponent(val script: Script) : UIComponent( private val colorAnim = ColorAnimation(200L) private val alphaAnim = EaseOutAnimation(200L) private val backgroundPicture: SkiaImage? = if (script.backgroundResourcePath.isNotBlank()) { - Skia.createImage(script.backgroundResourcePath) + SkiaRenderer.createImage(script.backgroundResourcePath) } else { null } override fun renderComponent() { backgroundPicture?.let { - val (imageWidth, imageHeight) = Skia.imageSize(it) + val (imageWidth, imageHeight) = SkiaRenderer.imageSize(it) val scale = maxOf(width / imageWidth, height / imageHeight) val drawWidth = imageWidth * scale val drawHeight = imageHeight * scale val drawX = xPos + (width - drawWidth) / 2f val drawY = yPos + (height - drawHeight) / 2f - Skia.pushScissor(xPos, yPos, width, height, 5f) - Skia.blurredImage( + SkiaRenderer.pushScissor(xPos, yPos, width, height, 5f) + SkiaRenderer.blurredImage( it, drawX, drawY, drawWidth, drawHeight, radius = 2f, cornerRadius = 5f ) - Skia.popScissor() + SkiaRenderer.popScissor() } val borderColor = colorAnim.get(theme.border, theme.textMuted, !script.enabled) val alpha = alphaAnim.get(0f, 255f, !script.enabled).toInt() - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, theme.backgroundPrimary.updateAlpha(100) ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( xPos, yPos, width, height, 1f, 5f, borderColor ) if (script.enabled) { - Skia.image( + SkiaRenderer.image( pauseIcon, xPos + (width - ICON_SIZE) / 2, yPos + (height - ICON_SIZE) / 2, @@ -65,15 +65,15 @@ class ScriptComponent(val script: Script) : UIComponent( ) } - Skia.text( - Skia.regularFont, script.name, + SkiaRenderer.text( + SkiaRenderer.regularFont, script.name, xPos + PADDING, yPos + height - PADDING - FONT_SIZE, FONT_SIZE, theme.textPrimary ) } override fun mouseClicked(button: Int): Boolean { - if (button == 0 && MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (button == 0 && Mouse.isHoveringOver(xPos, yPos, width, height)) { colorAnim.start() if (script.enabled) { @@ -91,7 +91,7 @@ class ScriptComponent(val script: Script) : UIComponent( companion object { val WIDTH = (TopbarComponent.width - 60) / 2f - val pauseIcon = Skia.createImage("/assets/cobalt/ui/pause.svg") + val pauseIcon = SkiaRenderer.createImage("/assets/cobalt/ui/pause.svg") private const val PADDING = 20f private const val FONT_SIZE = 20f diff --git a/src/main/kotlin/org/cobalt/ui/component/SidebarComponent.kt b/src/main/kotlin/org/cobalt/ui/component/SidebarComponent.kt index 3169a32..811b200 100644 --- a/src/main/kotlin/org/cobalt/ui/component/SidebarComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/SidebarComponent.kt @@ -4,10 +4,10 @@ import org.cobalt.Cobalt.minecraft import org.cobalt.module.ModuleCategory import org.cobalt.ui.UIComponent import org.cobalt.ui.component.button.SidebarButton -import org.cobalt.util.helper.Multithreading -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.helper.SkiaCorner -import org.cobalt.util.skia.helper.SkiaImage +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.data.SkiaCorner +import org.cobalt.util.render.skia.data.SkiaImage +import org.cobalt.util.scheduling.Multithreading object SidebarComponent : UIComponent( width = 250f, @@ -15,7 +15,7 @@ object SidebarComponent : UIComponent( ) { private val buttons = mutableListOf() - private val steveFace = Skia.createImage("/assets/cobalt/ui/steve.png") + private val steveFace = SkiaRenderer.createImage("/assets/cobalt/ui/steve.png") private var playerFace: SkiaImage? = null init { @@ -26,23 +26,23 @@ object SidebarComponent : UIComponent( } Multithreading.runAsync { - playerFace = Skia.createImage("https://mc-heads.net/avatar/${minecraft.user.name}/100/face.png") + playerFace = SkiaRenderer.createImage("https://mc-heads.net/avatar/${minecraft.user.name}/100/face.png") } } override fun renderComponent() { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 10f, theme.backgroundSecondary, SkiaCorner.LEFT_SIDE ) - val titleTextWidth = Skia.textWidth(Skia.boldFont, TITLE_TEXT, TITLE_FONT_SIZE) + val titleTextWidth = SkiaRenderer.textWidth(SkiaRenderer.boldFont, TITLE_TEXT, TITLE_FONT_SIZE) val titleTextX = xPos + (width - titleTextWidth) / 2 val titleTextY = yPos + TITLE_PADDING - Skia.text( - Skia.boldFont, TITLE_TEXT, + SkiaRenderer.text( + SkiaRenderer.boldFont, TITLE_TEXT, titleTextX, titleTextY, TITLE_FONT_SIZE, theme.textPrimary ) @@ -65,12 +65,12 @@ object SidebarComponent : UIComponent( val boxX = xPos + USER_INFO_OUTER_PADDING val boxY = yPos + height - (USER_INFO_HEIGHT + USER_INFO_OUTER_PADDING) - Skia.roundedRect( + SkiaRenderer.roundedRect( boxX, boxY, USER_INFO_WIDTH, USER_INFO_HEIGHT, USER_INFO_CORNER_RADIUS, theme.backgroundPrimary, ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( boxX, boxY, USER_INFO_WIDTH, USER_INFO_HEIGHT, 1f, USER_INFO_CORNER_RADIUS, theme.border ) @@ -78,14 +78,14 @@ object SidebarComponent : UIComponent( val playerFaceX = boxX + USER_INFO_INNER_PADDING val playerFaceY = boxY + (USER_INFO_HEIGHT - PLAYER_FACE_SIDE_LENGTH) / 2 - Skia.image( + SkiaRenderer.image( playerFace ?: steveFace, playerFaceX, playerFaceY, PLAYER_FACE_SIDE_LENGTH, PLAYER_FACE_SIDE_LENGTH, PLAYER_FACE_SIDE_LENGTH / 2 ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( playerFaceX, playerFaceY, PLAYER_FACE_SIDE_LENGTH, PLAYER_FACE_SIDE_LENGTH, 1f, PLAYER_FACE_SIDE_LENGTH / 2, theme.border @@ -94,13 +94,13 @@ object SidebarComponent : UIComponent( val textX = boxX + PLAYER_FACE_SIDE_LENGTH + (USER_INFO_INNER_PADDING * 2) val textY = boxY + USER_INFO_INNER_PADDING - Skia.text( - Skia.regularFont, minecraft.gameProfile.name, + SkiaRenderer.text( + SkiaRenderer.regularFont, minecraft.gameProfile.name, textX, textY, USER_INFO_TEXT_SIZE, theme.textPrimary ) - Skia.text( - Skia.regularFont, "User", + SkiaRenderer.text( + SkiaRenderer.regularFont, "User", textX, textY + USER_INFO_TEXT_SIZE + 2f, USER_INFO_TEXT_SIZE, theme.textSecondary, ) diff --git a/src/main/kotlin/org/cobalt/ui/component/SwitchComponent.kt b/src/main/kotlin/org/cobalt/ui/component/SwitchComponent.kt index e16988a..c2b7a8d 100644 --- a/src/main/kotlin/org/cobalt/ui/component/SwitchComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/SwitchComponent.kt @@ -4,8 +4,8 @@ import org.cobalt.module.Module import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.ColorAnimation import org.cobalt.ui.animation.EaseOutAnimation -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class SwitchComponent(val module: Module) : UIComponent( width = 30f, @@ -19,12 +19,12 @@ class SwitchComponent(val module: Module) : UIComponent( val mainColor = colorAnimation.get(theme.backgroundPrimary, theme.accentPrimary, !module.enabled) val knobX = xOffsetAnimation.get(xPos + 1f, xPos + width - KNOB_SIZE - 1f, !module.enabled) - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, mainColor ) - Skia.circle( + SkiaRenderer.circle( knobX + KNOB_SIZE / 2f, yPos + height / 2f, KNOB_SIZE / 2f, @@ -33,7 +33,7 @@ class SwitchComponent(val module: Module) : UIComponent( } override fun mouseClicked(button: Int): Boolean { - if (button == 0 && MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (button == 0 && Mouse.isHoveringOver(xPos, yPos, width, height)) { module.enabled = !module.enabled colorAnimation.start() xOffsetAnimation.start() diff --git a/src/main/kotlin/org/cobalt/ui/component/TextInputComponent.kt b/src/main/kotlin/org/cobalt/ui/component/TextInputComponent.kt index 4d6408f..a8fb370 100644 --- a/src/main/kotlin/org/cobalt/ui/component/TextInputComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/TextInputComponent.kt @@ -3,11 +3,11 @@ package org.cobalt.ui.component import java.util.function.Consumer import net.minecraft.client.input.CharacterEvent import net.minecraft.client.input.KeyEvent -import org.cobalt.dsl.updateAlpha import org.cobalt.ui.UIComponent import org.cobalt.ui.helper.TextInputHelper -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class TextInputComponent( width: Float, @@ -27,7 +27,7 @@ class TextInputComponent( override fun renderComponent() { inputHandler.updateBounds(xPos, yPos, width, height) - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, theme.backgroundPrimary @@ -43,12 +43,12 @@ class TextInputComponent( val textX = xPos + TEXT_PADDING - xOffset val textY = yPos + (height - fontSize) / 2 - Skia.pushScissor(xPos, yPos, width, height) + SkiaRenderer.pushScissor(xPos, yPos, width, height) drawSelection(textX, textY) - Skia.text( - Skia.regularFont, + SkiaRenderer.text( + SkiaRenderer.regularFont, currentText, textX, textY, fontSize, textColor @@ -56,15 +56,15 @@ class TextInputComponent( if (inputHandler.focused && (System.currentTimeMillis() / 500) % 2 == 0L) { val caretX = textX + caretOffset - Skia.rect( + SkiaRenderer.rect( caretX, textY, 1.5f, fontSize, theme.textPrimary ) } - Skia.popScissor() - Skia.roundedOutline( + SkiaRenderer.popScissor() + SkiaRenderer.roundedOutline( xPos, yPos, width, height, 1f, 5f, theme.border @@ -92,12 +92,12 @@ class TextInputComponent( inputHandler.text.substring(0, inputHandler.selectionEnd) } - val selStartX = textX + Skia.textWidth(Skia.regularFont, selStartPrefix, fontSize) - val selEndX = textX + Skia.textWidth(Skia.regularFont, selEndPrefix, fontSize) + val selStartX = textX + SkiaRenderer.textWidth(SkiaRenderer.regularFont, selStartPrefix, fontSize) + val selEndX = textX + SkiaRenderer.textWidth(SkiaRenderer.regularFont, selEndPrefix, fontSize) val selWidth = selEndX - selStartX val selectionColor = theme.textPrimary.updateAlpha(40) - Skia.rect( + SkiaRenderer.rect( selStartX, textY, selWidth, fontSize, selectionColor @@ -123,11 +123,11 @@ class TextInputComponent( inputHandler.text.substring(0, inputHandler.caretIndex) } - return Skia.textWidth(Skia.regularFont, caretPrefix, fontSize) + return SkiaRenderer.textWidth(SkiaRenderer.regularFont, caretPrefix, fontSize) } private fun updateScrollOffset(currentText: String, maxTextWidth: Float, caretOffset: Float) { - val textWidth = Skia.textWidth(Skia.regularFont, currentText, fontSize) + val textWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, currentText, fontSize) if (textWidth <= maxTextWidth) { xOffset = 0f @@ -141,7 +141,7 @@ class TextInputComponent( } override fun mouseClicked(button: Int): Boolean { - val relativeX = MouseUtils.mouseX - (xPos + TEXT_PADDING - xOffset) + val relativeX = Mouse.mouseX - (xPos + TEXT_PADDING - xOffset) return inputHandler.mouseClicked(button, relativeX) || super.mouseClicked(button) } @@ -150,7 +150,7 @@ class TextInputComponent( } override fun mouseDragged(button: Int, offsetX: Double, offsetY: Double): Boolean { - val relativeX = MouseUtils.mouseX - (xPos + TEXT_PADDING - xOffset) + val relativeX = Mouse.mouseX - (xPos + TEXT_PADDING - xOffset) return inputHandler.mouseDragged(button, relativeX) || super.mouseDragged(button, offsetX, offsetY) } diff --git a/src/main/kotlin/org/cobalt/ui/component/ThemeComponent.kt b/src/main/kotlin/org/cobalt/ui/component/ThemeComponent.kt index 40a00ee..fd04262 100644 --- a/src/main/kotlin/org/cobalt/ui/component/ThemeComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/ThemeComponent.kt @@ -1,11 +1,11 @@ package org.cobalt.ui.component -import org.cobalt.dsl.updateAlpha import org.cobalt.ui.UIComponent import org.cobalt.ui.theme.Theme import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class ThemeComponent(val newTheme: Theme) : UIComponent( width = WIDTH, @@ -13,13 +13,13 @@ class ThemeComponent(val newTheme: Theme) : UIComponent( ) { override fun renderComponent() { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, newTheme.accentPrimary.updateAlpha(40) ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( xPos, yPos, width, height, 1f, 5f, newTheme.accentPrimary @@ -28,8 +28,8 @@ class ThemeComponent(val newTheme: Theme) : UIComponent( val startX = xPos + INNER_PADDING var startY = yPos + INNER_PADDING - Skia.text( - Skia.regularFont, newTheme.name, + SkiaRenderer.text( + SkiaRenderer.regularFont, newTheme.name, startX, startY, THEME_NAME_FONT_SIZE, theme.textPrimary ) @@ -39,21 +39,21 @@ class ThemeComponent(val newTheme: Theme) : UIComponent( val textColor = if (isActive) theme.success else theme.textMuted val statusY = startY + THEME_NAME_FONT_SIZE + TEXT_PADDING - Skia.text( - Skia.regularFont, text, + SkiaRenderer.text( + SkiaRenderer.regularFont, text, startX, statusY, STATUS_FONT_SIZE, textColor ) startY = yPos + height - SWATCH_HEIGHT - INNER_PADDING - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, startY, SWATCH_WIDTH, SWATCH_HEIGHT, 2.5f, newTheme.accentPrimary ) - Skia.roundedRect( + SkiaRenderer.roundedRect( startX + SWATCH_WIDTH + SWATCH_PADDING, startY, SWATCH_WIDTH, SWATCH_HEIGHT, 2.5f, newTheme.accentSecondary @@ -61,7 +61,7 @@ class ThemeComponent(val newTheme: Theme) : UIComponent( } override fun mouseClicked(button: Int): Boolean { - val isHovered = MouseUtils.isHoveringOver(xPos, yPos, width, height) + val isHovered = Mouse.isHoveringOver(xPos, yPos, width, height) if (button != 0 || !isHovered) { return false diff --git a/src/main/kotlin/org/cobalt/ui/component/TopbarComponent.kt b/src/main/kotlin/org/cobalt/ui/component/TopbarComponent.kt index 7ce9fba..f3f6a4a 100644 --- a/src/main/kotlin/org/cobalt/ui/component/TopbarComponent.kt +++ b/src/main/kotlin/org/cobalt/ui/component/TopbarComponent.kt @@ -7,9 +7,9 @@ import org.cobalt.ui.page.impl.ScriptsPage import org.cobalt.ui.page.impl.ThemesPage import org.cobalt.ui.screen.ConfigScreen import org.cobalt.ui.screen.HudEditorScreen -import org.cobalt.util.helper.TickScheduler -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.helper.SkiaCorner +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.data.SkiaCorner +import org.cobalt.util.scheduling.TickScheduler object TopbarComponent : UIComponent() { @@ -51,7 +51,7 @@ object TopbarComponent : UIComponent() { override fun renderComponent() { val currentPage = ConfigScreen.currentPage - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 10f, theme.backgroundSecondary, @@ -61,8 +61,8 @@ object TopbarComponent : UIComponent() { val textX = xPos + INNER_PADDING + 10f val textY = yPos + (height - CURRENT_PAGE_TITLE_FONT) / 2 - Skia.text( - Skia.regularFont, currentPage.title, + SkiaRenderer.text( + SkiaRenderer.regularFont, currentPage.title, textX, textY, CURRENT_PAGE_TITLE_FONT, theme.textPrimary ) diff --git a/src/main/kotlin/org/cobalt/ui/component/button/IconButton.kt b/src/main/kotlin/org/cobalt/ui/component/button/IconButton.kt index 270a8f6..d615723 100644 --- a/src/main/kotlin/org/cobalt/ui/component/button/IconButton.kt +++ b/src/main/kotlin/org/cobalt/ui/component/button/IconButton.kt @@ -1,11 +1,11 @@ package org.cobalt.ui.component.button -import org.cobalt.dsl.updateAlpha import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.ColorAnimation import org.cobalt.ui.animation.EaseOutAnimation -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class IconButton( resourcePath: String, @@ -15,14 +15,14 @@ class IconButton( height = 40f ) { - private val icon = Skia.createImage(resourcePath) + private val icon = SkiaRenderer.createImage(resourcePath) private val colorAnimation = ColorAnimation(150L) private val alphaAnimation = EaseOutAnimation(150L) private var wasHovering = false override fun renderComponent() { - val hovering = MouseUtils.isHoveringOver(xPos, yPos, width, height) + val hovering = Mouse.isHoveringOver(xPos, yPos, width, height) if (hovering != wasHovering) { colorAnimation.start() @@ -34,19 +34,19 @@ class IconButton( val borderColor = colorAnimation.get(theme.border, theme.accentPrimary, !hovering) val iconColor = colorAnimation.get(theme.textMuted, theme.accentPrimary, !hovering) - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, theme.backgroundPrimary ) - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, 5f, theme.accentPrimary.updateAlpha(alpha) ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( xPos, yPos, width, height, 1f, 5f, borderColor @@ -55,14 +55,14 @@ class IconButton( val iconX = xPos + (width - ICON_SIZE) / 2f val iconY = yPos + (height - ICON_SIZE) / 2f - Skia.image( + SkiaRenderer.image( icon, iconX, iconY, ICON_SIZE, ICON_SIZE, color = iconColor ) } override fun mouseClicked(button: Int): Boolean { - if (button != 0 || !MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (button != 0 || !Mouse.isHoveringOver(xPos, yPos, width, height)) { return false } diff --git a/src/main/kotlin/org/cobalt/ui/component/button/SidebarButton.kt b/src/main/kotlin/org/cobalt/ui/component/button/SidebarButton.kt index 0544741..e7a6feb 100644 --- a/src/main/kotlin/org/cobalt/ui/component/button/SidebarButton.kt +++ b/src/main/kotlin/org/cobalt/ui/component/button/SidebarButton.kt @@ -1,21 +1,21 @@ package org.cobalt.ui.component.button -import org.cobalt.dsl.updateAlpha import org.cobalt.module.ModuleCategory import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.ColorAnimation import org.cobalt.ui.animation.EaseOutAnimation import org.cobalt.ui.page.impl.ModulesPage import org.cobalt.ui.screen.ConfigScreen -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class SidebarButton(val category: ModuleCategory) : UIComponent( width = WIDTH, height = HEIGHT ) { - private val icon = Skia.createImage(category.iconPath) + private val icon = SkiaRenderer.createImage(category.iconPath) private val colorAnimation = ColorAnimation(150L) private val xOffsetAnimation = EaseOutAnimation(200L) @@ -38,14 +38,14 @@ class SidebarButton(val category: ModuleCategory) : UIComponent( val xOffset = xOffsetAnimation.get(0F, TEXT_SELECTED_OFFSET, !isSelectedNow) if (isSelectedNow) { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, CORNER_RADIUS, opaqueColor ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( xPos, yPos, width, height, 1f, CORNER_RADIUS, mainColor @@ -55,7 +55,7 @@ class SidebarButton(val category: ModuleCategory) : UIComponent( val iconX = xPos + ICON_LEFT_PADDING fun iconY(iconSize: Float) = yPos + (height - iconSize) / 2F - Skia.image( + SkiaRenderer.image( icon, iconX + xOffset, iconY(ICON_SIZE), ICON_SIZE, ICON_SIZE, @@ -63,7 +63,7 @@ class SidebarButton(val category: ModuleCategory) : UIComponent( ) if (isSelectedNow) { - Skia.image( + SkiaRenderer.image( selectedIcon, iconX, iconY(SELECTION_ICON_SIZE), SELECTION_ICON_SIZE, SELECTION_ICON_SIZE, @@ -74,8 +74,8 @@ class SidebarButton(val category: ModuleCategory) : UIComponent( val textX = iconX + ICON_SIZE + ICON_TEXT_PADDING val textY = yPos + height / 2F - FONT_SIZE / 2F - Skia.text( - Skia.regularFont, + SkiaRenderer.text( + SkiaRenderer.regularFont, category.displayName, textX + xOffset, textY, FONT_SIZE, textColor @@ -83,7 +83,7 @@ class SidebarButton(val category: ModuleCategory) : UIComponent( } override fun mouseClicked(button: Int): Boolean { - if (button == 0 && MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (button == 0 && Mouse.isHoveringOver(xPos, yPos, width, height)) { ConfigScreen.currentPage = ModulesPage ConfigScreen.selectedCategory = category return true @@ -104,7 +104,7 @@ class SidebarButton(val category: ModuleCategory) : UIComponent( private const val SELECTION_ICON_SIZE = 13f private const val TEXT_SELECTED_OFFSET = 17f - private val selectedIcon = Skia.createImage("/assets/cobalt/ui/selected.svg") + private val selectedIcon = SkiaRenderer.createImage("/assets/cobalt/ui/selected.svg") } } diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/Setting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/Setting.kt index 24b3cb8..319ab67 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/Setting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/Setting.kt @@ -8,7 +8,7 @@ import org.cobalt.ui.UIComponent import org.cobalt.ui.component.ModuleComponent import org.cobalt.ui.component.setting.impl.InfoSetting import org.cobalt.util.config.SettingContainer -import org.cobalt.util.skia.Skia +import org.cobalt.util.render.SkiaRenderer abstract class Setting( val name: String, @@ -31,15 +31,15 @@ abstract class Setting( val totalTextHeight = NAME_SIZE + extraHeight val textStartY = yPos + (BASE_HEIGHT - totalTextHeight) / 2 - Skia.text( - Skia.regularFont, name, + SkiaRenderer.text( + SkiaRenderer.regularFont, name, xPos + PADDING, textStartY, NAME_SIZE, theme.textPrimary ) if (description.isNotBlank()) { - Skia.text( - Skia.regularFont, description, + SkiaRenderer.text( + SkiaRenderer.regularFont, description, xPos + PADDING, textStartY + NAME_SIZE + TEXT_SPACING_Y, DESCRIPTION_SIZE, theme.textMuted ) diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/ButtonSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/ButtonSetting.kt index be53ee9..6e5cde3 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/ButtonSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/ButtonSetting.kt @@ -2,12 +2,12 @@ package org.cobalt.ui.component.setting.impl import com.google.gson.JsonElement import com.google.gson.JsonPrimitive -import org.cobalt.dsl.updateAlpha import org.cobalt.ui.animation.ColorAnimation import org.cobalt.ui.animation.EaseOutAnimation import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class ButtonSetting( name: String, @@ -24,14 +24,14 @@ class ButtonSetting( private var wasHovering = false private val buttonWidth = - Skia.textWidth(Skia.regularFont, buttonLabel, FONT_SIZE) + 30f + SkiaRenderer.textWidth(SkiaRenderer.regularFont, buttonLabel, FONT_SIZE) + 30f override fun renderSetting() { val buttonWidth = buttonWidth val startX = xPos + width - buttonWidth - PADDING val startY = yPos + (height - BUTTON_HEIGHT) / 2 - val hovering = MouseUtils.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT) + val hovering = Mouse.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT) if (hovering != wasHovering) { colorAnim.start() @@ -43,28 +43,28 @@ class ButtonSetting( val borderColor = colorAnim.get(theme.border, theme.accentPrimary, !hovering) val textColor = colorAnim.get(theme.textPrimary, theme.accentPrimary, !hovering) - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, startY, buttonWidth, BUTTON_HEIGHT, 5f, theme.backgroundPrimary ) - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, startY, buttonWidth, BUTTON_HEIGHT, 5f, theme.accentPrimary.updateAlpha(alpha) ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( startX, startY, buttonWidth, BUTTON_HEIGHT, 1f, 5f, borderColor ) - val textWidth = Skia.textWidth(Skia.regularFont, buttonLabel, FONT_SIZE) + val textWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, buttonLabel, FONT_SIZE) - Skia.text( - Skia.regularFont, buttonLabel, + SkiaRenderer.text( + SkiaRenderer.regularFont, buttonLabel, startX + (buttonWidth - textWidth) / 2, startY + (BUTTON_HEIGHT - FONT_SIZE) / 2, FONT_SIZE, textColor @@ -75,7 +75,7 @@ class ButtonSetting( val startX = xPos + width - buttonWidth - PADDING val startY = yPos + (height - BUTTON_HEIGHT) / 2 - if (button == 0 && MouseUtils.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT)) { + if (button == 0 && Mouse.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT)) { onClick.run() return true } diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/CheckboxSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/CheckboxSetting.kt index d901457..18f30b8 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/CheckboxSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/CheckboxSetting.kt @@ -2,12 +2,12 @@ package org.cobalt.ui.component.setting.impl import com.google.gson.JsonElement import com.google.gson.JsonPrimitive -import org.cobalt.dsl.updateAlpha import org.cobalt.ui.animation.ColorAnimation import org.cobalt.ui.animation.EaseOutAnimation import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class CheckboxSetting( name: String, @@ -34,19 +34,19 @@ class CheckboxSetting( val borderColor = colorAnimation.get(theme.border, theme.accentPrimary, !value) val bgColor = colorAnimation.get(theme.backgroundPrimary, theme.accentPrimary, !value) - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, startY, SIZE, SIZE, 5f, bgColor.updateAlpha(alpha) ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( startX, startY, SIZE, SIZE, 1f, 5f, borderColor ) - Skia.circle( + SkiaRenderer.circle( startX + SIZE / 2, startY + SIZE / 2, 2f, borderColor @@ -54,7 +54,7 @@ class CheckboxSetting( } override fun mouseClicked(button: Int): Boolean { - if (!MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (!Mouse.isHoveringOver(xPos, yPos, width, height)) { return false } diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/InfoSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/InfoSetting.kt index 3e3015d..e9f3f37 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/InfoSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/InfoSetting.kt @@ -2,9 +2,9 @@ package org.cobalt.ui.component.setting.impl import com.google.gson.JsonElement import com.google.gson.JsonPrimitive -import org.cobalt.dsl.updateAlpha import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.skia.Skia +import org.cobalt.util.color.updateAlpha +import org.cobalt.util.render.SkiaRenderer class InfoSetting( val text: String, @@ -15,13 +15,13 @@ class InfoSetting( override fun write(): JsonElement = JsonPrimitive("") override fun renderSetting() { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos + INNER_PADDING, yPos + INNER_PADDING, width - (INNER_PADDING * 2), height - (INNER_PADDING * 2), 5f, color.updateAlpha(40) ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( xPos + INNER_PADDING, yPos + INNER_PADDING, width - (INNER_PADDING * 2), height - (INNER_PADDING * 2), 1f, 5f, color @@ -29,7 +29,7 @@ class InfoSetting( val imageY = yPos + 1f + (height - ICON_SIZE) / 2 - Skia.image( + SkiaRenderer.image( icon, xPos + PADDING, imageY, ICON_SIZE, ICON_SIZE, color = color @@ -38,8 +38,8 @@ class InfoSetting( val textX = xPos + ICON_SIZE + PADDING + 10f val textY = yPos + (height - NAME_SIZE) / 2 - Skia.text( - Skia.regularFont, text, + SkiaRenderer.text( + SkiaRenderer.regularFont, text, textX, textY, NAME_SIZE, color ) @@ -52,7 +52,7 @@ class InfoSetting( Type.ERROR -> theme.error } - private val icon = Skia.createImage( + private val icon = SkiaRenderer.createImage( when (type) { Type.INFO -> "/assets/cobalt/ui/info.svg" Type.WARNING -> "/assets/cobalt/ui/warning.svg" diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/KeyBindSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/KeyBindSetting.kt index 0058962..72378d2 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/KeyBindSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/KeyBindSetting.kt @@ -5,8 +5,8 @@ import com.google.gson.JsonPrimitive import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.input.KeyEvent import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer import org.lwjgl.glfw.GLFW class KeyBindSetting( @@ -35,28 +35,28 @@ class KeyBindSetting( get() = if (listening) "..." else value.displayName.string private val buttonWidth: Float - get() = Skia.textWidth(Skia.regularFont, displayText, FONT_SIZE) + 30f + get() = SkiaRenderer.textWidth(SkiaRenderer.regularFont, displayText, FONT_SIZE) + 30f override fun renderSetting() { val startX = xPos + width - buttonWidth - PADDING val startY = yPos + (height - BUTTON_HEIGHT) / 2 - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, startY, buttonWidth, BUTTON_HEIGHT, 5f, theme.backgroundPrimary ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( startX, startY, buttonWidth, BUTTON_HEIGHT, 1f, 5f, theme.border ) - val textWidth = Skia.textWidth(Skia.regularFont, displayText, FONT_SIZE) + val textWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, displayText, FONT_SIZE) - Skia.text( - Skia.regularFont, displayText, + SkiaRenderer.text( + SkiaRenderer.regularFont, displayText, startX + (buttonWidth - textWidth) / 2, startY + (BUTTON_HEIGHT - FONT_SIZE) / 2, FONT_SIZE, theme.textPrimary @@ -66,7 +66,7 @@ class KeyBindSetting( override fun mouseClicked(button: Int): Boolean { val startX = xPos + width - buttonWidth - PADDING val startY = yPos + (height - BUTTON_HEIGHT) / 2 - val isHovered = MouseUtils.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT) + val isHovered = Mouse.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT) if (listening) { value = InputConstants.Type.MOUSE.getOrCreate(button) diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt index b62260a..df21fc3 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/ModeSetting.kt @@ -3,8 +3,8 @@ package org.cobalt.ui.component.setting.impl import com.google.gson.JsonElement import com.google.gson.JsonPrimitive import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class ModeSetting( name: String, @@ -27,29 +27,29 @@ class ModeSetting( override fun renderSetting() { val display = options.getOrNull(value) ?: "" - val buttonWidth = Skia.textWidth(Skia.regularFont, display, FONT_SIZE) + 30f + val buttonWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, display, FONT_SIZE) + 30f val startX = xPos + width - buttonWidth - PADDING val startY = yPos + (height - BUTTON_HEIGHT) / 2 val borderColor = theme.border val textColor = theme.textPrimary - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, startY, buttonWidth, BUTTON_HEIGHT, 5f, theme.backgroundPrimary ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( startX, startY, buttonWidth, BUTTON_HEIGHT, 1f, 5f, borderColor ) - val textWidth = Skia.textWidth(Skia.regularFont, display, FONT_SIZE) + val textWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, display, FONT_SIZE) - Skia.text( - Skia.regularFont, display, + SkiaRenderer.text( + SkiaRenderer.regularFont, display, startX + (buttonWidth - textWidth) / 2, startY + (BUTTON_HEIGHT - FONT_SIZE) / 2, FONT_SIZE, textColor @@ -62,11 +62,11 @@ class ModeSetting( } val display = options.getOrNull(value) ?: "" - val buttonWidth = Skia.textWidth(Skia.regularFont, display, FONT_SIZE) + 30f + val buttonWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, display, FONT_SIZE) + 30f val startX = xPos + width - buttonWidth - PADDING val startY = yPos + (height - BUTTON_HEIGHT) / 2 - if (!MouseUtils.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT)) { + if (!Mouse.isHoveringOver(startX, startY, buttonWidth, BUTTON_HEIGHT)) { return false } diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/RangeSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/RangeSetting.kt index 61f66a4..9a6a5d9 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/RangeSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/RangeSetting.kt @@ -4,8 +4,8 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class RangeSetting( name: String, @@ -43,22 +43,22 @@ class RangeSetting( val boxX = xPos + width - PADDING - boxWidth val boxY = yPos + (BASE_HEIGHT - VALUE_BOX_HEIGHT) / 2 - Skia.roundedRect( + SkiaRenderer.roundedRect( boxX, boxY, boxWidth, VALUE_BOX_HEIGHT, 5f, theme.backgroundPrimary ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( boxX, boxY, boxWidth, VALUE_BOX_HEIGHT, 1f, 5f, theme.border ) - val textWidth = Skia.textWidth(Skia.regularFont, text, FONT_SIZE) + val textWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, text, FONT_SIZE) - Skia.text( - Skia.regularFont, text, + SkiaRenderer.text( + SkiaRenderer.regularFont, text, boxX + (boxWidth - textWidth) / 2, boxY + (VALUE_BOX_HEIGHT - FONT_SIZE) / 2, FONT_SIZE, theme.textPrimary @@ -66,24 +66,24 @@ class RangeSetting( val geometry = trackGeometry() - Skia.roundedRect( + SkiaRenderer.roundedRect( geometry.startX, geometry.trackY - 2f, geometry.trackWidth, 4f, 3f, theme.backgroundPrimary ) - Skia.roundedRect( + SkiaRenderer.roundedRect( geometry.startKnobX, geometry.trackY - 2f, (geometry.endKnobX - geometry.startKnobX).coerceAtLeast(0f), 4f, 3f, theme.accentPrimary ) - Skia.circle( + SkiaRenderer.circle( geometry.startKnobX, geometry.trackY, KNOB_RADIUS, theme.textPrimary ) - Skia.circle( + SkiaRenderer.circle( geometry.endKnobX, geometry.trackY, KNOB_RADIUS, theme.textPrimary ) @@ -97,14 +97,14 @@ class RangeSetting( val geometry = trackGeometry() dragging = when { - MouseUtils.isHoveringOver( + Mouse.isHoveringOver( geometry.startKnobX - KNOB_RADIUS, geometry.trackY - KNOB_RADIUS, KNOB_RADIUS * 2, KNOB_RADIUS * 2 ) -> Knob.START - MouseUtils.isHoveringOver( + Mouse.isHoveringOver( geometry.endKnobX - KNOB_RADIUS, geometry.trackY - KNOB_RADIUS, KNOB_RADIUS * 2, @@ -141,7 +141,7 @@ class RangeSetting( private fun updateValue() { val (startX, trackWidth) = trackGeometry() - val rel = ((MouseUtils.mouseX - startX) / trackWidth).coerceIn(0f, 1f) + val rel = ((Mouse.mouseX - startX) / trackWidth).coerceIn(0f, 1f) val raw = (min + rel * (max - min)).coerceIn(min.toFloat(), max.toFloat()) when (dragging) { @@ -191,7 +191,7 @@ class RangeSetting( } private fun boxWidth(text: String): Float = - Skia.textWidth(Skia.regularFont, text, FONT_SIZE) + VALUE_BOX_PADDING_X * 2f + SkiaRenderer.textWidth(SkiaRenderer.regularFont, text, FONT_SIZE) + VALUE_BOX_PADDING_X * 2f private data class TrackGeometry( val startX: Float, diff --git a/src/main/kotlin/org/cobalt/ui/component/setting/impl/SliderSetting.kt b/src/main/kotlin/org/cobalt/ui/component/setting/impl/SliderSetting.kt index 6293558..3bb45e3 100644 --- a/src/main/kotlin/org/cobalt/ui/component/setting/impl/SliderSetting.kt +++ b/src/main/kotlin/org/cobalt/ui/component/setting/impl/SliderSetting.kt @@ -3,8 +3,8 @@ package org.cobalt.ui.component.setting.impl import com.google.gson.JsonElement import com.google.gson.JsonPrimitive import org.cobalt.ui.component.setting.Setting -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer class SliderSetting( name: String, @@ -33,20 +33,20 @@ class SliderSetting( val boxX = xPos + width - PADDING - boxWidth val boxY = yPos + (BASE_HEIGHT - VALUE_BOX_HEIGHT) / 2 - Skia.roundedRect( + SkiaRenderer.roundedRect( boxX, boxY, boxWidth, VALUE_BOX_HEIGHT, 5f, theme.backgroundPrimary ) - Skia.roundedOutline( + SkiaRenderer.roundedOutline( boxX, boxY, boxWidth, VALUE_BOX_HEIGHT, 1f, 5f, theme.border ) - val textWidth = Skia.textWidth(Skia.regularFont, text, FONT_SIZE) + val textWidth = SkiaRenderer.textWidth(SkiaRenderer.regularFont, text, FONT_SIZE) - Skia.text( - Skia.regularFont, text, + SkiaRenderer.text( + SkiaRenderer.regularFont, text, boxX + (boxWidth - textWidth) / 2, boxY + (VALUE_BOX_HEIGHT - FONT_SIZE) / 2, FONT_SIZE, theme.textPrimary @@ -54,19 +54,19 @@ class SliderSetting( val (startX, trackWidth, trackY, knobX) = trackGeometry() - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, trackY - 2f, trackWidth, 4f, 3f, theme.backgroundPrimary ) - Skia.roundedRect( + SkiaRenderer.roundedRect( startX, trackY - 2f, (knobX - startX).coerceAtLeast(0f), 4f, 3f, theme.accentPrimary ) - Skia.circle( + SkiaRenderer.circle( knobX, trackY, KNOB_RADIUS, theme.textPrimary ) @@ -80,7 +80,7 @@ class SliderSetting( val (_, _, trackY, knobX) = trackGeometry() if ( - !MouseUtils.isHoveringOver( + !Mouse.isHoveringOver( knobX - KNOB_RADIUS, trackY - KNOB_RADIUS, KNOB_RADIUS * 2, KNOB_RADIUS * 2 ) @@ -115,7 +115,7 @@ class SliderSetting( private fun updateValue() { val (startX, trackWidth) = trackGeometry() - val rel = ((MouseUtils.mouseX - startX) / trackWidth).coerceIn(0f, 1f) + val rel = ((Mouse.mouseX - startX) / trackWidth).coerceIn(0f, 1f) rawValue = (min + rel * (max - min)).coerceIn(min.toFloat(), max.toFloat()) value = rawValue.toInt().coerceIn(min, max) @@ -132,7 +132,7 @@ class SliderSetting( } private fun valueBoxWidth(text: String): Float = - Skia.textWidth(Skia.regularFont, text, FONT_SIZE) + VALUE_BOX_PADDING_X * 2f + SkiaRenderer.textWidth(SkiaRenderer.regularFont, text, FONT_SIZE) + VALUE_BOX_PADDING_X * 2f private data class TrackGeometry( val startX: Float, diff --git a/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt b/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt index a306efe..c92f0e1 100644 --- a/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt +++ b/src/main/kotlin/org/cobalt/ui/helper/DragHandler.kt @@ -1,13 +1,13 @@ package org.cobalt.ui.helper import org.cobalt.module.type.RenderableModule -import org.cobalt.util.MouseUtils -import org.cobalt.util.MouseUtils.mouseX -import org.cobalt.util.MouseUtils.mouseY -import org.cobalt.util.WindowUtils.scaleX -import org.cobalt.util.WindowUtils.scaleY -import org.cobalt.util.WindowUtils.windowHeight -import org.cobalt.util.WindowUtils.windowWidth +import org.cobalt.util.client.WindowUtils.scaleX +import org.cobalt.util.client.WindowUtils.scaleY +import org.cobalt.util.client.WindowUtils.windowHeight +import org.cobalt.util.client.WindowUtils.windowWidth +import org.cobalt.util.input.Mouse +import org.cobalt.util.input.Mouse.mouseX +import org.cobalt.util.input.Mouse.mouseY class DragHandler { @@ -43,7 +43,7 @@ class DragHandler { val squareOffset = squareSizeScaled / 2f if ( - !MouseUtils.isHoveringOver( + !Mouse.isHoveringOver( renderX + scaledWidth - squareOffset, renderY + scaledHeight - squareOffset, squareSizeScaled, squareSizeScaled diff --git a/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt b/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt index cea0999..f556aea 100644 --- a/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt +++ b/src/main/kotlin/org/cobalt/ui/helper/SnapHelper.kt @@ -1,7 +1,7 @@ package org.cobalt.ui.helper import kotlin.math.abs -import org.cobalt.util.WindowUtils +import org.cobalt.util.client.WindowUtils class SnapHelper(private val snapThreshold: Float = 5f) { diff --git a/src/main/kotlin/org/cobalt/ui/helper/TextInputHelper.kt b/src/main/kotlin/org/cobalt/ui/helper/TextInputHelper.kt index 5c6b4bf..b673dd8 100644 --- a/src/main/kotlin/org/cobalt/ui/helper/TextInputHelper.kt +++ b/src/main/kotlin/org/cobalt/ui/helper/TextInputHelper.kt @@ -5,8 +5,8 @@ import kotlin.math.abs import net.minecraft.client.input.CharacterEvent import net.minecraft.client.input.KeyEvent import org.cobalt.ui.component.TextInputComponent -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer import org.lwjgl.glfw.GLFW class TextInputHelper( @@ -79,7 +79,7 @@ class TextInputHelper( return false } - val hoveringOver = MouseUtils.isHoveringOver(xPos, yPos, width, height) + val hoveringOver = Mouse.isHoveringOver(xPos, yPos, width, height) focused = hoveringOver if (hoveringOver) { @@ -110,7 +110,7 @@ class TextInputHelper( return false } - focused = MouseUtils.isHoveringOver(xPos, yPos, width, height) + focused = Mouse.isHoveringOver(xPos, yPos, width, height) return focused } @@ -124,7 +124,7 @@ class TextInputHelper( for (i in 0..text.length) { val prefix = if (textInputType == TextInputComponent.Type.PASSWORD) "*".repeat(i) else text.substring(0, i) - val w = Skia.textWidth(Skia.regularFont, prefix, fontSize) + val w = SkiaRenderer.textWidth(SkiaRenderer.regularFont, prefix, fontSize) val diff = abs(w - relativeX) if (diff < minDiff) { diff --git a/src/main/kotlin/org/cobalt/ui/notification/Notification.kt b/src/main/kotlin/org/cobalt/ui/notification/Notification.kt index ca9442f..8385f7d 100644 --- a/src/main/kotlin/org/cobalt/ui/notification/Notification.kt +++ b/src/main/kotlin/org/cobalt/ui/notification/Notification.kt @@ -4,9 +4,9 @@ import kotlin.time.Duration import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.BounceAnimation import org.cobalt.ui.animation.EaseOutAnimation -import org.cobalt.util.WindowUtils.windowWidth -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.helper.SkiaCorner +import org.cobalt.util.client.WindowUtils.windowWidth +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.data.SkiaCorner class Notification( private val title: String, @@ -60,7 +60,7 @@ class Notification( val resolvedY = targetY + slideDownAnim.get(previousY - targetY, 0f, false) updateBounds(resolvedX, resolvedY) - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, CORNER_RADIUS, theme.backgroundPrimary ) @@ -72,18 +72,18 @@ class Notification( private fun drawText() { val contentWidth = width - CONTENT_PADDING * 2 - Skia.wrappedText( - Skia.boldFont, title, + SkiaRenderer.wrappedText( + SkiaRenderer.boldFont, title, xPos + CONTENT_PADDING, yPos + CONTENT_PADDING, contentWidth, TITLE_FONT_SIZE, theme.textPrimary ) - val titleHeight = Skia.wrappedTextHeight( - Skia.boldFont, title, contentWidth, TITLE_FONT_SIZE + val titleHeight = SkiaRenderer.wrappedTextHeight( + SkiaRenderer.boldFont, title, contentWidth, TITLE_FONT_SIZE ) - Skia.wrappedText( - Skia.boldFont, description, + SkiaRenderer.wrappedText( + SkiaRenderer.boldFont, description, xPos + CONTENT_PADDING, yPos + CONTENT_PADDING + titleHeight + TITLE_DESCRIPTION_GAP, contentWidth, DESCRIPTION_FONT_SIZE, theme.textSecondary ) @@ -93,7 +93,7 @@ class Notification( val progress = calculateProgress(System.currentTimeMillis()) val fillWidth = width * progress - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos + height - PROGRESS_BAR_HEIGHT, width, PROGRESS_BAR_HEIGHT, CORNER_RADIUS, @@ -102,7 +102,7 @@ class Notification( ) if (fillWidth > 0f) { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos + height - PROGRESS_BAR_HEIGHT, fillWidth, PROGRESS_BAR_HEIGHT, CORNER_RADIUS, @@ -142,14 +142,14 @@ class Notification( private const val PROGRESS_BAR_HEIGHT: Float = 5f private fun calculateHeight(title: String, description: String): Float { - val titleHeight = Skia.wrappedTextHeight( - Skia.boldFont, title, + val titleHeight = SkiaRenderer.wrappedTextHeight( + SkiaRenderer.boldFont, title, DEFAULT_WIDTH - CONTENT_PADDING * 2, TITLE_FONT_SIZE ) - val descHeight = Skia.wrappedTextHeight( - Skia.boldFont, description, + val descHeight = SkiaRenderer.wrappedTextHeight( + SkiaRenderer.boldFont, description, DEFAULT_WIDTH - CONTENT_PADDING * 2, DESCRIPTION_FONT_SIZE ) diff --git a/src/main/kotlin/org/cobalt/ui/notification/NotificationManager.kt b/src/main/kotlin/org/cobalt/ui/notification/NotificationManager.kt index 646f124..5bdd72a 100644 --- a/src/main/kotlin/org/cobalt/ui/notification/NotificationManager.kt +++ b/src/main/kotlin/org/cobalt/ui/notification/NotificationManager.kt @@ -4,8 +4,8 @@ import kotlin.time.Duration import org.cobalt.event.EventBus import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.RenderEvent -import org.cobalt.util.WindowUtils.windowHeight -import org.cobalt.util.skia.SkiaPIP +import org.cobalt.util.client.WindowUtils.windowHeight +import org.cobalt.util.render.skia.SkiaPIP object NotificationManager { diff --git a/src/main/kotlin/org/cobalt/ui/page/Page.kt b/src/main/kotlin/org/cobalt/ui/page/Page.kt index 3dd7049..57c0fce 100644 --- a/src/main/kotlin/org/cobalt/ui/page/Page.kt +++ b/src/main/kotlin/org/cobalt/ui/page/Page.kt @@ -3,8 +3,8 @@ package org.cobalt.ui.page import org.cobalt.ui.UIComponent import org.cobalt.ui.component.SidebarComponent import org.cobalt.ui.component.TopbarComponent -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.helper.SkiaCorner +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.data.SkiaCorner abstract class Page : UIComponent() { @@ -17,7 +17,7 @@ abstract class Page : UIComponent() { get() = SidebarComponent.height - TopbarComponent.height override fun renderComponent() { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, CORNER_RADIUS, diff --git a/src/main/kotlin/org/cobalt/ui/page/impl/ModulesPage.kt b/src/main/kotlin/org/cobalt/ui/page/impl/ModulesPage.kt index bdf2b4f..74b34f2 100644 --- a/src/main/kotlin/org/cobalt/ui/page/impl/ModulesPage.kt +++ b/src/main/kotlin/org/cobalt/ui/page/impl/ModulesPage.kt @@ -6,8 +6,8 @@ import org.cobalt.ui.component.ModuleComponent import org.cobalt.ui.helper.ScrollHelper import org.cobalt.ui.page.Page import org.cobalt.ui.screen.ConfigScreen -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer object ModulesPage : Page() { @@ -63,7 +63,7 @@ object ModulesPage : Page() { val rightX = xPos + PADDING + ModuleComponent.WIDTH + COLUMN_GAP var rightY = yPos + PADDING + pageOffset - scrollHelper.scrollOffset - Skia.pushScissor(xPos, yPos, width, height) + SkiaRenderer.pushScissor(xPos, yPos, width, height) moduleComponents.forEachIndexed { index, component -> if (index % 2 == 0) { @@ -81,7 +81,7 @@ object ModulesPage : Page() { } } - Skia.popScissor() + SkiaRenderer.popScissor() val contentHeight = maxOf( leftY + scrollHelper.scrollOffset, @@ -93,7 +93,7 @@ object ModulesPage : Page() { override fun mouseScrolled(horizontalAmount: Double, verticalAmount: Double): Boolean { - if (MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (Mouse.isHoveringOver(xPos, yPos, width, height)) { scrollHelper.scroll(verticalAmount) return true } diff --git a/src/main/kotlin/org/cobalt/ui/page/impl/ScriptsPage.kt b/src/main/kotlin/org/cobalt/ui/page/impl/ScriptsPage.kt index 1525854..38d8d20 100644 --- a/src/main/kotlin/org/cobalt/ui/page/impl/ScriptsPage.kt +++ b/src/main/kotlin/org/cobalt/ui/page/impl/ScriptsPage.kt @@ -6,8 +6,8 @@ import org.cobalt.ui.animation.EaseOutAnimation import org.cobalt.ui.component.ScriptComponent import org.cobalt.ui.helper.ScrollHelper import org.cobalt.ui.page.Page -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer object ScriptsPage : Page() { @@ -59,13 +59,13 @@ object ScriptsPage : Page() { val pageOffset = openingOffset.get(-30f, 0f) var currentY = yPos + PADDING + pageOffset - scrollHelper.scrollOffset - Skia.pushScissor(xPos, yPos, width, height) + SkiaRenderer.pushScissor(xPos, yPos, width, height) scriptComponents .groupBy { (_, category) -> category } .forEach { (category, components) -> - Skia.text( - Skia.regularFont, category, + SkiaRenderer.text( + SkiaRenderer.regularFont, category, xPos + PADDING, currentY, CATEGORY_FONT_SIZE, theme.textMuted ) @@ -91,14 +91,14 @@ object ScriptsPage : Page() { currentY = columnY.max() + GROUP_GAP } - Skia.popScissor() + SkiaRenderer.popScissor() val contentHeight = currentY + scrollHelper.scrollOffset - yPos scrollHelper.updateMaxScroll(contentHeight, height) } override fun mouseScrolled(horizontalAmount: Double, verticalAmount: Double): Boolean { - if (MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (Mouse.isHoveringOver(xPos, yPos, width, height)) { scrollHelper.scroll(verticalAmount) return true } diff --git a/src/main/kotlin/org/cobalt/ui/page/impl/ThemesPage.kt b/src/main/kotlin/org/cobalt/ui/page/impl/ThemesPage.kt index 43b1100..c7b9523 100644 --- a/src/main/kotlin/org/cobalt/ui/page/impl/ThemesPage.kt +++ b/src/main/kotlin/org/cobalt/ui/page/impl/ThemesPage.kt @@ -7,8 +7,8 @@ import org.cobalt.ui.component.button.IconButton import org.cobalt.ui.helper.ScrollHelper import org.cobalt.ui.page.Page import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.MouseUtils -import org.cobalt.util.skia.Skia +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer object ThemesPage : Page() { @@ -62,7 +62,7 @@ object ThemesPage : Page() { val contentHeight = pageOffset + PADDING * 2 + rows * ThemeComponent.HEIGHT + (rows - 1) * SPACING scrollHelper.updateMaxScroll(contentHeight, height) - Skia.pushScissor(xPos, yPos, width, height) + SkiaRenderer.pushScissor(xPos, yPos, width, height) themeComponents.forEachIndexed { index, component -> val col = index % COLUMNS @@ -76,7 +76,7 @@ object ThemesPage : Page() { .renderComponent() } - Skia.popScissor() + SkiaRenderer.popScissor() val buttonX = xPos + width - PADDING - reloadButton.width val buttonY = yPos + height - PADDING - reloadButton.height @@ -87,7 +87,7 @@ object ThemesPage : Page() { } override fun mouseScrolled(horizontalAmount: Double, verticalAmount: Double): Boolean { - if (MouseUtils.isHoveringOver(xPos, yPos, width, height)) { + if (Mouse.isHoveringOver(xPos, yPos, width, height)) { scrollHelper.scroll(verticalAmount) return true } diff --git a/src/main/kotlin/org/cobalt/ui/screen/ConfigScreen.kt b/src/main/kotlin/org/cobalt/ui/screen/ConfigScreen.kt index 1544839..b5c48f5 100644 --- a/src/main/kotlin/org/cobalt/ui/screen/ConfigScreen.kt +++ b/src/main/kotlin/org/cobalt/ui/screen/ConfigScreen.kt @@ -13,10 +13,10 @@ import org.cobalt.ui.page.Page import org.cobalt.ui.page.impl.ModulesPage import org.cobalt.ui.theme.Theme import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.WindowUtils.windowHeight -import org.cobalt.util.WindowUtils.windowWidth -import org.cobalt.util.helper.Multithreading -import org.cobalt.util.skia.Skia +import org.cobalt.util.client.WindowUtils.windowHeight +import org.cobalt.util.client.WindowUtils.windowWidth +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.scheduling.Multithreading object ConfigScreen : UIScreen() { @@ -67,7 +67,7 @@ object ConfigScreen : UIScreen() { val centerX = windowWidth / 2f val centerY = windowHeight / 2f - Skia.push() + SkiaRenderer.push() val scale = when { closing -> closeAnim.get(1f, 0f) @@ -76,9 +76,9 @@ object ConfigScreen : UIScreen() { } if (scale != 1f) { - Skia.translate(centerX, centerY) - Skia.scale(scale, scale) - Skia.translate(-centerX, -centerY) + SkiaRenderer.translate(centerX, centerY) + SkiaRenderer.scale(scale, scale) + SkiaRenderer.translate(-centerX, -centerY) } val interfaceWidth = SidebarComponent.width + ModulesPage.width @@ -102,25 +102,25 @@ object ConfigScreen : UIScreen() { .updateBounds(pageX, pageY) .renderComponent() - Skia.roundedOutline( + SkiaRenderer.roundedOutline( pageX, pageY, interfaceWidth, interfaceHeight, 1f, 10f, theme.border ) - Skia.line( + SkiaRenderer.line( startX, pageY, startX, pageY + ModulesPage.height, 1f, theme.border ) - Skia.line( + SkiaRenderer.line( startX, startY, startX + TopbarComponent.width, startY, 1f, theme.border ) - Skia.pop() + SkiaRenderer.pop() if (closing && !closeAnim.isAnimating()) { closing = false diff --git a/src/main/kotlin/org/cobalt/ui/screen/HudEditorScreen.kt b/src/main/kotlin/org/cobalt/ui/screen/HudEditorScreen.kt index dcd1234..38429c2 100644 --- a/src/main/kotlin/org/cobalt/ui/screen/HudEditorScreen.kt +++ b/src/main/kotlin/org/cobalt/ui/screen/HudEditorScreen.kt @@ -9,13 +9,13 @@ import org.cobalt.ui.helper.DragHandler import org.cobalt.ui.helper.SnapHelper import org.cobalt.ui.theme.Theme import org.cobalt.ui.theme.ThemeManager -import org.cobalt.util.MouseUtils -import org.cobalt.util.WindowUtils.scaleX -import org.cobalt.util.WindowUtils.scaleY -import org.cobalt.util.WindowUtils.windowHeight -import org.cobalt.util.WindowUtils.windowWidth -import org.cobalt.util.helper.Multithreading -import org.cobalt.util.skia.Skia +import org.cobalt.util.client.WindowUtils.scaleX +import org.cobalt.util.client.WindowUtils.scaleY +import org.cobalt.util.client.WindowUtils.windowHeight +import org.cobalt.util.client.WindowUtils.windowWidth +import org.cobalt.util.input.Mouse +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.scheduling.Multithreading object HudEditorScreen : UIScreen() { @@ -33,19 +33,19 @@ object HudEditorScreen : UIScreen() { override fun renderSkia() { modules.forEach { module -> - Skia.push() + SkiaRenderer.push() val renderX = module.xPos * scaleX val renderY = module.yPos * scaleY val finalScale = module.scale * scaleY - Skia.translate(renderX, renderY) - Skia.scale(finalScale, finalScale) - Skia.translate(-module.xPos, -module.yPos) + SkiaRenderer.translate(renderX, renderY) + SkiaRenderer.scale(finalScale, finalScale) + SkiaRenderer.translate(-module.xPos, -module.yPos) drawRenderableModule(module) - Skia.pop() + SkiaRenderer.pop() } drawSnapGuides() @@ -58,9 +58,9 @@ object HudEditorScreen : UIScreen() { snapHelper.activeGuides.forEach { guide -> if (guide.isVertical) { - Skia.line(guide.position, 0f, guide.position, windowHeight, 1f, theme.accentPrimary) + SkiaRenderer.line(guide.position, 0f, guide.position, windowHeight, 1f, theme.accentPrimary) } else { - Skia.line(0f, guide.position, windowWidth, guide.position, 1f, theme.accentPrimary) + SkiaRenderer.line(0f, guide.position, windowWidth, guide.position, 1f, theme.accentPrimary) } } } @@ -75,11 +75,11 @@ object HudEditorScreen : UIScreen() { val isSelected = module == selectedModule - Skia.outline(x, y, width, height, 1f, if (isSelected) theme.accentPrimary else theme.border) + SkiaRenderer.outline(x, y, width, height, 1f, if (isSelected) theme.accentPrimary else theme.border) if (isSelected) { val squareOffset = SQUARE_SIZE / 2.0f - Skia.rect( + SkiaRenderer.rect( x + width - squareOffset, y + height - squareOffset, SQUARE_SIZE, SQUARE_SIZE, theme.accentPrimary @@ -110,7 +110,7 @@ object HudEditorScreen : UIScreen() { return@firstOrNull true } - if (MouseUtils.isHoveringOver(renderX, renderY, scaledWidth, scaledHeight)) { + if (Mouse.isHoveringOver(renderX, renderY, scaledWidth, scaledHeight)) { dragHandler.startMove(renderX, renderY) return@firstOrNull true } @@ -152,7 +152,7 @@ object HudEditorScreen : UIScreen() { val scaledWidth = candidate.width * candidate.scale * resScale val scaledHeight = candidate.height * candidate.scale * resScale - MouseUtils.isHoveringOver(renderX, renderY, scaledWidth, scaledHeight) + Mouse.isHoveringOver(renderX, renderY, scaledWidth, scaledHeight) } ?: return super.mouseScrolled(x, y, scrollX, scrollY) selectedModule = module diff --git a/src/main/kotlin/org/cobalt/ui/theme/ThemeManager.kt b/src/main/kotlin/org/cobalt/ui/theme/ThemeManager.kt index 0246797..abb8b51 100644 --- a/src/main/kotlin/org/cobalt/ui/theme/ThemeManager.kt +++ b/src/main/kotlin/org/cobalt/ui/theme/ThemeManager.kt @@ -5,7 +5,7 @@ import java.awt.Color import java.nio.file.Files import kotlin.io.path.notExists import org.cobalt.Cobalt.configDir -import org.cobalt.util.config.BasicConfig +import org.cobalt.util.config.SimpleConfig object ThemeManager { @@ -19,7 +19,7 @@ object ThemeManager { "/assets/cobalt/themes/arcticFrost.json", ) - private val settingsConfig = BasicConfig( + private val settingsConfig = SimpleConfig( configDir.resolve("settings.json").toString(), Settings::class.java ) diff --git a/src/main/kotlin/org/cobalt/util/ColorUtils.kt b/src/main/kotlin/org/cobalt/util/ColorUtils.kt deleted file mode 100644 index 8b7f23d..0000000 --- a/src/main/kotlin/org/cobalt/util/ColorUtils.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.cobalt.util - -import kotlin.math.roundToInt -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.MutableComponent -import net.minecraft.network.chat.Style -import net.minecraft.network.chat.TextColor -import org.cobalt.dsl.alpha -import org.cobalt.dsl.blue -import org.cobalt.dsl.green -import org.cobalt.dsl.red - -object ColorUtils { - - @JvmStatic - fun buildTextGradient( - text: String, - startColor: Int, - endColor: Int, - baseStyle: Style = Style.EMPTY, - ): MutableComponent { - val result = Component.empty() - val textLength = text.length - - if (textLength <= 1) { - return Component - .literal(text) - .setStyle(baseStyle.withColor(TextColor.fromRgb(startColor))) - } - - for (index in text.indices) { - val denominator = (textLength - 1).toDouble() - val ratio = index.toDouble() / denominator - val red = (startColor.red + ratio * (endColor.red - startColor.red)).roundToInt() - val green = (startColor.green + ratio * (endColor.green - startColor.green)).roundToInt() - val blue = (startColor.blue + ratio * (endColor.blue - startColor.blue)).roundToInt() - val interpolatedColor = (red shl 16) or (green shl 8) or blue - - val coloredChar = Component.literal(text[index].toString()) - .setStyle(baseStyle.withColor(TextColor.fromRgb(interpolatedColor))) - - result.append(coloredChar) - } - - return result - } - - @JvmStatic - fun getRed(colorRgb: Int) = colorRgb.red - - @JvmStatic - fun getGreen(colorRgb: Int) = colorRgb.green - - @JvmStatic - fun getBlue(colorRgb: Int) = colorRgb.blue - - @JvmStatic - fun getAlpha(colorRgb: Int) = colorRgb.alpha - -} diff --git a/src/main/kotlin/org/cobalt/util/BlockUtils.kt b/src/main/kotlin/org/cobalt/util/block/BlockUtils.kt similarity index 84% rename from src/main/kotlin/org/cobalt/util/BlockUtils.kt rename to src/main/kotlin/org/cobalt/util/block/BlockUtils.kt index b96769f..adc7a7c 100644 --- a/src/main/kotlin/org/cobalt/util/BlockUtils.kt +++ b/src/main/kotlin/org/cobalt/util/block/BlockUtils.kt @@ -1,21 +1,32 @@ -package org.cobalt.util +package org.cobalt.util.block import net.minecraft.core.BlockPos import net.minecraft.world.level.EmptyBlockGetter -import net.minecraft.world.level.block.AmethystClusterBlock -import net.minecraft.world.level.block.BambooStalkBlock -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.PointedDripstoneBlock -import net.minecraft.world.level.block.ScaffoldingBlock -import net.minecraft.world.level.block.ShulkerBoxBlock -import net.minecraft.world.level.block.SlabBlock +import net.minecraft.world.level.block.* import net.minecraft.world.level.block.piston.MovingPistonBlock import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.properties.SlabType import net.minecraft.world.level.material.FlowingFluid import net.minecraft.world.level.material.Fluids +import net.minecraft.world.phys.AABB +import net.minecraft.world.phys.Vec3 import org.cobalt.pathfinder.helper.BlockStateAccessor +fun BlockPos.centerVec(): Vec3 { + return Vec3(x + 0.5, y + 0.5, z + 0.5) +} + +fun Vec3.smallBox(): AABB { + return AABB( + x - 0.25, + y - 0.25, + z - 0.25, + x + 0.25, + y + 0.25, + z + 0.25 + ) +} + object BlockUtils { @JvmStatic diff --git a/src/main/kotlin/org/cobalt/util/FrustumUtils.kt b/src/main/kotlin/org/cobalt/util/block/FrustumUtils.kt similarity index 95% rename from src/main/kotlin/org/cobalt/util/FrustumUtils.kt rename to src/main/kotlin/org/cobalt/util/block/FrustumUtils.kt index e2dd466..1a89e6d 100644 --- a/src/main/kotlin/org/cobalt/util/FrustumUtils.kt +++ b/src/main/kotlin/org/cobalt/util/block/FrustumUtils.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.block import net.minecraft.client.renderer.culling.Frustum import net.minecraft.world.phys.AABB diff --git a/src/main/kotlin/org/cobalt/util/helper/ChatFormatter.kt b/src/main/kotlin/org/cobalt/util/chat/ChatFormatter.kt similarity index 98% rename from src/main/kotlin/org/cobalt/util/helper/ChatFormatter.kt rename to src/main/kotlin/org/cobalt/util/chat/ChatFormatter.kt index 2047737..5b89849 100644 --- a/src/main/kotlin/org/cobalt/util/helper/ChatFormatter.kt +++ b/src/main/kotlin/org/cobalt/util/chat/ChatFormatter.kt @@ -1,11 +1,10 @@ -package org.cobalt.util.helper +package org.cobalt.util.chat import net.minecraft.ChatFormatting import net.minecraft.network.chat.Component import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.Style import net.minecraft.network.chat.TextColor -import org.cobalt.util.ColorUtils object ChatFormatter { @@ -215,7 +214,7 @@ object ChatFormatter { flush() result.append( - ColorUtils.buildTextGradient( + ChatUtils.buildGradientComponent( text.substring(tagEnd + 1, closeStart), gradient.first, gradient.second, diff --git a/src/main/kotlin/org/cobalt/util/ChatUtils.kt b/src/main/kotlin/org/cobalt/util/chat/ChatUtils.kt similarity index 64% rename from src/main/kotlin/org/cobalt/util/ChatUtils.kt rename to src/main/kotlin/org/cobalt/util/chat/ChatUtils.kt index f37e6e7..cc6637b 100644 --- a/src/main/kotlin/org/cobalt/util/ChatUtils.kt +++ b/src/main/kotlin/org/cobalt/util/chat/ChatUtils.kt @@ -1,13 +1,23 @@ -package org.cobalt.util +package org.cobalt.util.chat +import kotlin.math.roundToInt +import net.minecraft.network.chat.Component import net.minecraft.network.chat.MutableComponent +import net.minecraft.network.chat.Style +import net.minecraft.network.chat.TextColor import org.cobalt.Cobalt import org.cobalt.Cobalt.minecraft import org.cobalt.Cobalt.runOnClientThread import org.cobalt.module.impl.misc.Debug -import org.cobalt.util.helper.ChatFormatter +import org.cobalt.util.color.blue +import org.cobalt.util.color.green +import org.cobalt.util.color.red import org.slf4j.LoggerFactory +enum class MessageType { + DEFAULT, RAW, DEBUG +} + object ChatUtils { private val logger = LoggerFactory.getLogger(this::class.java) @@ -76,6 +86,39 @@ object ChatUtils { } } + @JvmStatic + fun buildGradientComponent( + text: String, + startColor: Int, + endColor: Int, + baseStyle: Style = Style.EMPTY, + ): MutableComponent { + val result = Component.empty() + val textLength = text.length + + if (textLength <= 1) { + return Component + .literal(text) + .setStyle(baseStyle.withColor(TextColor.fromRgb(startColor))) + } + + for (index in text.indices) { + val denominator = (textLength - 1).toDouble() + val ratio = index.toDouble() / denominator + val red = (startColor.red + ratio * (endColor.red - startColor.red)).roundToInt() + val green = (startColor.green + ratio * (endColor.green - startColor.green)).roundToInt() + val blue = (startColor.blue + ratio * (endColor.blue - startColor.blue)).roundToInt() + val interpolatedColor = (red shl 16) or (green shl 8) or blue + + val coloredChar = Component.literal(text[index].toString()) + .setStyle(baseStyle.withColor(TextColor.fromRgb(interpolatedColor))) + + result.append(coloredChar) + } + + return result + } + private fun addToChat(component: MutableComponent) { runOnClientThread { val player = minecraft.player @@ -90,9 +133,3 @@ object ChatUtils { } } - -enum class MessageType { - DEFAULT, - RAW, - DEBUG -} diff --git a/src/main/kotlin/org/cobalt/util/PlayerUtils.kt b/src/main/kotlin/org/cobalt/util/client/PlayerUtils.kt similarity index 97% rename from src/main/kotlin/org/cobalt/util/PlayerUtils.kt rename to src/main/kotlin/org/cobalt/util/client/PlayerUtils.kt index 0184e05..25e99fc 100644 --- a/src/main/kotlin/org/cobalt/util/PlayerUtils.kt +++ b/src/main/kotlin/org/cobalt/util/client/PlayerUtils.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.client import kotlin.math.ceil import kotlin.math.floor @@ -7,7 +7,7 @@ import net.minecraft.core.BlockPos import net.minecraft.world.phys.Vec3 import org.cobalt.Cobalt.minecraft import org.cobalt.Cobalt.runOnClientThread -import org.cobalt.util.rotation.Rotation +import org.cobalt.util.rotation.data.Rotation object PlayerUtils { diff --git a/src/main/kotlin/org/cobalt/util/WindowUtils.kt b/src/main/kotlin/org/cobalt/util/client/WindowUtils.kt similarity index 92% rename from src/main/kotlin/org/cobalt/util/WindowUtils.kt rename to src/main/kotlin/org/cobalt/util/client/WindowUtils.kt index 0fa9214..5c50343 100644 --- a/src/main/kotlin/org/cobalt/util/WindowUtils.kt +++ b/src/main/kotlin/org/cobalt/util/client/WindowUtils.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.client import org.cobalt.Cobalt.minecraft diff --git a/src/main/kotlin/org/cobalt/dsl/Color.kt b/src/main/kotlin/org/cobalt/util/color/ColorExtensions.kt similarity index 77% rename from src/main/kotlin/org/cobalt/dsl/Color.kt rename to src/main/kotlin/org/cobalt/util/color/ColorExtensions.kt index 28f5064..6e3180a 100644 --- a/src/main/kotlin/org/cobalt/dsl/Color.kt +++ b/src/main/kotlin/org/cobalt/util/color/ColorExtensions.kt @@ -1,4 +1,6 @@ -package org.cobalt.dsl +@file:JvmName("ColorUtils") + +package org.cobalt.util.color import java.awt.Color @@ -12,7 +14,7 @@ inline val Int.blue get() = this and 0xFF inline val Int.alpha - get() = this shr 24 and 0xFF + get() = this ushr 24 fun Color.updateAlpha(alpha: Int): Color { return Color(red, green, blue, alpha) diff --git a/src/main/kotlin/org/cobalt/util/config/BasicConfig.kt b/src/main/kotlin/org/cobalt/util/config/SimpleConfig.kt similarity index 87% rename from src/main/kotlin/org/cobalt/util/config/BasicConfig.kt rename to src/main/kotlin/org/cobalt/util/config/SimpleConfig.kt index af58918..4f723be 100644 --- a/src/main/kotlin/org/cobalt/util/config/BasicConfig.kt +++ b/src/main/kotlin/org/cobalt/util/config/SimpleConfig.kt @@ -3,7 +3,7 @@ package org.cobalt.util.config import com.google.gson.GsonBuilder import java.io.File -class BasicConfig(path: String, private val clazz: Class) { +class SimpleConfig(path: String, private val clazz: Class) { private val file = File(path) diff --git a/src/main/kotlin/org/cobalt/util/KeybindUtils.kt b/src/main/kotlin/org/cobalt/util/input/Keyboard.kt similarity index 97% rename from src/main/kotlin/org/cobalt/util/KeybindUtils.kt rename to src/main/kotlin/org/cobalt/util/input/Keyboard.kt index bbcc8d0..2f0d0da 100644 --- a/src/main/kotlin/org/cobalt/util/KeybindUtils.kt +++ b/src/main/kotlin/org/cobalt/util/input/Keyboard.kt @@ -1,11 +1,11 @@ -package org.cobalt.util +package org.cobalt.util.input import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.KeyMapping import org.cobalt.Cobalt.minecraft import org.lwjgl.glfw.GLFW -object KeybindUtils { +object Keyboard { @JvmStatic val allKeys = arrayOf( diff --git a/src/main/kotlin/org/cobalt/util/MouseUtils.kt b/src/main/kotlin/org/cobalt/util/input/Mouse.kt similarity index 96% rename from src/main/kotlin/org/cobalt/util/MouseUtils.kt rename to src/main/kotlin/org/cobalt/util/input/Mouse.kt index bb5c2d1..8e9bce0 100644 --- a/src/main/kotlin/org/cobalt/util/MouseUtils.kt +++ b/src/main/kotlin/org/cobalt/util/input/Mouse.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.input import net.minecraft.client.input.MouseButtonInfo import org.cobalt.Cobalt.minecraft @@ -6,7 +6,19 @@ import org.cobalt.Cobalt.runOnClientThread import org.cobalt.mixin.client.MouseHandlerAccessor import org.lwjgl.glfw.GLFW -object MouseUtils { +enum class MouseButton { + LEFT, RIGHT, MIDDLE +} + +enum class MouseAction { + PRESS, RELEASE +} + +enum class MouseMode { + DEFAULT, UNGRAB_MOUSE, LOCK_MOUSE +} + +object Mouse { @JvmStatic var mouseMode: MouseMode = MouseMode.DEFAULT @@ -54,15 +66,3 @@ object MouseUtils { } } - -enum class MouseButton { - LEFT, RIGHT, MIDDLE -} - -enum class MouseAction { - PRESS, RELEASE -} - -enum class MouseMode { - DEFAULT, UNGRAB_MOUSE, LOCK_MOUSE -} diff --git a/src/main/kotlin/org/cobalt/util/InventoryUtils.kt b/src/main/kotlin/org/cobalt/util/inventory/InventoryUtils.kt similarity index 56% rename from src/main/kotlin/org/cobalt/util/InventoryUtils.kt rename to src/main/kotlin/org/cobalt/util/inventory/InventoryUtils.kt index 2509cc0..fb7bc15 100644 --- a/src/main/kotlin/org/cobalt/util/InventoryUtils.kt +++ b/src/main/kotlin/org/cobalt/util/inventory/InventoryUtils.kt @@ -1,8 +1,10 @@ -package org.cobalt.util +package org.cobalt.util.inventory import net.minecraft.world.inventory.ContainerInput import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack import org.cobalt.Cobalt.minecraft +import org.cobalt.util.input.MouseButton object InventoryUtils { @@ -46,21 +48,9 @@ object InventoryUtils { val player = minecraft.player ?: return -1 val inventory = player.inventory - for (i in 0..8) { - val stack = inventory.getItem(i) - - if (stack.isEmpty) { - continue - } - - val displayName = stack.hoverName.string - - if (displayName.contains(name, ignoreCase = true)) { - return i - } + return findSlot(9, { inventory.getItem(it) }) { + it.hoverName.string.contains(name, ignoreCase = true) } - - return -1 } @JvmStatic @@ -68,21 +58,9 @@ object InventoryUtils { val player = minecraft.player ?: return -1 val inventory = player.inventory - for (slot in 0..8) { - val stack = inventory.getItem(slot) - - if (stack.isEmpty) { - continue - } - - for (line in ItemUtils.getLoreLines(stack)) { - if (line.string.contains(lore, ignoreCase = true)) { - return slot - } - } + return findSlot(9, { inventory.getItem(it) }) { stack -> + ItemUtils.getLoreLines(stack).any { it.string.contains(lore, ignoreCase = true) } } - - return -1 } @JvmStatic @@ -90,19 +68,9 @@ object InventoryUtils { val player = minecraft.player ?: return -1 val inventory = player.inventory - for (slot in 0 until inventory.containerSize) { - val stack = inventory.getItem(slot) - - if (stack.isEmpty) { - continue - } - - if (stack.hoverName.string.contains(name, ignoreCase = true)) { - return slot - } + return findSlot(inventory.containerSize, { inventory.getItem(it) }) { + it.hoverName.string.contains(name, ignoreCase = true) } - - return -1 } @JvmStatic @@ -110,19 +78,7 @@ object InventoryUtils { val player = minecraft.player ?: return -1 val inventory = player.inventory - for (slot in 0 until inventory.containerSize) { - val stack = inventory.getItem(slot) - - if (stack.isEmpty) { - continue - } - - if (stack.item == item) { - return slot - } - } - - return -1 + return findSlot(inventory.containerSize, { inventory.getItem(it) }) { it.item == item } } @JvmStatic @@ -131,19 +87,9 @@ object InventoryUtils { val menu = player.containerMenu val containerSlots = menu.slots.size - player.inventory.nonEquipmentItems.size - for (slot in 0 until containerSlots) { - val stack = menu.getSlot(slot).item - - if (stack.isEmpty) { - continue - } - - if (stack.hoverName.string.contains(name, ignoreCase = true)) { - return slot - } + return findSlot(containerSlots, { menu.getSlot(it).item }) { + it.hoverName.string.contains(name, ignoreCase = true) } - - return -1 } @JvmStatic @@ -152,19 +98,7 @@ object InventoryUtils { val menu = player.containerMenu val containerSlots = menu.slots.size - player.inventory.nonEquipmentItems.size - for (slot in 0 until containerSlots) { - val stack = menu.getSlot(slot).item - - if (stack.isEmpty) { - continue - } - - if (stack.item == item) { - return slot - } - } - - return -1 + return findSlot(containerSlots, { menu.getSlot(it).item }) { it.item == item } } @JvmStatic @@ -172,17 +106,25 @@ object InventoryUtils { val player = minecraft.player ?: return -1 val inventory = player.inventory - for (slot in 0 until inventory.containerSize) { - val stack = inventory.getItem(slot) + return findSlot(inventory.containerSize, { inventory.getItem(it) }) { stack -> + ItemUtils.getLoreLines(stack).any { it.string.contains(lore, ignoreCase = true) } + } + } + + private inline fun findSlot( + size: Int, + getStack: (Int) -> ItemStack, + predicate: (ItemStack) -> Boolean, + ): Int { + for (slot in 0 until size) { + val stack = getStack(slot) if (stack.isEmpty) { continue } - for (line in ItemUtils.getLoreLines(stack)) { - if (line.string.contains(lore, ignoreCase = true)) { - return slot - } + if (predicate(stack)) { + return slot } } diff --git a/src/main/kotlin/org/cobalt/util/ItemUtils.kt b/src/main/kotlin/org/cobalt/util/inventory/ItemUtils.kt similarity index 89% rename from src/main/kotlin/org/cobalt/util/ItemUtils.kt rename to src/main/kotlin/org/cobalt/util/inventory/ItemUtils.kt index f187525..e16f42d 100644 --- a/src/main/kotlin/org/cobalt/util/ItemUtils.kt +++ b/src/main/kotlin/org/cobalt/util/inventory/ItemUtils.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.inventory import net.minecraft.core.component.DataComponents import net.minecraft.network.chat.Component diff --git a/src/main/kotlin/org/cobalt/util/WorldRenderUtils.kt b/src/main/kotlin/org/cobalt/util/render/GizmoRenderer.kt similarity index 97% rename from src/main/kotlin/org/cobalt/util/WorldRenderUtils.kt rename to src/main/kotlin/org/cobalt/util/render/GizmoRenderer.kt index f50e780..6b5e8a1 100644 --- a/src/main/kotlin/org/cobalt/util/WorldRenderUtils.kt +++ b/src/main/kotlin/org/cobalt/util/render/GizmoRenderer.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.render import java.awt.Color import net.minecraft.core.BlockPos @@ -10,7 +10,7 @@ import net.minecraft.world.phys.AABB import net.minecraft.world.phys.Vec3 import org.cobalt.Cobalt -object WorldRenderUtils { +object GizmoRenderer { @JvmStatic fun drawBlockPos( @@ -28,6 +28,7 @@ object WorldRenderUtils { ) } + @JvmStatic fun drawEntityOutline( entity: Entity, color: Color, diff --git a/src/main/kotlin/org/cobalt/util/skia/Skia.kt b/src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt similarity index 95% rename from src/main/kotlin/org/cobalt/util/skia/Skia.kt rename to src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt index b14e007..0c59428 100644 --- a/src/main/kotlin/org/cobalt/util/skia/Skia.kt +++ b/src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt @@ -1,6 +1,6 @@ @file:Suppress("TooManyFunctions") -package org.cobalt.util.skia +package org.cobalt.util.render import io.github.humbleui.skija.* import io.github.humbleui.skija.svg.SVGDOM @@ -9,12 +9,11 @@ import io.github.humbleui.types.RRect import io.github.humbleui.types.Rect import java.awt.Color import kotlin.math.max -import org.cobalt.util.skia.helper.SkiaCorner -import org.cobalt.util.skia.helper.SkiaFont -import org.cobalt.util.skia.helper.SkiaImage -import org.joml.Matrix3x2fc +import org.cobalt.util.render.skia.data.SkiaCorner +import org.cobalt.util.render.skia.data.SkiaFont +import org.cobalt.util.render.skia.data.SkiaImage -object Skia { +object SkiaRenderer { private val imageCache = HashMap() private val fonts = HashMap() @@ -58,17 +57,6 @@ object Skia { canvas().translate(x, y) } - @JvmStatic - fun transform(matrix: Matrix3x2fc) { - val skiaMatrix = Matrix33( - matrix.m00(), matrix.m10(), matrix.m20(), - matrix.m01(), matrix.m11(), matrix.m21(), - 0f, 0f, 1f - ) - - canvas().concat(skiaMatrix) - } - @JvmStatic fun rotate(degrees: Float) { canvas().rotate(degrees) diff --git a/src/main/kotlin/org/cobalt/util/ResourceUtils.kt b/src/main/kotlin/org/cobalt/util/render/skia/ResourceUtils.kt similarity index 96% rename from src/main/kotlin/org/cobalt/util/ResourceUtils.kt rename to src/main/kotlin/org/cobalt/util/render/skia/ResourceUtils.kt index 7c15fb3..a408fda 100644 --- a/src/main/kotlin/org/cobalt/util/ResourceUtils.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/ResourceUtils.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.render.skia import java.io.File import java.io.FileNotFoundException diff --git a/src/main/kotlin/org/cobalt/util/skia/SkiaBootstrap.kt b/src/main/kotlin/org/cobalt/util/render/skia/SkiaBootstrap.kt similarity index 98% rename from src/main/kotlin/org/cobalt/util/skia/SkiaBootstrap.kt rename to src/main/kotlin/org/cobalt/util/render/skia/SkiaBootstrap.kt index ead7ee6..5c6e955 100644 --- a/src/main/kotlin/org/cobalt/util/skia/SkiaBootstrap.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/SkiaBootstrap.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.skia +package org.cobalt.util.render.skia import java.net.URI import java.nio.file.Files diff --git a/src/main/kotlin/org/cobalt/util/skia/SkiaPIP.kt b/src/main/kotlin/org/cobalt/util/render/skia/SkiaPIP.kt similarity index 96% rename from src/main/kotlin/org/cobalt/util/skia/SkiaPIP.kt rename to src/main/kotlin/org/cobalt/util/render/skia/SkiaPIP.kt index c76a5f0..e036c16 100644 --- a/src/main/kotlin/org/cobalt/util/skia/SkiaPIP.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/SkiaPIP.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.skia +package org.cobalt.util.render.skia import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack @@ -8,7 +8,7 @@ import net.minecraft.client.gui.render.pip.PictureInPictureRenderer import net.minecraft.client.renderer.SubmitNodeCollector import net.minecraft.client.renderer.state.gui.pip.PictureInPictureRenderState import org.cobalt.Cobalt.minecraft -import org.cobalt.util.skia.surface.SkiaSurface +import org.cobalt.util.render.skia.surface.SkiaSurface import org.joml.Matrix3x2f class SkiaPIP : PictureInPictureRenderer() { diff --git a/src/main/kotlin/org/cobalt/util/skia/helper/SkiaCorner.kt b/src/main/kotlin/org/cobalt/util/render/skia/data/SkiaCorner.kt similarity index 92% rename from src/main/kotlin/org/cobalt/util/skia/helper/SkiaCorner.kt rename to src/main/kotlin/org/cobalt/util/render/skia/data/SkiaCorner.kt index 42f8978..18538ff 100644 --- a/src/main/kotlin/org/cobalt/util/skia/helper/SkiaCorner.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/data/SkiaCorner.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.skia.helper +package org.cobalt.util.render.skia.data enum class SkiaCorner { diff --git a/src/main/kotlin/org/cobalt/util/skia/helper/SkiaFont.kt b/src/main/kotlin/org/cobalt/util/render/skia/data/SkiaFont.kt similarity index 57% rename from src/main/kotlin/org/cobalt/util/skia/helper/SkiaFont.kt rename to src/main/kotlin/org/cobalt/util/render/skia/data/SkiaFont.kt index 40cb49b..c10aad6 100644 --- a/src/main/kotlin/org/cobalt/util/skia/helper/SkiaFont.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/data/SkiaFont.kt @@ -1,6 +1,6 @@ -package org.cobalt.util.skia.helper +package org.cobalt.util.render.skia.data -import org.cobalt.util.ResourceUtils +import org.cobalt.util.render.skia.ResourceUtils data class SkiaFont(val location: String) { diff --git a/src/main/kotlin/org/cobalt/util/skia/helper/SkiaImage.kt b/src/main/kotlin/org/cobalt/util/render/skia/data/SkiaImage.kt similarity index 67% rename from src/main/kotlin/org/cobalt/util/skia/helper/SkiaImage.kt rename to src/main/kotlin/org/cobalt/util/render/skia/data/SkiaImage.kt index 5388fb2..97c1451 100644 --- a/src/main/kotlin/org/cobalt/util/skia/helper/SkiaImage.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/data/SkiaImage.kt @@ -1,6 +1,6 @@ -package org.cobalt.util.skia.helper +package org.cobalt.util.render.skia.data -import org.cobalt.util.ResourceUtils +import org.cobalt.util.render.skia.ResourceUtils class SkiaImage( val location: String, diff --git a/src/main/kotlin/org/cobalt/util/skia/surface/GlSurface.kt b/src/main/kotlin/org/cobalt/util/render/skia/surface/GlSurface.kt similarity index 95% rename from src/main/kotlin/org/cobalt/util/skia/surface/GlSurface.kt rename to src/main/kotlin/org/cobalt/util/render/skia/surface/GlSurface.kt index 51c36d9..63ccbb9 100644 --- a/src/main/kotlin/org/cobalt/util/skia/surface/GlSurface.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/surface/GlSurface.kt @@ -1,10 +1,10 @@ -package org.cobalt.util.skia.surface +package org.cobalt.util.render.skia.surface import com.mojang.blaze3d.opengl.GlStateManager import com.mojang.blaze3d.opengl.GlTexture import com.mojang.blaze3d.textures.GpuTexture import io.github.humbleui.skija.* -import org.cobalt.util.skia.Skia +import org.cobalt.util.render.SkiaRenderer import org.lwjgl.opengl.GL11C import org.lwjgl.opengl.GL30C import org.lwjgl.opengl.GL33C @@ -42,12 +42,12 @@ internal class GlSurface : SkiaSurface { val skijaSurface = surfaceFor(width, height, colorTexId) - Skia.beginFrame(skijaSurface.canvas) + SkiaRenderer.beginFrame(skijaSurface.canvas) try { draw(skijaSurface.canvas) } finally { - Skia.endFrame() + SkiaRenderer.endFrame() } directContext.flushAndSubmit(skijaSurface, true) diff --git a/src/main/kotlin/org/cobalt/util/skia/surface/SkiaSurface.kt b/src/main/kotlin/org/cobalt/util/render/skia/surface/SkiaSurface.kt similarity index 93% rename from src/main/kotlin/org/cobalt/util/skia/surface/SkiaSurface.kt rename to src/main/kotlin/org/cobalt/util/render/skia/surface/SkiaSurface.kt index 778abaf..e2367a8 100644 --- a/src/main/kotlin/org/cobalt/util/skia/surface/SkiaSurface.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/surface/SkiaSurface.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.skia.surface +package org.cobalt.util.render.skia.surface import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.textures.GpuTexture diff --git a/src/main/kotlin/org/cobalt/util/skia/surface/VulkanSurface.kt b/src/main/kotlin/org/cobalt/util/render/skia/surface/VulkanSurface.kt similarity index 95% rename from src/main/kotlin/org/cobalt/util/skia/surface/VulkanSurface.kt rename to src/main/kotlin/org/cobalt/util/render/skia/surface/VulkanSurface.kt index bbe4d10..26e881f 100644 --- a/src/main/kotlin/org/cobalt/util/skia/surface/VulkanSurface.kt +++ b/src/main/kotlin/org/cobalt/util/render/skia/surface/VulkanSurface.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.skia.surface +package org.cobalt.util.render.skia.surface import com.mojang.blaze3d.GpuFormat import com.mojang.blaze3d.systems.RenderSystem @@ -7,7 +7,7 @@ import com.mojang.blaze3d.vulkan.VulkanDevice import com.mojang.blaze3d.vulkan.VulkanGpuTexture import io.github.humbleui.skija.* import org.cobalt.mixin.mojang.GpuDeviceAccessor -import org.cobalt.util.skia.Skia +import org.cobalt.util.render.SkiaRenderer import org.lwjgl.vulkan.VK import org.lwjgl.vulkan.VK12.* @@ -38,12 +38,12 @@ internal class VulkanSurface : SkiaSurface { val skijaSurface = surfaceFor(directContext, width, height, vkImage, vkFormat) - Skia.beginFrame(skijaSurface.canvas) + SkiaRenderer.beginFrame(skijaSurface.canvas) try { draw(skijaSurface.canvas) } finally { - Skia.endFrame() + SkiaRenderer.endFrame() } directContext.flushAndSubmit(skijaSurface, false) diff --git a/src/main/kotlin/org/cobalt/util/RotationUtils.kt b/src/main/kotlin/org/cobalt/util/rotation/RotationMath.kt similarity index 83% rename from src/main/kotlin/org/cobalt/util/RotationUtils.kt rename to src/main/kotlin/org/cobalt/util/rotation/RotationMath.kt index 1b72c3d..4bc5fb1 100644 --- a/src/main/kotlin/org/cobalt/util/RotationUtils.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/RotationMath.kt @@ -1,15 +1,14 @@ -package org.cobalt.util +package org.cobalt.util.rotation import kotlin.math.abs import kotlin.math.atan2 import kotlin.math.sqrt import net.minecraft.util.Mth import net.minecraft.world.phys.Vec3 -import org.cobalt.Cobalt.minecraft -import org.cobalt.util.rotation.Rotation +import org.cobalt.Cobalt +import org.cobalt.util.rotation.data.Rotation - -object RotationUtils { +object RotationMath { @JvmStatic val gcd: Double @@ -18,7 +17,7 @@ object RotationUtils { @JvmStatic val mouseSensitivityFactor: Double get() { - val sensitivity = minecraft.options.sensitivity().get() + val sensitivity = Cobalt.minecraft.options.sensitivity().get() val f = sensitivity * 0.6f + 0.2f return f * f * f * 8.0 } @@ -51,7 +50,7 @@ object RotationUtils { @JvmStatic fun getRotation(end: Vec3): Rotation { - val start = minecraft.player?.eyePosition ?: return Rotation.ZERO + val start = Cobalt.minecraft.player?.eyePosition ?: return Rotation.ZERO return getRotation(start, end) } diff --git a/src/main/kotlin/org/cobalt/util/rotation/Rotation.kt b/src/main/kotlin/org/cobalt/util/rotation/data/Rotation.kt similarity index 73% rename from src/main/kotlin/org/cobalt/util/rotation/Rotation.kt rename to src/main/kotlin/org/cobalt/util/rotation/data/Rotation.kt index bd822b8..4bb6140 100644 --- a/src/main/kotlin/org/cobalt/util/rotation/Rotation.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/data/Rotation.kt @@ -1,8 +1,8 @@ -package org.cobalt.util.rotation +package org.cobalt.util.rotation.data import kotlin.math.roundToInt -import org.cobalt.util.PlayerUtils -import org.cobalt.util.RotationUtils +import org.cobalt.util.client.PlayerUtils +import org.cobalt.util.rotation.RotationMath data class Rotation( val yaw: Float, @@ -17,7 +17,7 @@ data class Rotation( return this } - val gcd = RotationUtils.gcd + val gcd = RotationMath.gcd val diff = currentRotation.rotationDeltaTo(this) val g1 = (diff.deltaYaw / gcd).roundToInt() * gcd @@ -31,8 +31,8 @@ data class Rotation( fun rotationDeltaTo(other: Rotation): RotationDelta { return RotationDelta( - RotationUtils.angleDifference(other.yaw, this.yaw), - RotationUtils.angleDifference(other.pitch, this.pitch) + RotationMath.angleDifference(other.yaw, this.yaw), + RotationMath.angleDifference(other.pitch, this.pitch) ) } diff --git a/src/main/kotlin/org/cobalt/util/rotation/RotationDelta.kt b/src/main/kotlin/org/cobalt/util/rotation/data/RotationDelta.kt similarity index 66% rename from src/main/kotlin/org/cobalt/util/rotation/RotationDelta.kt rename to src/main/kotlin/org/cobalt/util/rotation/data/RotationDelta.kt index 5f8e23b..cc19104 100644 --- a/src/main/kotlin/org/cobalt/util/rotation/RotationDelta.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/data/RotationDelta.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.rotation +package org.cobalt.util.rotation.data data class RotationDelta( val deltaYaw: Float, diff --git a/src/main/kotlin/org/cobalt/util/rotation/RotationTarget.kt b/src/main/kotlin/org/cobalt/util/rotation/data/RotationTarget.kt similarity index 82% rename from src/main/kotlin/org/cobalt/util/rotation/RotationTarget.kt rename to src/main/kotlin/org/cobalt/util/rotation/data/RotationTarget.kt index fc35383..5847d86 100644 --- a/src/main/kotlin/org/cobalt/util/rotation/RotationTarget.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/data/RotationTarget.kt @@ -1,9 +1,9 @@ -package org.cobalt.util.rotation +package org.cobalt.util.rotation.data import net.minecraft.core.BlockPos import net.minecraft.world.entity.Entity import net.minecraft.world.phys.Vec3 -import org.cobalt.util.RotationUtils +import org.cobalt.util.rotation.RotationMath class RotationTarget { @@ -14,15 +14,12 @@ class RotationTarget { val targetRotation: Rotation get() { - rotation?.let { - return it - } val vec = vector ?: entity?.position() ?: blockPos?.let { Vec3.atCenterOf(it) } ?: error("No rotation target set.") - return RotationUtils.getRotation(vec) + return rotation ?: RotationMath.getRotation(vec) } constructor(entityTarget: Entity) { diff --git a/src/main/kotlin/org/cobalt/util/helper/Clock.kt b/src/main/kotlin/org/cobalt/util/scheduling/Clock.kt similarity index 68% rename from src/main/kotlin/org/cobalt/util/helper/Clock.kt rename to src/main/kotlin/org/cobalt/util/scheduling/Clock.kt index 7ebd979..c428fab 100644 --- a/src/main/kotlin/org/cobalt/util/helper/Clock.kt +++ b/src/main/kotlin/org/cobalt/util/scheduling/Clock.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.helper +package org.cobalt.util.scheduling class Clock { @@ -12,11 +12,6 @@ class Clock { this.isScheduled = true } - fun schedule(milliseconds: Double) { - this.endTime = (System.currentTimeMillis() + milliseconds.toLong()) - this.isScheduled = true - } - fun passed(): Boolean { return System.currentTimeMillis() >= endTime } diff --git a/src/main/kotlin/org/cobalt/util/helper/Multithreading.kt b/src/main/kotlin/org/cobalt/util/scheduling/Multithreading.kt similarity index 92% rename from src/main/kotlin/org/cobalt/util/helper/Multithreading.kt rename to src/main/kotlin/org/cobalt/util/scheduling/Multithreading.kt index f247c37..b2ec701 100644 --- a/src/main/kotlin/org/cobalt/util/helper/Multithreading.kt +++ b/src/main/kotlin/org/cobalt/util/scheduling/Multithreading.kt @@ -1,4 +1,4 @@ -package org.cobalt.util.helper +package org.cobalt.util.scheduling import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicInteger diff --git a/src/main/kotlin/org/cobalt/util/helper/TickScheduler.kt b/src/main/kotlin/org/cobalt/util/scheduling/TickScheduler.kt similarity index 92% rename from src/main/kotlin/org/cobalt/util/helper/TickScheduler.kt rename to src/main/kotlin/org/cobalt/util/scheduling/TickScheduler.kt index dd87595..fb2da01 100644 --- a/src/main/kotlin/org/cobalt/util/helper/TickScheduler.kt +++ b/src/main/kotlin/org/cobalt/util/scheduling/TickScheduler.kt @@ -1,6 +1,6 @@ -package org.cobalt.util.helper +package org.cobalt.util.scheduling -import java.util.* +import java.util.PriorityQueue import org.cobalt.event.EventBus import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.TickEvent diff --git a/src/main/kotlin/org/cobalt/util/ServerUtils.kt b/src/main/kotlin/org/cobalt/util/server/ConnectionTracker.kt similarity index 96% rename from src/main/kotlin/org/cobalt/util/ServerUtils.kt rename to src/main/kotlin/org/cobalt/util/server/ConnectionTracker.kt index 6b2f419..1ccb450 100644 --- a/src/main/kotlin/org/cobalt/util/ServerUtils.kt +++ b/src/main/kotlin/org/cobalt/util/server/ConnectionTracker.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.server import kotlin.math.min import net.minecraft.network.protocol.game.ClientboundSetTimePacket @@ -9,7 +9,7 @@ import org.cobalt.event.EventBus import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.PacketEvent -object ServerUtils { +object ConnectionTracker { @JvmStatic var averageTps = 20.0 diff --git a/src/main/kotlin/org/cobalt/util/ScoreboardUtils.kt b/src/main/kotlin/org/cobalt/util/server/Scoreboard.kt similarity index 96% rename from src/main/kotlin/org/cobalt/util/ScoreboardUtils.kt rename to src/main/kotlin/org/cobalt/util/server/Scoreboard.kt index 55d7eb7..bd4a610 100644 --- a/src/main/kotlin/org/cobalt/util/ScoreboardUtils.kt +++ b/src/main/kotlin/org/cobalt/util/server/Scoreboard.kt @@ -1,4 +1,4 @@ -package org.cobalt.util +package org.cobalt.util.server import net.minecraft.network.chat.Component import net.minecraft.network.chat.numbers.StyledFormat @@ -7,7 +7,7 @@ import net.minecraft.world.scores.PlayerTeam import org.cobalt.Cobalt.minecraft import org.cobalt.mixin.gui.HudAccessor -object ScoreboardUtils { +object Scoreboard { @JvmStatic val title: Component? diff --git a/src/main/kotlin/org/cobalt/util/TablistUtils.kt b/src/main/kotlin/org/cobalt/util/server/Tablist.kt similarity index 92% rename from src/main/kotlin/org/cobalt/util/TablistUtils.kt rename to src/main/kotlin/org/cobalt/util/server/Tablist.kt index 7a21735..90face7 100644 --- a/src/main/kotlin/org/cobalt/util/TablistUtils.kt +++ b/src/main/kotlin/org/cobalt/util/server/Tablist.kt @@ -1,10 +1,10 @@ -package org.cobalt.util +package org.cobalt.util.server import net.minecraft.network.chat.Component import net.minecraft.world.level.GameType import org.cobalt.Cobalt.minecraft -object TablistUtils { +object Tablist { @JvmStatic val lines: List diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 7f5a224..92ab789 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -20,7 +20,7 @@ "preLaunch": [ { "adapter": "kotlin", - "value": "org.cobalt.util.skia.SkiaBootstrap" + "value": "org.cobalt.util.render.skia.SkiaBootstrap" } ], "client": [ From fd319bb0688cb580c9942521335ac0b8299cef84 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 21:05:41 -0400 Subject: [PATCH 25/28] chore: cleanup --- .../org/cobalt/pathfinder/PathExecutor.kt | 10 +-- .../org/cobalt/pathfinder/helper/Input.kt | 23 +++++++ .../org/cobalt/pathfinder/helper/PathInput.kt | 55 --------------- .../cobalt/pathfinder/helper/PlayerInput.kt | 68 ++++++++++++++----- .../movement/walk/AscendMovement.kt | 4 -- .../cobalt/pathfinder/state/ExecutorState.kt | 6 +- .../pathfinder/state/fly/StartFlyState.kt | 10 +-- .../pathfinder/state/pathing/PathingState.kt | 59 +++++++++++++++- 8 files changed, 144 insertions(+), 91 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/helper/Input.kt delete mode 100644 src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt index 4fbb03f..f223be7 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathExecutor.kt @@ -7,7 +7,7 @@ import org.cobalt.event.annotation.SubscribeEvent import org.cobalt.event.impl.TickEvent import org.cobalt.event.impl.WorldEvent import org.cobalt.pathfinder.calculate.Path -import org.cobalt.pathfinder.helper.PathInput +import org.cobalt.pathfinder.helper.PlayerInput import org.cobalt.pathfinder.state.ExecutorState import org.cobalt.pathfinder.state.calculation.CalculatingState import org.cobalt.util.chat.ChatUtils @@ -23,7 +23,7 @@ object PathExecutor { var pathIndex: Int = 0 var running = false - var pathInput = PathInput() + var playerInput = PlayerInput() init { EventBus.register(this) @@ -53,7 +53,7 @@ object PathExecutor { return } - player.input = pathInput + player.input = playerInput this.config = config this.running = true @@ -68,7 +68,7 @@ object PathExecutor { minecraft.player?.let { it.input = KeyboardInput(minecraft.options) }.also { - pathInput.stopMovement() + playerInput.stopMovement() } running = false @@ -96,7 +96,7 @@ object PathExecutor { } if (minecraft.gui.screen() != null) { - pathInput.stopMovement() + playerInput.stopMovement() return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/Input.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/Input.kt new file mode 100644 index 0000000..4d32c36 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/Input.kt @@ -0,0 +1,23 @@ +package org.cobalt.pathfinder.helper + +data class Input( + var forward: Boolean = false, + var backward: Boolean = false, + var left: Boolean = false, + var right: Boolean = false, + var jump: Boolean = false, + var sprint: Boolean = false, + var sneak: Boolean = false, +) { + + fun apply(other: Input) { + forward = forward || other.forward + backward = backward || other.backward + left = left || other.left + right = right || other.right + jump = jump || other.jump + sprint = sprint || other.sprint + sneak = sneak || other.sneak + } + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt deleted file mode 100644 index 2b0f2b8..0000000 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PathInput.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.cobalt.pathfinder.helper - -import net.minecraft.client.player.ClientInput -import net.minecraft.world.entity.player.Input -import net.minecraft.world.phys.Vec2 - -class PathInput : ClientInput() { - - var forward = false - var backward = false - var left = false - var right = false - var jump = false - var shift = false - var sprint = false - - fun applyInput(input: PlayerInput) { - forward = input.forward - backward = input.backward - left = input.left - right = input.right - jump = input.jump - sprint = input.sprint - shift = input.sneak - } - - fun stopMovement() { - applyInput(PlayerInput()) - } - - override fun tick() { - this.keyPresses = Input( - forward, - backward, - left, - right, - jump, - shift, - sprint - ) - - val forwardImpulse = calculateImpulse(forward, backward) - val leftImpulse = calculateImpulse(left, right) - this.moveVector = Vec2(leftImpulse, forwardImpulse).normalized() - } - - private fun calculateImpulse(positive: Boolean, negative: Boolean): Float { - return when { - positive == negative -> 0.0f - positive -> 1.0f - else -> -1.0f - } - } - -} diff --git a/src/main/kotlin/org/cobalt/pathfinder/helper/PlayerInput.kt b/src/main/kotlin/org/cobalt/pathfinder/helper/PlayerInput.kt index 6628b3b..0677e5c 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/helper/PlayerInput.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/helper/PlayerInput.kt @@ -1,23 +1,55 @@ package org.cobalt.pathfinder.helper -data class PlayerInput( - var forward: Boolean = false, - var backward: Boolean = false, - var left: Boolean = false, - var right: Boolean = false, - var jump: Boolean = false, - var sprint: Boolean = false, - var sneak: Boolean = false, -) { - - fun apply(other: PlayerInput) { - forward = forward || other.forward - backward = backward || other.backward - left = left || other.left - right = right || other.right - jump = jump || other.jump - sprint = sprint || other.sprint - sneak = sneak || other.sneak +import net.minecraft.client.player.ClientInput +import net.minecraft.world.entity.player.Input +import net.minecraft.world.phys.Vec2 + +class PlayerInput : ClientInput() { + + var forward = false + var backward = false + var left = false + var right = false + var jump = false + var shift = false + var sprint = false + + fun applyInput(input: org.cobalt.pathfinder.helper.Input) { + forward = input.forward + backward = input.backward + left = input.left + right = input.right + jump = input.jump + sprint = input.sprint + shift = input.sneak + } + + fun stopMovement() { + applyInput(Input()) + } + + override fun tick() { + this.keyPresses = Input( + forward, + backward, + left, + right, + jump, + shift, + sprint + ) + + val forwardImpulse = calculateImpulse(forward, backward) + val leftImpulse = calculateImpulse(left, right) + this.moveVector = Vec2(leftImpulse, forwardImpulse).normalized() + } + + private fun calculateImpulse(positive: Boolean, negative: Boolean): Float { + return when { + positive == negative -> 0.0f + positive -> 1.0f + else -> -1.0f + } } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt index f156c65..fd4459b 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/walk/AscendMovement.kt @@ -22,10 +22,6 @@ class AscendMovement( val y = currNode.y + 1 val z = currNode.z + dz - if (!MovementValidator.canWalkOn(ctx, currNode.x, currNode.y, currNode.z)) { - return - } - if (!MovementValidator.canWalkOn(ctx, x, y - 1, z)) { return } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt index 58a793f..0b60899 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/ExecutorState.kt @@ -1,12 +1,12 @@ package org.cobalt.pathfinder.state import org.cobalt.pathfinder.PathExecutor -import org.cobalt.pathfinder.helper.PathInput +import org.cobalt.pathfinder.helper.PlayerInput abstract class ExecutorState { - protected val input: PathInput = - PathExecutor.pathInput + protected val playerInput: PlayerInput = + PathExecutor.playerInput open fun enter() {} open fun onTick() {} diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt index 2c39546..7443363 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/fly/StartFlyState.kt @@ -31,26 +31,26 @@ class StartFlyState : ExecutorState() { flyStage = FlyStage.INITIAL_JUMP } - input.stopMovement() + playerInput.stopMovement() when (flyStage) { FlyStage.INITIAL_JUMP -> { - input.jump = true + playerInput.jump = true flyStage = FlyStage.RELEASE_INITIAL_JUMP } FlyStage.RELEASE_INITIAL_JUMP -> { - input.jump = false + playerInput.jump = false flyStage = FlyStage.SECOND_JUMP } FlyStage.SECOND_JUMP -> { - input.jump = true + playerInput.jump = true flyStage = FlyStage.RELEASE_SECOND_JUMP } FlyStage.RELEASE_SECOND_JUMP -> { - input.jump = false + playerInput.jump = false } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt index 8462c57..6483593 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/state/pathing/PathingState.kt @@ -1,5 +1,62 @@ package org.cobalt.pathfinder.state.pathing +import org.cobalt.pathfinder.PathExecutor +import org.cobalt.pathfinder.calculate.Path +import org.cobalt.pathfinder.calculate.PathNode import org.cobalt.pathfinder.state.ExecutorState +import org.cobalt.pathfinder.state.fly.StartFlyState +import org.cobalt.util.chat.ChatUtils +import org.cobalt.util.chat.MessageType +import org.cobalt.util.client.PlayerUtils -class PathingState : ExecutorState() +class PathingState : ExecutorState() { + + private var path: Path? = null + + private inline var currPathIndex + get() = PathExecutor.pathIndex + set(value) { + PathExecutor.pathIndex = value + } + + override fun enter() { + path = PathExecutor.path ?: run { + ChatUtils.sendSystemMessage("Cannot traverse a nonexistent path...") + PathExecutor.stop() + return + } + } + + override fun exit() { + playerInput.stopMovement() + } + + override fun onTick() { + val path = path ?: return + + val nodes = path.nodes + val targetNode = nodes[currPathIndex] + + if (targetNode.useMovementFly && PlayerUtils.canFly && !PlayerUtils.isFlying) { + PathExecutor.changeState(StartFlyState()) + } + + if (hasArrived(targetNode)) { + currPathIndex++ + + if (currPathIndex >= nodes.size) { + ChatUtils.sendSystemMessage("Completed Path!", MessageType.DEBUG) + PathExecutor.stop() + } + + return + } + + // TODO: ROTATIONS & MOVEMENT + } + + private fun hasArrived(node: PathNode): Boolean { + return node.block == PlayerUtils.position + } + +} From 7365dc8f1fbe7f758d732a3fc277e0e113893625 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:27:40 -0400 Subject: [PATCH 26/28] chore: cleanup (start working on costs) --- .../org/cobalt/pathfinder/PathConfig.kt | 8 +------- .../org/cobalt/pathfinder/PathRenderer.kt | 20 +++++++------------ .../calculate/path/AStarPathfinder.kt | 6 +++--- .../org/cobalt/pathfinder/cost/ActionCosts.kt | 7 +++++++ .../pathfinder/movement/CalculationContext.kt | 4 ++-- 5 files changed, 20 insertions(+), 25 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/pathfinder/cost/ActionCosts.kt diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt index c99007f..6319b04 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathConfig.kt @@ -6,17 +6,11 @@ import org.cobalt.pathfinder.goal.Goal class PathConfig( val goal: Goal, val mode: PathMode = PathMode.WALK, - val shouldSprint: Boolean = true, val returnBestNode: Boolean = false, - val maxCalculationTime: Long = MAX_CALCULATION_TIME, - val hasReachedThreshold: Double = 0.09, + val maxCalculationTime: Long = 10_000_000_000L, ) { val allowFly: Boolean = mode == PathMode.FLY - companion object { - const val MAX_CALCULATION_TIME = 10_000_000_000L - } - } diff --git a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt index 24a3b15..787271f 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/PathRenderer.kt @@ -19,9 +19,9 @@ object PathRenderer { val path: Path = PathExecutor.path ?: return val nodes = path.nodes - val keyNodes = path.keyNodes + val index = PathExecutor.pathIndex - val targetNode = nodes[PathExecutor.pathIndex].centerVec + val targetNode = nodes[index].centerVec val playerPos = PlayerUtils.position.centerVec() if (Debug.enabled) { @@ -29,21 +29,15 @@ object PathRenderer { GizmoRenderer.drawBox(targetNode.smallBox(), Color.RED) } - for (index in keyNodes.indices) { - val node = keyNodes[index] - - GizmoRenderer.drawBlockPos( - if (node.useMovementFly) node.block else node.blockStandingOn, - color = theme.accentPrimary - ) - + for (index in nodes.indices) { if (index <= 0) continue - val prev = keyNodes[index - 1] + val prev = nodes[index - 1] + val curr = nodes[index + 1] GizmoRenderer.drawLine( - if (prev.useMovementFly) prev.centerVec else prev.topCenterVec, - if (node.useMovementFly) node.centerVec else node.topCenterVec, + prev.centerVec, + curr.centerVec, theme.accentSecondary ) } diff --git a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt index ea9f023..db6f174 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/calculate/path/AStarPathfinder.kt @@ -19,8 +19,8 @@ class AStarPathfinder( val startZ: Int, val goal: Goal, val mode: PathMode, - val returnBestNode: Boolean = false, - val maxCalculationTime: Long = 10_000_000L, + val returnBestNode: Boolean, + val maxCalculationTime: Long, ) { private val closedSet = Long2ObjectOpenHashMap() @@ -86,7 +86,7 @@ class AStarPathfinder( movementResult.reset() move.calculateCost(calculationContext, currentNode, movementResult) - if (movementResult.cost < calculationContext.infCost) { + if (movementResult.cost < calculationContext.actionCosts.infCost) { relaxNeighbor(currentNode, move, movementResult, openSet) } } diff --git a/src/main/kotlin/org/cobalt/pathfinder/cost/ActionCosts.kt b/src/main/kotlin/org/cobalt/pathfinder/cost/ActionCosts.kt new file mode 100644 index 0000000..05cc0a7 --- /dev/null +++ b/src/main/kotlin/org/cobalt/pathfinder/cost/ActionCosts.kt @@ -0,0 +1,7 @@ +package org.cobalt.pathfinder.cost + +class ActionCosts(speedAmplifier: Int, jumpAmplifier: Int) { + + val infCost = 1e4 + +} diff --git a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt index 5733196..8cbb409 100644 --- a/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt +++ b/src/main/kotlin/org/cobalt/pathfinder/movement/CalculationContext.kt @@ -2,18 +2,18 @@ package org.cobalt.pathfinder.movement import net.minecraft.world.effect.MobEffects import org.cobalt.Cobalt.minecraft +import org.cobalt.pathfinder.cost.ActionCosts import org.cobalt.pathfinder.helper.BlockStateAccessor class CalculationContext { - val infCost = 1e6 - val level = minecraft.level!! val player = minecraft.player!! val speedAmplifier = player.getEffect(MobEffects.SPEED)?.amplifier ?: -1 val jumpAmplifier = player.getEffect(MobEffects.JUMP_BOOST)?.amplifier ?: -1 + val actionCosts = ActionCosts(speedAmplifier, jumpAmplifier) val blockStateAccessor = BlockStateAccessor(level) } From d3f6c545e30db3410abf27e32ab843af84263f5b Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:41:19 -0400 Subject: [PATCH 27/28] fix: merge conflict with Notification.kt --- .../cobalt/ui/notification/Notification.kt | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/org/cobalt/ui/notification/Notification.kt b/src/main/kotlin/org/cobalt/ui/notification/Notification.kt index c5cd0b1..2c920c9 100644 --- a/src/main/kotlin/org/cobalt/ui/notification/Notification.kt +++ b/src/main/kotlin/org/cobalt/ui/notification/Notification.kt @@ -4,9 +4,9 @@ import kotlin.time.Duration import org.cobalt.ui.UIComponent import org.cobalt.ui.animation.BounceAnimation import org.cobalt.ui.animation.EaseOutAnimation -import org.cobalt.util.WindowUtils.windowWidth -import org.cobalt.util.skia.Skia -import org.cobalt.util.skia.helper.SkiaCorner +import org.cobalt.util.client.WindowUtils.windowWidth +import org.cobalt.util.render.SkiaRenderer +import org.cobalt.util.render.skia.data.SkiaCorner class Notification( private val title: String, @@ -60,7 +60,7 @@ class Notification( val resolvedY = targetY + slideDownAnim.get(previousY - targetY, 0f, false) updateBounds(resolvedX, resolvedY) - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos, width, height, CORNER_RADIUS, theme.backgroundPrimary ) @@ -74,17 +74,17 @@ class Notification( val hasDescription = description.isNotBlank() val titleFontSize = if (hasDescription) TITLE_FONT_SIZE else TITLE_FONT_SIZE_ALONE - val titleHeight = Skia.wrappedTextHeight(Skia.boldFont, title, contentWidth, titleFontSize) + val titleHeight = SkiaRenderer.wrappedTextHeight(SkiaRenderer.boldFont, title, contentWidth, titleFontSize) - Skia.wrappedText( - Skia.boldFont, title, + SkiaRenderer.wrappedText( + SkiaRenderer.boldFont, title, xPos + CONTENT_PADDING, yPos + CONTENT_PADDING, contentWidth, titleFontSize, theme.textPrimary, ) if (hasDescription) { - Skia.wrappedText( - Skia.boldFont, description, + SkiaRenderer.wrappedText( + SkiaRenderer.boldFont, description, xPos + CONTENT_PADDING, yPos + CONTENT_PADDING + titleHeight + TITLE_DESCRIPTION_GAP, contentWidth, DESCRIPTION_FONT_SIZE, theme.textSecondary, ) @@ -95,7 +95,7 @@ class Notification( val progress = calculateProgress(System.currentTimeMillis()) val fillWidth = width * progress - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos + height - PROGRESS_BAR_HEIGHT, width, PROGRESS_BAR_HEIGHT, CORNER_RADIUS, @@ -104,7 +104,7 @@ class Notification( ) if (fillWidth > 0f) { - Skia.roundedRect( + SkiaRenderer.roundedRect( xPos, yPos + height - PROGRESS_BAR_HEIGHT, fillWidth, PROGRESS_BAR_HEIGHT, CORNER_RADIUS, @@ -146,8 +146,8 @@ class Notification( private fun calculateHeight(title: String, description: String): Float { val titleFontSize = if (description.isNotBlank()) TITLE_FONT_SIZE else TITLE_FONT_SIZE_ALONE - val titleHeight = Skia.wrappedTextHeight( - Skia.boldFont, title, + val titleHeight = SkiaRenderer.wrappedTextHeight( + SkiaRenderer.boldFont, title, DEFAULT_WIDTH - CONTENT_PADDING * 2, titleFontSize ) @@ -155,8 +155,8 @@ class Notification( var descHeight = 0f if (description.isNotBlank()) { - descHeight = Skia.wrappedTextHeight( - Skia.boldFont, description, + descHeight = SkiaRenderer.wrappedTextHeight( + SkiaRenderer.boldFont, description, DEFAULT_WIDTH - CONTENT_PADDING * 2, DESCRIPTION_FONT_SIZE ) From 9a2bd3c959f8e876fc249682ed757fce5fc4819f Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:15:01 -0400 Subject: [PATCH 28/28] fix: detekt issues --- .../org/cobalt/util/render/SkiaRenderer.kt | 55 ++------------- .../util/render/skia/helper/LineWrapper.kt | 68 +++++++++++++++++++ 2 files changed, 72 insertions(+), 51 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/util/render/skia/helper/LineWrapper.kt diff --git a/src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt b/src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt index 08114bd..9e8aa3d 100644 --- a/src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt +++ b/src/main/kotlin/org/cobalt/util/render/SkiaRenderer.kt @@ -3,7 +3,6 @@ package org.cobalt.util.render import io.github.humbleui.skija.* -import io.github.humbleui.skija.paragraph.FontCollection import io.github.humbleui.skija.svg.SVGDOM import io.github.humbleui.skija.svg.SVGLengthContext import io.github.humbleui.types.RRect @@ -13,6 +12,7 @@ import kotlin.math.max import org.cobalt.util.render.skia.data.SkiaCorner import org.cobalt.util.render.skia.data.SkiaFont import org.cobalt.util.render.skia.data.SkiaImage +import org.cobalt.util.render.skia.helper.LineWrapper object SkiaRenderer { @@ -193,58 +193,11 @@ object SkiaRenderer { @JvmStatic fun wrap(font: SkiaFont, text: String, maxWidth: Float, size: Float): List { - val lines = mutableListOf() - val line = StringBuilder() - - fun pushLine() { - lines.add(line.toString()) - line.clear() - } - - fun append(text: String) { - if (line.isNotEmpty()) { - line.append(' ') - } - - line.append(text) - } - - fun addWord(word: String) { - val candidate = if (line.isEmpty()) word else "$line $word" - - if (line.isNotEmpty() && textWidth(font, candidate, size) > maxWidth) { - pushLine() - } - - if (textWidth(font, word, size) <= maxWidth) { - append(word) - return - } - - val chunk = StringBuilder() - - for (char in word) { - if (chunk.isNotEmpty() && textWidth(font, "$chunk$char", size) > maxWidth) { - append(chunk.toString()) - pushLine() - chunk.clear() - } - - chunk.append(char) - } - - append(chunk.toString()) - } - - text.split('\n').forEach { paragraph -> - paragraph.split(' ').filter { it.isNotEmpty() }.forEach { addWord(it) } - - if (line.isNotEmpty()) { - pushLine() - } + val wrapper = LineWrapper(maxWidth) { stringToMeasure -> + textWidth(font, stringToMeasure, size) } - return lines + return wrapper.wrap(text) } @JvmStatic diff --git a/src/main/kotlin/org/cobalt/util/render/skia/helper/LineWrapper.kt b/src/main/kotlin/org/cobalt/util/render/skia/helper/LineWrapper.kt new file mode 100644 index 0000000..5117d7d --- /dev/null +++ b/src/main/kotlin/org/cobalt/util/render/skia/helper/LineWrapper.kt @@ -0,0 +1,68 @@ +package org.cobalt.util.render.skia.helper + +class LineWrapper( + private val maxWidth: Float, + private val measureWidth: (String) -> Float, +) { + + private val lines = mutableListOf() + private val line = StringBuilder() + + fun wrap(text: String): List { + text.split('\n').forEach { paragraph -> + paragraph.split(' ').filter { it.isNotEmpty() }.forEach { word -> + processWord(word) + } + + if (line.isNotEmpty()) { + pushLine() + } + } + + return lines + } + + private fun processWord(word: String) { + val candidate = if (line.isEmpty()) word else "$line $word" + + if (line.isNotEmpty() && measureWidth(candidate) > maxWidth) { + pushLine() + } + + if (measureWidth(word) <= maxWidth) { + append(word) + } else { + splitAndAppendLongWord(word) + } + } + + private fun splitAndAppendLongWord(word: String) { + val chunk = StringBuilder() + + for (char in word) { + if (chunk.isNotEmpty() && measureWidth("$chunk$char") > maxWidth) { + append(chunk.toString()) + pushLine() + chunk.clear() + } + + chunk.append(char) + } + + append(chunk.toString()) + } + + private fun pushLine() { + lines.add(line.toString()) + line.clear() + } + + private fun append(word: String) { + if (line.isNotEmpty()) { + line.append(' ') + } + + line.append(word) + } + +}