Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/kotlin/net/ccbluex/liquidbounce/LiquidBounce.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import net.ccbluex.liquidbounce.utils.inventory.InventoryManager
import net.ccbluex.liquidbounce.utils.kotlin.EventPriorityConvention.FIRST_PRIORITY
import net.ccbluex.liquidbounce.utils.kotlin.Minecraft
import net.ccbluex.liquidbounce.utils.mappings.EnvironmentRemapper
import net.ccbluex.liquidbounce.utils.network.LocalPlayerFallDamageTracker
import net.minecraft.resources.Identifier
import net.minecraft.server.packs.resources.PreparableReloadListener
import net.minecraft.server.packs.resources.ReloadableResourceManager
Expand Down Expand Up @@ -272,6 +273,7 @@ object LiquidBounce : EventListener {
// Utility managers
RotationManager
BlinkManager
LocalPlayerFallDamageTracker
InteractionTracker
CombatManager
FriendManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ import net.ccbluex.liquidbounce.config.types.group.ToggleableValueGroup
import net.ccbluex.liquidbounce.event.EventListener
import net.ccbluex.liquidbounce.event.events.AttackEntityEvent
import net.ccbluex.liquidbounce.event.events.MovementInputEvent
import net.ccbluex.liquidbounce.event.events.PacketEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleDebug
import net.ccbluex.liquidbounce.utils.math.multiply
import net.ccbluex.liquidbounce.utils.network.LocalPlayerFallDamageTracker
import net.minecraft.client.gui.screens.inventory.InventoryScreen
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket
import kotlin.random.Random

object VelocityIntave : VelocityMode("Intave") {

Expand Down Expand Up @@ -68,13 +67,14 @@ object VelocityIntave : VelocityMode("Intave") {
}

private val randomize = tree(Randomize())
private var isFallDamage = false
private var currentDelay = 0
private var delayCounter = 0

@Suppress("unused")
private val tickJumpHandler = handler<MovementInputEvent> {
val shouldJump = Math.random() * 100 < chance && player.hurtTime > 5 && !isFallDamage
val shouldJump = Random.nextInt(100) < chance
&& player.hurtTime > 5
&& !LocalPlayerFallDamageTracker.isCurrentFallDamage
val canJump = player.onGround() && mc.screen !is InventoryScreen
val shouldFinallyJump = shouldJump && canJump

Expand All @@ -91,26 +91,6 @@ object VelocityIntave : VelocityMode("Intave") {
}
}

@Suppress("unused")
private val packetHandler = handler<PacketEvent> { event ->
val packet = event.packet

if (packet is ClientboundSetEntityMotionPacket && packet.id == player.id) {
val velocityX = packet.movement.x
val velocityY = packet.movement.y
val velocityZ = packet.movement.z

// Check if the player is taking fall damage
// We set this on every packet, because if the player gets hit afterward,
// we will know that from the velocity.
isFallDamage = velocityX == 0.0 && velocityZ == 0.0 && velocityY < 0
ModuleDebug.debugParameter(this, "VelocityX", velocityX)
ModuleDebug.debugParameter(this, "VelocityY", velocityY)
ModuleDebug.debugParameter(this, "VelocityZ", velocityZ)
ModuleDebug.debugParameter(this, "IsFallDamage", isFallDamage)
}
}

}

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ package net.ccbluex.liquidbounce.features.module.modules.combat.velocity.mode

import net.ccbluex.liquidbounce.config.types.group.ToggleableValueGroup
import net.ccbluex.liquidbounce.event.events.MovementInputEvent
import net.ccbluex.liquidbounce.event.events.PacketEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleDebug
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket
import net.ccbluex.liquidbounce.utils.network.LocalPlayerFallDamageTracker
import kotlin.random.Random

/**
Expand All @@ -47,17 +46,15 @@ internal object VelocityJumpReset : VelocityMode("JumpReset") {
}

private var limitUntilJump = 0
private var isFallDamage = false

private var hitsUntilJump = JumpByReceivedHits.hitsUntilJump.random()
private var ticksUntilJump = JumpByDelay.ticksUntilJump.random()

@Suppress("ComplexCondition", "unused")
private val movementInputHandler = handler<MovementInputEvent> { event ->
// To be able to alter velocity when receiving knockback, player must be sprinting.
if (player.hurtTime != 9 || !player.onGround() || !player.isSprinting ||
isFallDamage || !isCooldownOver() || chance != 100f && Random.nextInt(100) > chance)
{
LocalPlayerFallDamageTracker.isCurrentFallDamage || !isCooldownOver() ||
chance != 100f && Random.nextInt(100) > chance) {
updateLimit()
return@handler
}
Expand All @@ -69,26 +66,6 @@ internal object VelocityJumpReset : VelocityMode("JumpReset") {
ticksUntilJump = JumpByDelay.ticksUntilJump.random()
}

@Suppress("unused")
private val packetHandler = handler<PacketEvent> { event ->
val packet = event.packet

if (packet is ClientboundSetEntityMotionPacket && packet.id == player.id) {
val velocityX = packet.movement.x
val velocityY = packet.movement.y
val velocityZ = packet.movement.z

// Check if the player is taking fall damage
// We set this on every packet, because if the player gets hit afterward,
// we will know that from the velocity.
isFallDamage = velocityX == 0.0 && velocityZ == 0.0 && velocityY < 0
ModuleDebug.debugParameter(this, "VelocityX", velocityX)
ModuleDebug.debugParameter(this, "VelocityY", velocityY)
ModuleDebug.debugParameter(this, "VelocityZ", velocityZ)
ModuleDebug.debugParameter(this, "IsFallDamage", isFallDamage)
}
}

private fun isCooldownOver(): Boolean {
ModuleDebug.debugParameter(this, "HitsUntilJump", hitsUntilJump)
ModuleDebug.debugParameter(this, "UntilJump", ticksUntilJump)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import net.ccbluex.liquidbounce.event.sequenceHandler
import net.ccbluex.liquidbounce.event.waitTicks
import net.ccbluex.liquidbounce.features.blink.BlinkManager
import net.ccbluex.liquidbounce.features.blink.BlinkManager.Action
import net.ccbluex.liquidbounce.utils.network.LocalPlayerFallDamageTracker
import net.ccbluex.liquidbounce.utils.network.isLocalPlayerVelocity
import net.minecraft.network.protocol.common.ClientboundKeepAlivePacket

Expand Down Expand Up @@ -89,6 +90,11 @@ internal object VelocityLag : VelocityMode("Lag") {
private val movementInputHandler = handler<MovementInputEvent> { event ->
// To be able to alter velocity when receiving knockback, player must be sprinting.
if (jumpReset && shouldJump && player.onGround() && player.isSprinting) {
if (LocalPlayerFallDamageTracker.isCurrentFallDamage) {
shouldJump = false
return@handler
}

event.jump = true
shouldJump = false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,7 @@ object VelocityReduce : VelocityMode("Reduce") {

if (packet.isLocalPlayerDamage()) {
receiveDamage = true
}

if (packet.isLocalPlayerVelocity() && receiveDamage) {
} else if (packet.isLocalPlayerVelocity() && receiveDamage) {
receiveDamage = false
if (player.isUsingItem || ModuleScaffold.running) return@handler

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce)
*
* Copyright (c) 2015 - 2026 CCBlueX
*
* LiquidBounce is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiquidBounce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LiquidBounce. If not, see <https://www.gnu.org/licenses/>.
*/

package net.ccbluex.liquidbounce.utils.network

import net.ccbluex.liquidbounce.event.EventListener
import net.ccbluex.liquidbounce.event.events.GameTickEvent
import net.ccbluex.liquidbounce.event.events.PacketEvent
import net.ccbluex.liquidbounce.event.events.TransferOrigin
import net.ccbluex.liquidbounce.event.events.WorldChangeEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.utils.client.mc
import net.ccbluex.liquidbounce.utils.kotlin.EventPriorityConvention.READ_FINAL_STATE
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket
import net.minecraft.world.damagesource.DamageTypes
import java.util.concurrent.atomic.AtomicInteger

/**
* Tracks whether the local player's current knockback/hurt cycle was caused by vanilla fall damage.
*
* Vanilla sends the local player's fall [net.minecraft.network.protocol.game.ClientboundDamageEventPacket]
* before the matching `hurtMarked`-driven [ClientboundSetEntityMotionPacket]. We only confirm fall damage
* after seeing that following motion packet.
*
* @see net.minecraft.world.entity.LivingEntity#hurt
* @see net.minecraft.server.level.ServerLevel#broadcastDamageEvent
* @see net.minecraft.server.level.ServerEntity#sendChanges
*/
object LocalPlayerFallDamageTracker : EventListener {

@Volatile
private var state = State.NONE

private val activeTicks = AtomicInteger()
private val pendingTicks = AtomicInteger()

val isCurrentFallDamage: Boolean
get() = state == State.CONFIRMED_FALL_DAMAGE && activeTicks.get() > 0

private fun reset() {
state = State.NONE
activeTicks.set(0)
pendingTicks.set(0)
}

@Suppress("unused")
private val packetHandler = handler<PacketEvent>(READ_FINAL_STATE) { event ->
if (event.origin != TransferOrigin.INCOMING || !event.original || event.isCancelled) {
return@handler
}

val packet = event.packet
when {
packet.isLocalPlayerDamage() -> {
activeTicks.set(0)
pendingTicks.set(0)
state = if (packet.sourceType.`is`(DamageTypes.FALL)) State.AWAITING_FALL_DAMAGE_MOTION else State.NONE
}

packet is ClientboundSetEntityMotionPacket && packet.id == mc.player?.id -> {
if (state == State.AWAITING_FALL_DAMAGE_MOTION) {
state = State.CONFIRMED_FALL_DAMAGE
activeTicks.set(DAMAGE_WINDOW_TICKS)
}
}
}
}

@Suppress("unused")
private val gameTickHandler = handler<GameTickEvent> {
when (state) {
State.AWAITING_FALL_DAMAGE_MOTION -> {
if (pendingTicks.incrementAndGet() > FOLLOWING_MOTION_TIMEOUT_TICKS) {
reset()
}
}

State.CONFIRMED_FALL_DAMAGE -> {
val remainingTicks = activeTicks.updateAndGet { ticks ->
if (ticks > 0) ticks - 1 else 0
}
if (remainingTicks <= 0) {
reset()
}
}

State.NONE -> {}
}
}

@Suppress("unused")
private val worldChangeHandler = handler<WorldChangeEvent> {
reset()
}

private enum class State {
NONE,
AWAITING_FALL_DAMAGE_MOTION,
CONFIRMED_FALL_DAMAGE,
}

private const val FOLLOWING_MOTION_TIMEOUT_TICKS = 1
private const val DAMAGE_WINDOW_TICKS = 10

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import net.minecraft.network.protocol.game.ServerboundContainerSlotStateChangedP
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
import net.minecraft.world.phys.Vec3
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

fun Packet<*>?.isC2SContainerPacket() =
this is ServerboundContainerClickPacket ||
Expand All @@ -40,7 +42,12 @@ fun Packet<*>?.isC2SContainerPacket() =
this is ServerboundContainerSlotStateChangedPacket ||
this is ServerboundContainerClosePacket

@OptIn(ExperimentalContracts::class)
fun Packet<*>?.isLocalPlayerDamage(): Boolean {
contract {
returns(true) implies (this@isLocalPlayerDamage is ClientboundDamageEventPacket)
}

return this is ClientboundDamageEventPacket && this.entityId == mc.player?.id
}

Expand Down
Loading