Skip to content

Commit 30c6deb

Browse files
committed
obstacle passer in ElytraFly for mode Bounce
1 parent 1449055 commit 30c6deb

6 files changed

Lines changed: 212 additions & 19 deletions

File tree

src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@
1717

1818
package com.lambda.mixin.network;
1919

20+
import com.lambda.Lambda;
2021
import com.lambda.event.EventFlow;
2122
import com.lambda.event.events.ChatEvent;
2223
import com.lambda.event.events.InventoryEvent;
2324
import com.lambda.event.events.WorldEvent;
2425
import com.lambda.interaction.managers.inventory.InventoryManager;
2526
import com.lambda.module.modules.movement.Velocity;
27+
import com.lambda.module.modules.player.NoForceRotate;
2628
import com.lambda.module.modules.render.NoRender;
2729
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
2830
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
2931
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
32+
import com.llamalad7.mixinextras.sugar.Local;
3033
import net.minecraft.client.network.ClientPlayNetworkHandler;
3134
import net.minecraft.client.network.PlayerListEntry;
35+
import net.minecraft.entity.Entity;
3236
import net.minecraft.network.packet.s2c.play.*;
3337
import org.spongepowered.asm.mixin.Mixin;
3438
import org.spongepowered.asm.mixin.injection.At;
@@ -132,4 +136,26 @@ void onOverlayMessage(OverlayMessageS2CPacket packet, CallbackInfo ci) {
132136
ci.cancel();
133137
}
134138
}
139+
140+
@ModifyExpressionValue(method = "setPosition(Lnet/minecraft/entity/EntityPosition;Ljava/util/Set;Lnet/minecraft/entity/Entity;Z)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityPosition;yaw()F"))
141+
private static float wrapSetYaw(float original, @Local(argsOnly = true) Entity entity) {
142+
var player = Lambda.getMc().player;
143+
if (entity != player) return original;
144+
if (player == null) return original;
145+
if (NoForceRotate.INSTANCE.isEnabled()) {
146+
return player.getYaw();
147+
}
148+
return original;
149+
}
150+
151+
@ModifyExpressionValue(method = "setPosition(Lnet/minecraft/entity/EntityPosition;Ljava/util/Set;Lnet/minecraft/entity/Entity;Z)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityPosition;pitch()F"))
152+
private static float wrapSetPitch(float original, @Local(argsOnly = true) Entity entity) {
153+
var player = Lambda.getMc().player;
154+
if (entity != player) return original;
155+
if (player == null) return original;
156+
if (NoForceRotate.INSTANCE.isEnabled()) {
157+
return player.getPitch();
158+
}
159+
return original;
160+
}
135161
}

src/main/kotlin/com/lambda/module/modules/movement/ElytraFly.kt

Lines changed: 146 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.lambda.module.modules.movement
1919

20+
import baritone.api.pathing.goals.GoalGetToBlock
2021
import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig
2122
import com.lambda.config.applyEdits
2223
import com.lambda.context.SafeContext
@@ -25,7 +26,9 @@ import com.lambda.event.events.MovementEvent
2526
import com.lambda.event.events.PacketEvent
2627
import com.lambda.event.events.TickEvent
2728
import com.lambda.event.listener.SafeListener.Companion.listen
29+
import com.lambda.interaction.BaritoneManager
2830
import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest
31+
import com.lambda.interaction.material.StackSelection.Companion.select
2932
import com.lambda.module.Module
3033
import com.lambda.module.modules.movement.BetterFirework.canOpenElytra
3134
import com.lambda.module.modules.movement.BetterFirework.canTakeoff
@@ -35,12 +38,18 @@ import com.lambda.threading.runSafe
3538
import com.lambda.util.Timer
3639
import com.lambda.util.extension.isElytraFlying
3740
import com.lambda.util.player.MovementUtils.addSpeed
38-
import com.lambda.util.player.SlotUtils.hotbarAndInventoryStacks
3941
import com.lambda.util.player.SlotUtils.hotbarStacks
4042
import com.lambda.util.player.hasFirework
41-
import com.lambda.interaction.material.StackSelection.Companion.selectStack
4243
import com.lambda.module.hud.Speedometer
44+
import com.lambda.util.BlockUtils.blockState
4345
import com.lambda.util.SpeedUnit
46+
import com.lambda.util.math.dist
47+
import com.lambda.util.math.flooredBlockPos
48+
import com.lambda.util.math.isLoaded
49+
import com.lambda.util.player.SlotUtils.inventoryStacks
50+
import com.lambda.util.world.raycast.InteractionMask
51+
import com.lambda.util.world.raycast.RayCastUtils.blockResult
52+
import com.lambda.util.world.raycast.RayCastUtils.rayCast
4453
import net.minecraft.component.DataComponentTypes
4554
import net.minecraft.entity.Entity
4655
import net.minecraft.item.ItemStack
@@ -49,6 +58,13 @@ import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket
4958
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
5059
import net.minecraft.sound.SoundEvents
5160
import net.minecraft.util.math.Vec3d
61+
import java.lang.Math.toDegrees
62+
import java.lang.Math.toRadians
63+
import kotlin.math.atan2
64+
import kotlin.math.cos
65+
import kotlin.math.hypot
66+
import kotlin.math.roundToInt
67+
import kotlin.math.sin
5268
import kotlin.time.Duration.Companion.seconds
5369

5470
object ElytraFly : Module(
@@ -57,35 +73,42 @@ object ElytraFly : Module(
5773
tag = ModuleTag.MOVEMENT,
5874
) {
5975
@JvmStatic val mode by setting("Mode", FlyMode.Bounce)
76+
6077
private val inventory by setting("Inventory", true, "Allow using fireworks from the players inventory") { mode == FlyMode.GrimControl }
6178

6279
//ToDo: Implement these commented out settings
6380
private val takeoff by setting("Takeoff", true, "Automatically jumps and initiates gliding") { mode == FlyMode.Bounce }
6481
private val autoPitch by setting("Auto Pitch", true, "Automatically pitches the players rotation down to bounce at faster speeds") { mode == FlyMode.Bounce }
65-
private val pitch by setting("Pitch", 80, 0..90, 1) { autoPitch && mode == FlyMode.Bounce }
82+
private val pitch by setting("Pitch", 80, 0..90, 1) { mode == FlyMode.Bounce && autoPitch }
6683
private val yMotion by setting("Y Motion", false, "Cancels the players y velocity to aid speed") { mode == FlyMode.Bounce }
67-
private val yMotionStartSpeed by setting("Y Motion Start Speed", 30, 5..30, 1, "bps") { mode == FlyMode.Bounce && yMotion }
68-
private val speedLimit by setting("Speed Limit", 110, 10..720, 1, "bps") { mode == FlyMode.Bounce && yMotion }
84+
private val yMotionStartSpeed by setting("Y Motion Start Speed", 30, 5..40, 1, "bps") { mode == FlyMode.Bounce && yMotion }
85+
private val speedLimit by setting("Speed Limit", 110, 10..400, 1, "bps") { mode == FlyMode.Bounce && yMotion }
6986
private val jump by setting("Jump", true, "Automatically jumps") { mode == FlyMode.Bounce }
70-
private val flagPause by setting("Flag Pause", 20, 0..100, 1, "How long to pause if the server flags you for a movement check") { mode == FlyMode.Bounce }
71-
// private val passObstacles by setting("Pass Obstacles", true, "Automatically paths around obstacles using baritone") { mode == FlyMode.Bounce }
87+
private val flagPause by setting("Flag Pause", 5, 0..100, 1, "How long to pause if the server flags you for a movement check", "ticks") { mode == FlyMode.Bounce }
88+
private val passObstacles by setting("Pass Obstacles", true, "Automatically paths around obstacles using baritone") { mode == FlyMode.Bounce }
89+
private val acceptableOffsetRange by setting("Acceptable Offset Range", 2.0, 0.1..5.0, 0.01, "Acceptable offset from the original flight line to allow when starting to fly again after passing obstacles") { mode == FlyMode.Bounce && passObstacles }
90+
private val obstacleLookAhead by setting("Obstacle Look-Ahead", 15, 0..50, 1, "Looks ahead of the player to see if obstacles are in the way") { mode == FlyMode.Bounce && passObstacles }
91+
private val directionStep by setting("Direction Step", 45.0, 0.0..180.0, 0.1, "The step size to use when locking the flight direction") { mode == FlyMode.Bounce && passObstacles }
7292

7393
private val boostSpeed by setting("Boost", 0.00, 0.0..0.5, 0.005, description = "Speed to add when flying")
7494
private val rocketSpeed by setting("Rocket Speed", 0.0, 0.0..2.0, description = "Speed multiplier that the rocket gives you") { mode == FlyMode.Enhanced }
7595

7696
private val mute by setting("Mute Elytra", false, "Mutes the elytra sound when gliding")
7797

98+
private var startPos = Vec3d.ZERO
7899
private var jumpThisTick = false
79100
private var previouslyFlying: Boolean? = null
101+
private var passingToPos: Vec3d? = null
80102
private var glidePause = 0
103+
81104
private var flipFlop = false
82105
private var lastDuration = 1.0
83106
private val fireworkTimer = Timer()
84107

85108
init {
86109
setDefaultAutomationConfig {
87110
applyEdits {
88-
hideAllGroupsExcept(inventoryConfig)
111+
hideAllGroupsExcept(inventoryConfig, rotationConfig)
89112
}
90113
}
91114

@@ -97,14 +120,21 @@ object ElytraFly : Module(
97120
}
98121
}
99122

100-
listen<TickEvent.Post> {
101-
if (glidePause > 0) glidePause--
123+
onEnable {
124+
startPos = player.pos
102125
}
103126

104-
listen<PacketEvent.Receive.Post> { event ->
127+
onDisable {
128+
passingToPos = null
129+
if (passObstacles) BaritoneManager.cancel()
130+
}
131+
132+
listen<PacketEvent.Receive.Pre> { event ->
105133
if (event.packet !is PlayerPositionLookS2CPacket) return@listen
106134
if (mode == FlyMode.Bounce && player.isGliding) {
107-
glidePause = flagPause
135+
val snappedDir = getSnappedDir()
136+
val closestLinePoint = player.pos.findClosestPointOnLine(snappedDir)
137+
pathToValidPoint(closestLinePoint, snappedDir, true)
108138
}
109139
}
110140

@@ -161,11 +191,37 @@ object ElytraFly : Module(
161191
}
162192

163193
private fun SafeContext.findFirework(): ItemStack? {
164-
val stack = selectStack(count = 1) { isItem(Items.FIREWORK_ROCKET) }
165-
return stack.bestItemMatch(player.hotbarStacks) ?: if (inventory) stack.bestItemMatch(player.hotbarAndInventoryStacks) else null
194+
val stack = Items.FIREWORK_ROCKET.select()
195+
return stack.bestItemMatch(player.hotbarStacks) ?: if (inventory) stack.bestItemMatch(player.inventoryStacks) else null
166196
}
167197

168198
private fun SafeContext.onTickBounce() {
199+
if (!BaritoneManager.isActive) passingToPos = null
200+
201+
val playerPos = player.pos
202+
if (passObstacles && playerPos.let { Vec3d(it.x, startPos.y, it.z) } dist startPos > 0.1) run obstacleChecks@{
203+
val snappedDir = getSnappedDir()
204+
val closestLinePoint = playerPos.findClosestPointOnLine(snappedDir)
205+
passingToPos?.let { passingTo ->
206+
if (passingTo.isObstructed(snappedDir)) {
207+
pathToValidPoint(passingTo, snappedDir)
208+
}
209+
return
210+
}
211+
if (playerPos dist closestLinePoint <= acceptableOffsetRange) {
212+
if (playerPos.let { Vec3d(it.x, closestLinePoint.y, it.z) }.isObstructed(snappedDir))
213+
pathToValidPoint(closestLinePoint, snappedDir)
214+
else return@obstacleChecks
215+
} else pathToValidPoint(closestLinePoint, snappedDir, true)
216+
217+
return
218+
}
219+
220+
if (glidePause > 0) {
221+
glidePause--
222+
return
223+
}
224+
169225
if (autoPitch) rotationRequest { pitch(pitch.toFloat()) }.submit()
170226

171227
if (!player.isGliding) {
@@ -184,6 +240,76 @@ object ElytraFly : Module(
184240
}
185241
}
186242

243+
private fun SafeContext.getSnappedDir(): Vec3d {
244+
val travelDiff = player.pos.subtract(startPos).normalize().let { Vec3d(it.x, 0.0, it.z) }
245+
return lockYawToStep(travelDiff)
246+
}
247+
248+
context(safeContext: SafeContext)
249+
private fun pathToValidPoint(startSearchPos: Vec3d, dir: Vec3d, initialBlockedCheck: Boolean = false) {
250+
var skippingFirstCheck = !initialBlockedCheck
251+
var searchPos = startSearchPos
252+
while (skippingFirstCheck || searchPos.isObstructed(dir)) {
253+
searchPos = searchPos.add(dir.multiply(obstacleLookAhead.toDouble()))
254+
skippingFirstCheck = false
255+
}
256+
passTo(searchPos)
257+
safeContext.player.stopGliding()
258+
glidePause = flagPause
259+
}
260+
261+
private fun passTo(pos: Vec3d) {
262+
passingToPos = pos
263+
BaritoneManager.setGoalAndPath(GoalGetToBlock(pos.flooredBlockPos))
264+
}
265+
266+
context(safeContext: SafeContext)
267+
private fun Vec3d.isObstructed(direction: Vec3d) =
268+
if (!isLoaded) false
269+
else {
270+
flooredBlockPos.down().let { downPos ->
271+
!safeContext.blockState(downPos).isSolidBlock(safeContext.world, downPos)
272+
} ||
273+
rayCastObstructed(direction) ||
274+
add(0.0, 1.0, 0.0).rayCastObstructed(direction) ||
275+
add(0.0, 2.0, 0.0).rayCastObstructed(direction)
276+
}
277+
278+
context(safeContext: SafeContext)
279+
private fun Vec3d.rayCastObstructed(direction: Vec3d) =
280+
safeContext.rayCast(
281+
this,
282+
direction,
283+
obstacleLookAhead.toDouble(),
284+
InteractionMask.Block
285+
)?.blockResult != null
286+
287+
private fun Vec3d.findClosestPointOnLine(snappedDirection: Vec3d): Vec3d {
288+
val startToCurrent = subtract(startPos)
289+
val t = startToCurrent.dotProduct(snappedDirection) / snappedDirection.dotProduct(snappedDirection)
290+
return startPos.add(snappedDirection.multiply(t))
291+
}
292+
293+
fun lockYawToStep(vector: Vec3d): Vec3d {
294+
val yaw = atan2(vector.z, vector.x)
295+
val yawDegrees = toDegrees(yaw)
296+
297+
val normalizedYaw = (yawDegrees % 360.0 + 360.0) % 360.0
298+
299+
val steps = normalizedYaw / directionStep
300+
val roundedSteps = steps.roundToInt()
301+
val lockedYawDegrees = roundedSteps * directionStep
302+
303+
val normalizedLockedYawDegrees = (lockedYawDegrees % 360.0 + 360.0) % 360.0
304+
val lockedYaw = toRadians(normalizedLockedYawDegrees)
305+
306+
val horizontalLength = hypot(vector.x, vector.z)
307+
val x = cos(lockedYaw) * horizontalLength
308+
val z = sin(lockedYaw) * horizontalLength
309+
310+
return Vec3d(x, vector.y, z)
311+
}
312+
187313
@JvmStatic
188314
fun getModifiedBounceVelocity(original: Vec3d) =
189315
runSafe {
@@ -204,7 +330,12 @@ object ElytraFly : Module(
204330
previouslyFlying = original
205331
return@runSafe original
206332
}
207-
return if (isEnabled && mode == FlyMode.Bounce && previouslyFlying == true && glidePause <= 0) true
333+
return if (
334+
isEnabled &&
335+
mode == FlyMode.Bounce &&
336+
previouslyFlying == true &&
337+
glidePause <= 0 &&
338+
!BaritoneManager.isActive) true
208339
else {
209340
previouslyFlying = original
210341
original
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2026 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.module.modules.player
19+
20+
import com.lambda.module.Module
21+
import com.lambda.module.tag.ModuleTag
22+
23+
object NoForceRotate : Module(
24+
name = "NoForceRotate",
25+
description = "Prevents the server from forcing your players rotation",
26+
tag = ModuleTag.PLAYER
27+
)

src/main/kotlin/com/lambda/module/modules/player/RotationLock.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ object RotationLock : Module(
3939
}
4040

4141
@JvmStatic val yawMode by setting("Yaw Mode", Mode.Snap).group(Group.General)
42-
private val yawStep by setting("Yaw Step", 45.0, 1.0..180.0, 1.0) { yawMode == Mode.Snap }.group(Group.General)
43-
private val customYaw by setting("Custom Yaw", 0.0, -179.0..180.0, 1.0) { yawMode == Mode.Custom }.group(Group.General)
42+
private val yawStep by setting("Yaw Step", 45.0, 1.0..180.0, 0.1) { yawMode == Mode.Snap }.group(Group.General)
43+
private val customYaw by setting("Custom Yaw", 0.0, -179.0..180.0, 0.1) { yawMode == Mode.Custom }.group(Group.General)
4444
@JvmStatic val pitchMode by setting("Pitch Mode", Mode.None).group(Group.General)
45-
private val pitchStep by setting("Pitch Step", 45.0, 1.0..90.0, 1.0) { pitchMode == Mode.Snap }.group(Group.General)
46-
private val customPitch by setting("Custom Pitch", 0.0, -90.0..90.0, 1.0) { pitchMode == Mode.Custom }.group(Group.General)
45+
private val pitchStep by setting("Pitch Step", 45.0, 1.0..90.0, 0.1) { pitchMode == Mode.Snap }.group(Group.General)
46+
private val customPitch by setting("Custom Pitch", 0.0, -90.0..90.0, 0.1) { pitchMode == Mode.Custom }.group(Group.General)
4747

4848
override val rotationConfig = RotationSettings(this, Group.Rotation).apply {
4949
applyEdits {

src/main/kotlin/com/lambda/util/BlockUtils.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package com.lambda.util
2020
import com.lambda.context.SafeContext
2121
import com.lambda.util.EnchantmentUtils.getEnchantment
2222
import com.lambda.util.player.gamemode
23+
import com.lambda.util.world.WorldUtils.isLoaded
2324
import net.minecraft.block.AbstractCauldronBlock
2425
import net.minecraft.block.AbstractFurnaceBlock
2526
import net.minecraft.block.AbstractSignBlock
@@ -358,4 +359,7 @@ object BlockUtils {
358359
fun BlockPos.vecOf(direction: Direction): Vec3d = toCenterPos().add(Vec3d.of(direction.vector).multiply(0.5))
359360
fun BlockPos.offset(eightWayDirection: EightWayDirection, amount: Int): BlockPos =
360361
add(eightWayDirection.offsetX * amount, 0, eightWayDirection.offsetZ * amount)
362+
context(safeContext: SafeContext)
363+
val BlockPos.isLoaded get() = safeContext.isLoaded(this)
364+
361365
}

src/main/kotlin/com/lambda/util/math/Vectors.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package com.lambda.util.math
1919

20+
import com.lambda.context.SafeContext
21+
import com.lambda.util.BlockUtils.isLoaded
2022
import com.lambda.util.math.MathUtils.floorToInt
2123
import com.lambda.util.math.MathUtils.sq
2224
import net.minecraft.entity.Entity
@@ -200,6 +202,9 @@ infix fun Entity.distSq(other: Vec3d): Double = pos distSq other
200202
infix fun Entity.distSq(other: Vec3i): Int = blockPos distSq other
201203
infix fun Entity.distSq(other: Entity): Double = squaredDistanceTo(other)
202204

205+
context(safeContext: SafeContext)
206+
val Vec3d.isLoaded get() = flooredBlockPos.isLoaded
207+
203208
val UP = Vec3d(0.0, 1.0, 0.0)
204209
val DOWN = Vec3d(0.0, -1.0, 0.0)
205210
val CENTER = Vec3d(0.5, 0.5, 0.5)

0 commit comments

Comments
 (0)