Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 85km/h YPort mode to Speed #288

Merged
merged 16 commits into from
Oct 29, 2022
Merged
Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ object HoleSnap : Module(
}

getHole()?.let {
if (disableStrafe && Speed.mode == Speed.SpeedMode.STRAFE) Speed.disable()
if (disableStrafe && Speed.mode.value == Speed.SpeedMode.STRAFE) Speed.disable()
if ((airStrafe || player.onGround) && !player.isCentered(it)) {
val playerPos = player.positionVector
val targetPos = Vec3d(it.x + 0.5, player.posY, it.z + 0.5)
Original file line number Diff line number Diff line change
@@ -101,7 +101,7 @@ object Surround : Module(
// Centered check
if (!player.centerPlayer()) return@safeListener

if (disableStrafe && Speed.mode == Speed.SpeedMode.STRAFE) {
if (disableStrafe && Speed.mode.value == Speed.SpeedMode.STRAFE) {
Speed.disable()
}

260 changes: 195 additions & 65 deletions src/main/kotlin/com/lambda/client/module/modules/movement/Speed.kt
Original file line number Diff line number Diff line change
@@ -1,150 +1,207 @@
package com.lambda.client.module.modules.movement

import com.lambda.client.commons.interfaces.DisplayEnum
import com.lambda.client.event.SafeClientEvent
import com.lambda.client.event.events.PacketEvent
import com.lambda.client.event.events.PlayerMoveEvent
import com.lambda.client.event.events.PlayerTravelEvent
import com.lambda.client.manager.managers.TimerManager.modifyTimer
import com.lambda.client.manager.managers.TimerManager.resetTimer
import com.lambda.client.mixin.extension.isInWeb
import com.lambda.client.mixin.extension.playerY
import com.lambda.client.module.Category
import com.lambda.client.module.Module
import com.lambda.client.module.modules.player.AutoEat
import com.lambda.client.util.BaritoneUtils
import com.lambda.client.util.EntityUtils.flooredPosition
import com.lambda.client.util.EntityUtils.isInOrAboveLiquid
import com.lambda.client.util.MovementUtils
import com.lambda.client.util.MovementUtils.applySpeedPotionEffects
import com.lambda.client.util.MovementUtils.calcMoveYaw
import com.lambda.client.util.MovementUtils.isMoving
import com.lambda.client.util.MovementUtils.setSpeed
import com.lambda.client.util.MovementUtils.speed
import com.lambda.client.util.TickTimer
import com.lambda.client.util.TimeUnit
import com.lambda.client.util.threads.runSafe
import com.lambda.client.util.threads.safeListener
import net.minecraft.client.settings.KeyBinding
import net.minecraft.network.play.client.CPacketPlayer
import net.minecraft.network.play.server.SPacketPlayerPosLook
import net.minecraftforge.fml.common.gameevent.TickEvent
import java.lang.Double.max
import java.lang.Double.min
import kotlin.math.cos
import kotlin.math.hypot
import kotlin.math.sin


object Speed : Module(
name = "Speed",
description = "Move faster",
category = Category.MOVEMENT,
modulePriority = 100
) {
// General settings
val mode by setting("Mode", SpeedMode.STRAFE)

// strafe settings
private val strafeAirSpeedBoost by setting("Air Speed Boost", 0.029f, 0.01f..0.04f, 0.001f, { mode == SpeedMode.STRAFE })
private val strafeTimerBoost by setting("Timer Boost", true, { mode == SpeedMode.STRAFE })
private val strafeAutoJump by setting("Auto Jump", true, { mode == SpeedMode.STRAFE })
private val strafeOnHoldingSprint by setting("On Holding Sprint", false, { mode == SpeedMode.STRAFE })
private val strafeCancelInertia by setting("Cancel Inertia", false, { mode == SpeedMode.STRAFE })

// onGround settings
private val onGroundTimer by setting("Timer", true, { mode == SpeedMode.ONGROUND })
private val onGroundTimerSpeed by setting("Timer Speed", 1.29f, 1.0f..2.0f, 0.01f, { mode == SpeedMode.ONGROUND && onGroundTimer })
private val onGroundSpeed by setting("Speed", 1.31f, 1.0f..2.0f, 0.01f, { mode == SpeedMode.ONGROUND })
private val onGroundSprint by setting("Sprint", true, { mode == SpeedMode.ONGROUND })
private val onGroundCheckAbove by setting("Smart Mode", true, { mode == SpeedMode.ONGROUND })
val mode = setting("Mode", SpeedMode.STRAFE)

// Strafe settings
private val strafeAirSpeedBoost by setting("Air Speed Boost", 0.028f, 0.01f..0.04f, 0.001f, { mode.value == SpeedMode.STRAFE })
private val strafeTimerBoost by setting("Timer Boost", true, { mode.value == SpeedMode.STRAFE })
private val strafeAutoJump by setting("Auto Jump", true, { mode.value == SpeedMode.STRAFE }, description = "WARNING: Food intensive!")
private val strafeOnlyOverhead by setting("Only strafe on overhead", false, { mode.value == SpeedMode.STRAFE && strafeAutoJump })
private val strafeOnHoldingSprint by setting("On Holding Sprint", false, { mode.value == SpeedMode.STRAFE })
private val strafeCancelInertia by setting("Cancel Inertia", false, { mode.value == SpeedMode.STRAFE })

// YPort settings
private val yPortAccelerate by setting("Accelerate", true, { mode.value == SpeedMode.YPORT })
private val yPortStrict by setting("Head Strict", false, { mode.value == SpeedMode.YPORT }, description = "Only allow YPort when you are under a block")
private val yPortAirStrict by setting("Air Strict", false, { mode.value == SpeedMode.YPORT }, description = "Force YPort to handle Y movement differently, slows this down A LOT")
private val yPortMaxSpeed by setting("Maximum Speed", 0.0, 0.0..2.0, 0.001, { mode.value == SpeedMode.YPORT })
private val yPortAcceleration by setting("Acceleration Speed", 2.149, 1.0..5.0, 0.001, { mode.value == SpeedMode.YPORT })
private val yPortDecay by setting("Decay Amount", 0.66, 0.0..1.0, 0.001, { mode.value == SpeedMode.YPORT })

private const val TIMER_SPEED = 45.955883f

// Strafe Mode
private var jumpTicks = 0
private val strafeTimer = TickTimer(TimeUnit.TICKS)

// onGround Mode
private var wasSprintEnabled = Sprint.isEnabled
// yport stuff
private var currentSpeed = .2873
private var currentY = 0.0
private var phase: YPortPhase = YPortPhase.WALKING
private var prevPhase: YPortPhase = YPortPhase.WALKING
private var goUp = false
private var lastDistance = 0.0

private var currentMode = mode
private enum class YPortPhase {
// to help with bypassing right after setback
WAITING,
// to get some speed initially
WALKING,
// to jump and accelerate
ACCELERATING,
// to fall to the ground
SLOWDOWN,
// to slowly fall to the ground
FALLING
}

enum class SpeedMode {
STRAFE, ONGROUND
enum class SpeedMode(override val displayName: String) : DisplayEnum {
STRAFE("Strafe"),
YPORT("YPort")
}

init {
onEnable {
wasSprintEnabled = Sprint.isEnabled
currentSpeed = .2873
phase = YPortPhase.WALKING
prevPhase = YPortPhase.WALKING
goUp = false
currentY = 0.0
}

onDisable {
if (!wasSprintEnabled && mode == SpeedMode.ONGROUND) Sprint.disable()
runSafe {
reset()
}
}

safeListener<TickEvent.ClientTickEvent> {
if (mode != currentMode) {
currentMode = mode
reset()
}

if (mode == SpeedMode.ONGROUND && Sprint.isDisabled && onGroundSprint) Sprint.enable()
}

safeListener<PlayerTravelEvent> {
if (mode == SpeedMode.STRAFE && shouldStrafe()) strafe()
lastDistance = hypot(player.posX - player.prevPosX, player.posZ - player.prevPosZ)
if (mode.value == SpeedMode.STRAFE
&& shouldStrafe()
) strafe()
}

safeListener<PlayerMoveEvent> {
when (mode) {
when (mode.value) {
SpeedMode.STRAFE -> {
if (shouldStrafe()) setSpeed(max(player.speed, applySpeedPotionEffects(0.2873)))
else {
if (shouldStrafe()) {
setSpeed(max(player.speed, applySpeedPotionEffects(0.2873)))
} else {
reset()
if (strafeCancelInertia && !strafeTimer.tick(2L, false)) {
player.motionX = 0.0
player.motionZ = 0.0
}
}
}
SpeedMode.ONGROUND -> {
if (shouldOnGround()) onGround()
else resetTimer()

SpeedMode.YPORT -> {
handleBoost(it)
}
}
}

safeListener<PacketEvent.Send> {
if (mode.value != SpeedMode.YPORT
|| it.packet !is CPacketPlayer
|| !goUp
) return@safeListener

var offset = .42

//.015625 is the largest number that block heights are always divisible
while (world.collidesWithAnyBlock(player.entityBoundingBox.offset(.0, offset, .0))) {
if (offset <= 0)
break

offset -= .015625
}

val unModOffset = offset

if (currentY + unModOffset > 0)
offset += currentY
else if (yPortAirStrict && phase == YPortPhase.FALLING && prevPhase == YPortPhase.FALLING) {

var predictedY = currentY
predictedY -= 0.08
predictedY *= 0.9800000190734863 // 0.333200006 vs 0.341599999

if (predictedY + player.posY <= player.posY) {
phase = YPortPhase.WAITING
}
}

it.packet.playerY = (offset + player.posY)

currentY = offset - unModOffset
}

safeListener<PacketEvent.Receive> {
if (mode.value != SpeedMode.YPORT || it.packet !is SPacketPlayerPosLook) return@safeListener

currentSpeed = 0.0
currentY = 0.0
goUp = false
// 3 extra ticks at base speed
phase = YPortPhase.WAITING
}

mode.listeners.add {
runSafe { reset() }
}
}

private fun SafeClientEvent.strafe() {
player.jumpMovementFactor = strafeAirSpeedBoost
if (strafeTimerBoost) modifyTimer(45.87156f)
if ((Step.isDisabled || !player.collidedHorizontally) && strafeAutoJump) jump()
// slightly slower timer speed bypasses better (1.088)
if (strafeTimerBoost) modifyTimer(TIMER_SPEED)

strafeTimer.reset()
}
if ((Step.isDisabled || player.onGround) && strafeAutoJump) jump()

private fun SafeClientEvent.onGround() {
if (onGroundTimer) modifyTimer(50.0f / onGroundTimerSpeed)
else resetTimer()

player.motionX *= onGroundSpeed
player.motionZ *= onGroundSpeed
strafeTimer.reset()
}

private fun SafeClientEvent.shouldStrafe(): Boolean =
(!player.capabilities.isFlying
!player.capabilities.isFlying
&& !player.isElytraFlying
&& !mc.gameSettings.keyBindSneak.isKeyDown
&& (!strafeOnHoldingSprint || mc.gameSettings.keyBindSprint.isKeyDown)
&& !BaritoneUtils.isPathing
&& MovementUtils.isInputting
&& !(player.isInOrAboveLiquid || player.isInWeb))

private fun SafeClientEvent.shouldOnGround(): Boolean =
(world.getBlockState(player.flooredPosition.add(0.0, 2.0, 0.0)).material.isSolid || !onGroundCheckAbove)
&& !AutoEat.eating
&& player.isMoving
&& MovementUtils.isInputting
&& !player.movementInput.sneak
&& player.onGround
&& !(player.isInOrAboveLiquid || player.isInWeb)
&& !player.capabilities.isFlying
&& !player.isElytraFlying
&& !mc.gameSettings.keyBindSneak.isKeyDown
&& (!strafeOnlyOverhead || world.collidesWithAnyBlock(player.entityBoundingBox.offset(.0,.42,.0)))

private fun SafeClientEvent.reset() {
player.jumpMovementFactor = 0.02f
@@ -168,4 +225,77 @@ object Speed : Module(

jumpTicks--
}
}

private fun SafeClientEvent.handleBoost(event: PlayerMoveEvent) {
if (player.movementInput.moveForward == 0f && player.movementInput.moveStrafe == 0f
|| player.isInOrAboveLiquid
|| mc.gameSettings.keyBindJump.isKeyDown
|| !player.onGround
|| !world.collidesWithAnyBlock(player.entityBoundingBox.offset(0.0, 0.42, 0.0)) && yPortStrict
) {
resetTimer()
currentSpeed = .2873
return
}

modifyTimer(TIMER_SPEED)

prevPhase = phase

when (phase) {
YPortPhase.ACCELERATING -> {
// NCP says hDistance < 2.15 * hDistanceBaseRef
currentSpeed *= yPortAcceleration
phase = if (yPortAirStrict) YPortPhase.FALLING else YPortPhase.SLOWDOWN
goUp = true
currentY = 0.0
}

YPortPhase.SLOWDOWN -> {
// NCP says hDistDiff >= 0.66 * (lastMove.hDistance - hDistanceBaseRef)
currentSpeed = if (yPortAccelerate) {
lastDistance - yPortDecay * (lastDistance - .2873)
} else {
.2873
}
phase = YPortPhase.ACCELERATING
goUp = false
}

YPortPhase.FALLING -> {
if (prevPhase == YPortPhase.WALKING) {
currentSpeed = if (yPortAccelerate) {
lastDistance - yPortDecay * (lastDistance - .2873)
} else {
.2873
}
}

goUp = true

currentSpeed -= currentSpeed / 159

currentY -= 0.08
currentY *= 0.9800000190734863
}

else -> {
currentSpeed = max(currentSpeed, .2873)
phase = YPortPhase.values()[phase.ordinal + 1 % YPortPhase.values().size]
goUp = false
}
}

val yaw = calcMoveYaw()

if (yPortMaxSpeed != 0.0) {
currentSpeed = currentSpeed.coerceAtMost(yPortMaxSpeed)
}

event.x = -sin(yaw) * currentSpeed
event.y = min(0.0, event.y)
event.z = cos(yaw) * currentSpeed

player.setVelocity(event.x, event.y, event.z)
}
}