Overview
The RotationManager handles all rotation-related functionality in LiquidBounce, including smooth rotation transitions, server-side rotation tracking, and movement correction.
Package: net.ccbluex.liquidbounce.utils.aiming
Object Declaration
object RotationManager : EventListener
Properties
The rotation we want to aim at (client-side, not yet confirmed by server).var currentRotation: Rotation?
Previous rotation for interpolation.var previousRotation: Rotation?
Rotation confirmed by the server (accounting for fake lag/freeze).val serverRotation: Rotation
The actual rotation sent to and acknowledged by the server.var actualServerRotation: Rotation
private set
Currently active rotation target or previous target if transitioning.val activeRotationTarget: RotationTarget?
Core Methods
setRotationTarget
fun setRotationTarget(
rotation: Rotation,
considerInventory: Boolean = true,
valueGroup: RotationsValueGroup,
priority: Priority,
provider: ClientModule,
whenReached: RestrictedSingleUseAction? = null
)
Sets a rotation target with full configuration.
Target rotation (yaw, pitch)
If true, rotation updates are blocked when inventory is open
valueGroup
RotationsValueGroup
required
Rotation configuration (speed, smoothing, etc.)
Priority for rotation conflicts
Module requesting the rotation
whenReached
RestrictedSingleUseAction?
default:"null"
Callback when rotation is reached
Example:
RotationManager.setRotationTarget(
rotation = Rotation(player.yRot + 45f, player.xRot),
considerInventory = true,
valueGroup = rotationsConfigurable,
priority = Priority.IMPORTANT,
provider = this,
whenReached = {
// Rotation reached, attack entity
mc.gameMode?.attack(player, target)
}
)
setRotationTarget (RotationTarget)
fun setRotationTarget(
plan: RotationTarget,
priority: Priority,
provider: ClientModule
)
Sets a pre-configured rotation target.
isRotatingAllowed
fun isRotatingAllowed(rotationTarget: RotationTarget): Boolean
Checks if rotation updates are currently allowed.
update
Updates rotation to next step. Called automatically every tick.
rotationMatchesPreviousRotation
fun rotationMatchesPreviousRotation(): Boolean
Checks if current rotation matches previous rotation.
Rotation Class
data class Rotation(
val yaw: Float,
val pitch: Float,
val isNormalized: Boolean = false
)
Horizontal rotation (-180 to 180 degrees)
Vertical rotation (-90 to 90 degrees)
Rotation Methods
fun normalize(): Rotation
fun angleTo(other: Rotation): Float
RotationsValueGroup
Configuration for rotation behavior:
class RotationsValueGroup(...) {
val rotationSpeed by float("Speed", 180f, 0f..180f)
val smoothMode by enumChoice("SmoothMode", ...)
val resetThreshold by float("ResetThreshold", 2f, 0f..180f)
}
Movement Correction
RotationManager automatically corrects movement when rotating:
enum class MovementCorrection {
OFF, // No correction
CHANGE_LOOK, // Change camera look
SILENT // Silent correction (movement only)
}
Usage Examples
Basic Rotation
object ModuleAimAssist : ClientModule(...) {
val rotations = tree(RotationsConfigurable())
private val tickHandler = handler<GameTickEvent> {
val target = findTarget() ?: return@handler
val targetRotation = aimAtEntity(target)
RotationManager.setRotationTarget(
rotation = targetRotation,
considerInventory = true,
valueGroup = rotations,
priority = Priority.IMPORTANT,
provider = this
)
}
}
Rotation with Callback
val targetRotation = aimAtBlock(targetPos)
RotationManager.setRotationTarget(
rotation = targetRotation,
considerInventory = true,
valueGroup = rotations,
priority = Priority.IMPORTANT,
provider = this,
whenReached = {
// Place block when rotation reached
placeBlock(targetPos)
}
)
Checking Server Rotation
val serverRot = RotationManager.serverRotation
val currentRot = RotationManager.currentRotation
if (serverRot != currentRot) {
// Server hasn't received our rotation yet
// Wait before attacking
}
Smooth Rotation to Target
private fun aimAtEntity(entity: Entity): Rotation {
val eyePos = player.getEyePosition(1.0f)
val targetPos = entity.boundingBox.center
val diffX = targetPos.x - eyePos.x
val diffY = targetPos.y - eyePos.y
val diffZ = targetPos.z - eyePos.z
val distance = sqrt(diffX * diffX + diffZ * diffZ)
val yaw = Math.toDegrees(atan2(diffZ, diffX)).toFloat() - 90f
val pitch = -Math.toDegrees(atan2(diffY, distance)).toFloat()
return Rotation(yaw, pitch)
}
Priority System
enum class Priority(val priority: Int) {
NOT_IMPORTANT(0),
NORMAL(10),
IMPORTANT(20),
IMPORTANT_FOR_PLAYER_LIFE(30),
IMPORTANT_FOR_USAGE_CORRECTNESS(40)
}
Higher priority rotations override lower priority ones.
Best Practices
- Use appropriate priority - Combat modules should use IMPORTANT or higher
- Consider inventory state - Set
considerInventory = true for combat
- Use callbacks -
whenReached for precise timing
- Check server rotation - Ensure rotation was sent before critical actions
- Normalize rotations - Always call
.normalize() on custom rotations
- Use movement correction - SILENT for most cases, CHANGE_LOOK for legit
Integration with Combat
RotationManager integrates with CombatManager:
if (CombatManager.shouldPauseRotation) {
// Rotation updates paused (e.g., player eating)
return
}
Integration with InventoryManager
Rotations can be blocked when inventory is open:
if (rotationTarget.considerInventory && InventoryManager.isInventoryOpen) {
// Don't update rotation
return
}
Rotation Interpolation
RotationManager smoothly interpolates between rotations:
private fun interpolate(
from: Rotation,
to: Rotation,
speed: Float
): Rotation {
val diff = to - from
val step = diff.coerceIn(-speed, speed)
return from + step
}
See Also