Skip to content

Commit b69fba4

Browse files
committed
autocorrecting y motion obstacle passing and rubberbanding improvements when stopping flying
1 parent 55a92ca commit b69fba4

6 files changed

Lines changed: 187 additions & 102 deletions

File tree

src/main/kotlin/com/lambda/interaction/managers/rotating/RotationManager.kt

Lines changed: 15 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,14 @@ import com.lambda.interaction.managers.rotating.RotationManager.updateActiveRota
3737
import com.lambda.threading.runGameScheduled
3838
import com.lambda.threading.runSafe
3939
import com.lambda.util.extension.rotation
40-
import com.lambda.util.math.MathUtils.toRadian
4140
import com.lambda.util.math.Vec2d
4241
import com.lambda.util.math.lerp
42+
import com.lambda.util.player.RotationUtils.getInputRelativeTo
4343
import com.lambda.util.world.raycast.RayCastUtils.orMiss
4444
import net.minecraft.client.input.Input
4545
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
46-
import net.minecraft.util.PlayerInput
4746
import net.minecraft.util.hit.EntityHitResult
4847
import net.minecraft.util.math.Vec2f
49-
import kotlin.math.PI
50-
import kotlin.math.atan2
51-
import kotlin.math.cos
52-
import kotlin.math.sin
5348

5449
/**
5550
* Manager designed to rotate the player and adjust movement input to match the camera's direction.
@@ -208,82 +203,19 @@ object RotationManager : Manager<RotationRequest>(
208203
@JvmStatic
209204
fun redirectStrafeInputs(input: Input) = runSafe {
210205
val movementYaw = movementYaw ?: return@runSafe
211-
val playerYaw = player.yaw
206+
val viewYaw = player.yaw
212207

213-
if (movementYaw.minus(playerYaw).rem(360f).let { it * it } < 0.001f) return@runSafe
208+
val newPlayerInput = getInputRelativeTo(movementYaw, input.playerInput, viewYaw)
214209

215-
val originalStrafe = input.movementVector.x
216-
val originalForward = input.movementVector.y
217-
218-
if (originalStrafe == 0.0f && originalForward == 0.0f) return@runSafe
219-
220-
val deltaYawRad = (playerYaw - movementYaw).toRadian()
221-
222-
val cos = cos(deltaYawRad)
223-
val sin = sin(deltaYawRad)
224-
val newStrafe = originalStrafe * cos - originalForward * sin
225-
val newForward = originalStrafe * sin + originalForward * cos
226-
227-
val angle = atan2(newStrafe.toDouble(), newForward.toDouble())
228-
229-
// Define the boundaries for our 8 sectors (in radians). Each sector is 45 degrees (PI/4).
230-
val sector = (PI / 4.0).toFloat()
231-
val boundary = (PI / 8.0).toFloat() // The halfway point between sectors (22.5 degrees)
232-
233-
var pressForward = false
234-
var pressBackward = false
235-
var pressLeft = false
236-
var pressRight = false
237-
238-
// Determine which 45-degree sector the angle falls into and set the corresponding keys.
239-
when {
240-
angle > -boundary && angle <= boundary -> {
241-
pressForward = true
242-
}
243-
angle > boundary && angle <= boundary + sector -> {
244-
pressForward = true
245-
pressLeft = true
246-
}
247-
angle > boundary + sector && angle <= boundary + 2 * sector -> {
248-
pressLeft = true
249-
}
250-
angle > boundary + 2 * sector && angle <= boundary + 3 * sector -> {
251-
pressBackward = true
252-
pressLeft = true
253-
}
254-
angle > boundary + 3 * sector || angle <= -(boundary + 3 * sector) -> {
255-
pressBackward = true
256-
}
257-
angle > -(boundary + 3 * sector) && angle <= -(boundary + 2 * sector) -> {
258-
pressBackward = true
259-
pressRight = true
260-
}
261-
angle > -(boundary + 2 * sector) && angle <= -(boundary + sector) -> {
262-
pressRight = true
263-
}
264-
angle > -(boundary + sector) && angle <= -boundary -> {
265-
pressForward = true
266-
pressRight = true
267-
}
268-
}
269-
270-
input.playerInput = PlayerInput(
271-
pressForward,
272-
pressBackward,
273-
pressLeft,
274-
pressRight,
275-
input.playerInput.jump(),
276-
input.playerInput.sneak(),
277-
input.playerInput.sprint()
278-
)
210+
input.playerInput = newPlayerInput
279211

280212
val x = multiplier(input.playerInput.left(), input.playerInput.right())
281213
val y = multiplier(input.playerInput.forward(), input.playerInput.backward())
282214
input.movementVector = Vec2f(x, y).normalize()
283215
}
284216

285217
private fun multiplier(positive: Boolean, negative: Boolean) =
286-
((if (positive) 1 else 0) - (if (negative) 1 else 0)).toFloat()
218+
(if (positive) 1f else 0f) - (if (negative) 1f else 0f)
287219

288220
@JvmStatic fun onRotationSend() {
289221
prevServerRotation = serverRotation
@@ -301,16 +233,16 @@ object RotationManager : Manager<RotationRequest>(
301233
*/
302234
private fun SafeContext.updateActiveRotation() {
303235
val newYaw = yawRequest?.let { yawRequest ->
304-
val toYaw = if (yawRequest.keepTicks >= 0)
305-
yawRequest.yaw.value ?: activeRotation.yaw
306-
else player.rotation.yaw
236+
val toYaw =
237+
if (yawRequest.keepTicks >= 0) yawRequest.yaw.value ?: activeRotation.yaw
238+
else player.rotation.yaw
307239
serverRotation.slerpYaw(toYaw, yawRequest.rotationConfig.turnSpeed)
308240
} ?: player.rotation.yaw
309241

310242
val newPitch = pitchRequest?.let { pitchRequest ->
311-
val toPitch = if (pitchRequest.keepTicks >= 0)
312-
pitchRequest.pitch.value ?: activeRotation.pitch
313-
else player.rotation.pitch
243+
val toPitch =
244+
if (pitchRequest.keepTicks >= 0) pitchRequest.pitch.value ?: activeRotation.pitch
245+
else player.rotation.pitch
314246
serverRotation.slerpPitch(toPitch, pitchRequest.rotationConfig.turnSpeed)
315247
} ?: player.rotation.pitch
316248

@@ -369,17 +301,15 @@ object RotationManager : Manager<RotationRequest>(
369301

370302
@JvmStatic
371303
val movementYaw: Float?
372-
get() {
373-
return if (yawRequest == null || yawRequest?.rotationConfig?.rotationMode == RotationMode.Silent) null
304+
get() =
305+
if (yawRequest == null || yawRequest?.rotationConfig?.rotationMode == RotationMode.Silent) null
374306
else activeRotation.yaw.toFloat()
375-
}
376307

377308
@JvmStatic
378309
val movementPitch: Float?
379-
get() {
380-
return if (pitchRequest == null || pitchRequest?.rotationConfig?.rotationMode == RotationMode.Silent) null
310+
get() =
311+
if (pitchRequest == null || pitchRequest?.rotationConfig?.rotationMode == RotationMode.Silent) null
381312
else activeRotation.pitch.toFloat()
382-
}
383313

384314
@JvmStatic
385315
fun getRotationForVector(deltaTime: Double): Vec2d? = runSafe {

src/main/kotlin/com/lambda/module/modules/movement/elytrafly/ElytraFlyMode.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ abstract class ElytraFlyMode(
123123
return inventoryRequest.done
124124
}
125125

126+
open fun interrupt() {}
127+
126128
fun SafeContext.findElytra(): Slot? =
127129
ELYTRA_SELECTION.filterSlots(player.hotbarAndInventorySlots).minByOrNull { it.index }
128130

src/main/kotlin/com/lambda/module/modules/movement/elytrafly/ObstaclePassingMode.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ abstract class ObstaclePassingMode(
125125
return false
126126
}
127127

128-
private fun SafeContext.getSnappedDir(): Vec3d {
128+
protected fun SafeContext.getSnappedDir(): Vec3d {
129129
val travelDiff = player.pos.subtract(startPos).normalize().let { Vec3d(it.x, 0.0, it.z) }
130130
return lockYawToStep(travelDiff)
131131
}
@@ -150,24 +150,23 @@ abstract class ObstaclePassingMode(
150150
return Vec3d(x, vector.y, z)
151151
}
152152

153-
context(safeContext: SafeContext)
154-
private fun pathToValidPoint(startSearchPos: Vec3d, dir: Vec3d, initialBlockedCheck: Boolean = false) {
153+
private fun SafeContext.pathToValidPoint(startSearchPos: Vec3d, dir: Vec3d, initialBlockedCheck: Boolean = false) {
155154
var skippingFirstCheck = !initialBlockedCheck
156155
var searchPos = startSearchPos
157156
while (skippingFirstCheck || searchPos.isObstructed(dir)) {
158157
searchPos = searchPos.add(dir.multiply(passerConfig.obstacleLookAhead.toDouble()))
159158
skippingFirstCheck = false
160159
}
161160
passTo(searchPos)
162-
safeContext.player.stopGliding()
161+
interrupt()
163162
}
164163

165164
private fun passTo(pos: Vec3d) {
166165
passingToPos = pos
167166
BaritoneHandler.setGoalAndPath(GoalGetToBlock(pos.flooredBlockPos))
168167
}
169168

170-
private fun Vec3d.findClosestPointOnLine(snappedDirection: Vec3d): Vec3d {
169+
protected fun Vec3d.findClosestPointOnLine(snappedDirection: Vec3d): Vec3d {
171170
val startToCurrent = subtract(startPos)
172171
val t = startToCurrent.dotProduct(snappedDirection) / snappedDirection.dotProduct(snappedDirection)
173172
return startPos.add(snappedDirection.multiply(t))

src/main/kotlin/com/lambda/module/modules/movement/elytrafly/modes/BounceElytraFly.kt

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,23 @@ import com.lambda.util.PacketUtils.handlePacketSilently
3939
import com.lambda.util.PacketUtils.sendPacketSilently
4040
import com.lambda.util.SpeedUnit
4141
import com.lambda.util.TickTimer
42+
import com.lambda.util.math.distSq
43+
import com.lambda.util.math.minus
4244
import com.lambda.util.player.PlayerUtils.canStartGliding
4345
import com.lambda.util.player.PlayerUtils.canTakeoff
46+
import net.minecraft.client.input.KeyboardInput.getMovementMultiplier
4447
import net.minecraft.entity.Entity
4548
import net.minecraft.network.packet.Packet
4649
import net.minecraft.network.packet.s2c.common.CommonPingS2CPacket
50+
import net.minecraft.util.PlayerInput
51+
import net.minecraft.util.math.Vec2f
4752
import net.minecraft.util.math.Vec3d
4853
import java.util.*
4954
import java.util.concurrent.ConcurrentLinkedQueue
5055
import kotlin.math.abs
56+
import kotlin.math.cos
57+
import kotlin.math.pow
58+
import kotlin.math.sin
5159

5260
class BounceElytraFly(
5361
override val c: Config
@@ -69,33 +77,43 @@ class BounceElytraFly(
6977
@Group(Y_MOTION_GROUP) val yMotionSetting by c.setting("Y Motion", false, "Cancels the players y velocity to aid speed")
7078
@Group(Y_MOTION_GROUP) val onlyOnDiagonal: Boolean by c.setting("Only On Diagonal", true, "Only use y motion when the player is flying on a non-axial angle") { yMotionSetting }
7179
@Group(Y_MOTION_GROUP) val minDiagonalAngle by c.setting("Min Diagonal Angle", 15.0, 0.0..180.0, 0.1, "The minimum angle the player must be flying to use y motion") { yMotionSetting && onlyOnDiagonal }
80+
@Group(Y_MOTION_GROUP) val strictYMotionRange by c.setting("Strict Range", true, "provides an extra range check to sneak until within. Typically used for when you need to get within sub-block distances of walls for collision checks") { yMotionSetting }
81+
@Group(Y_MOTION_GROUP) val acceptableYMotionRange by c.setting("Acceptable Range", 0.1, 0.01..5.0, 0.01, "The acceptable distance, aside from forward distance, from the start position") { yMotionSetting && strictYMotionRange }
7282
@Group(Y_MOTION_GROUP) val yMotionStartSpeed by c.setting("Y Motion Start Speed", 30, 0..40, 1, unit = "bps") { yMotionSetting }
7383
@Group(Y_MOTION_GROUP) val speedLimit by c.setting("Speed Limit", 110, 10..400, 1, unit = "bps") { yMotionSetting }
74-
context(safeContext: SafeContext)
75-
private val yMotion
84+
private val SafeContext.yMotion
7685
get() = yMotionSetting &&
77-
(!onlyOnDiagonal || run {
78-
val normalised = abs(RotationManager.activeRotation.yaw % 90)
79-
normalised > minDiagonalAngle && normalised < 90 - minDiagonalAngle
80-
}) &&
81-
safeContext.player.isOnGround &&
82-
safeContext.player.isGliding &&
86+
onYMotionAngle &&
87+
player.isOnGround &&
88+
player.isGliding &&
8389
Speedometer.calculateSpeed(true, SpeedUnit.BlocksPerSecond).let { speed ->
8490
speed > yMotionStartSpeed && speed < speedLimit
8591
}
8692

93+
private val onYMotionAngle
94+
get() = !onlyOnDiagonal || diagonal
95+
8796
@Group(BOUNCE_OBSTACLE_PASSER_GROUP) override val passerConfig by c.configBlock(PasserSettings(c))
8897

8998
private var jumpThisTick = false
9099
private var prevGliding: Boolean? = null
91100
private val pauseTimer = TickTimer()
92101
private val pingPackets = ConcurrentLinkedQueue<CommonPingS2CPacket>()
93102
private val sendPacketQueue = LinkedList<Packet<*>>()
103+
private var sneakLeft = false
104+
private var sneakRight = false
105+
private var interrupting = false
94106

95107
private val SafeContext.queuePackets
96-
get() = fakeLag && player.isGliding && !yMotion &&
108+
get() = fakeLag && player.isGliding && (!yMotionSetting || !onYMotionAngle) &&
97109
player.y - startPos.y < if (passerConfig.passObstacles) passerConfig.minObstacleHeight + 0.1 else 0.163
98110

111+
private val diagonal: Boolean
112+
get() {
113+
val normalised = abs(RotationManager.activeRotation.yaw % 90)
114+
return normalised > minDiagonalAngle && normalised < 90 - minDiagonalAngle
115+
}
116+
99117
init {
100118
listen<TickEvent.Pre> {
101119
pauseTimer.tick()
@@ -104,12 +122,44 @@ class BounceElytraFly(
104122

105123
if (handlePassingObstacles()) return@listen
106124

125+
if (yMotionSetting && strictYMotionRange && onYMotionAngle && player.isOnGround) {
126+
val snappedDir = getSnappedDir()
127+
val closestLinePoint = player.pos.findClosestPointOnLine(snappedDir)
128+
val xz = Vec3d(player.x, closestLinePoint.y, player.z)
129+
if (xz distSq closestLinePoint > acceptableYMotionRange.pow(2)) {
130+
if (player.isGliding) {
131+
interrupt()
132+
return@listen
133+
}
134+
val offset = player.pos - startPos
135+
val cross = snappedDir.x * offset.z - snappedDir.z * offset.x
136+
val rotationRequest = rotationRequest {
137+
val yawAndPitch = snappedDir.yawAndPitch
138+
yaw(yawAndPitch.y)
139+
}.submit()
140+
if (!rotationRequest.done) return@listen
141+
sneakLeft = cross > 0
142+
sneakRight = !sneakLeft
143+
return@listen
144+
}
145+
}
146+
107147
if (!pauseTimer.hasSurpassed(flagPause)) return@listen
108148

109149
if (!player.isGliding) {
110150
if (takeoff && player.canTakeoff) {
111151
if (player.canStartGliding) GlideHandler.onGlide()
112-
else jumpThisTick = true
152+
else {
153+
val yawRad = Math.toRadians(player.yaw.toDouble())
154+
val rightX = -cos(yawRad)
155+
val rightZ = -sin(yawRad)
156+
val vx = player.velocity.x
157+
val vz = player.velocity.z
158+
val sidewaysSpeed = abs(vx * rightX + vz * rightZ)
159+
if (sidewaysSpeed >= 0.001) return@listen
160+
161+
jumpThisTick = true
162+
}
113163
}
114164
return@listen
115165
}
@@ -119,9 +169,33 @@ class BounceElytraFly(
119169
flyOrFakeFly()
120170
}
121171

172+
listen<TickEvent.Post>({ -100 }) {
173+
interrupting = false
174+
}
175+
122176
listen<MovementEvent.InputUpdate> { event ->
123-
if ((player.isGliding && jump) || jumpThisTick) {
124-
event.input.jump()
177+
val input = event.input
178+
val playerInput = input.playerInput
179+
if (sneakLeft || sneakRight) {
180+
input.playerInput = PlayerInput(
181+
playerInput.forward,
182+
playerInput.backward,
183+
sneakLeft,
184+
sneakRight,
185+
playerInput.jump,
186+
true,
187+
false
188+
)
189+
input.movementVector = Vec2f(
190+
getMovementMultiplier(sneakLeft, sneakRight),
191+
0f
192+
)
193+
sneakLeft = false
194+
sneakRight = false
195+
return@listen
196+
}
197+
if ((player.isGliding && !interrupting && jump) || jumpThisTick) {
198+
input.jump()
125199
jumpThisTick = false
126200
}
127201
}
@@ -145,11 +219,18 @@ class BounceElytraFly(
145219
onFlag { pauseTimer.reset() }
146220

147221
onDisable {
222+
jumpThisTick = false
148223
prevGliding = false
149224
flushPackets()
225+
sneakLeft = false
226+
sneakRight = false
150227
}
151228
}
152229

230+
override fun interrupt() {
231+
interrupting = true
232+
}
233+
153234
private fun SafeContext.flushPackets() {
154235
while (sendPacketQueue.isNotEmpty()) {
155236
val packet = sendPacketQueue.poll()
@@ -171,6 +252,7 @@ class BounceElytraFly(
171252
runSafe {
172253
val original: Boolean = player.getFlag(Entity.GLIDING_FLAG_INDEX)
173254
if (prevGliding == true &&
255+
!interrupting &&
174256
pauseTimer.hasSurpassed(flagPause) &&
175257
!BaritoneHandler.isActive) true
176258
else {

0 commit comments

Comments
 (0)