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

fix(BlockPlacer/MLG): add offhand support #5960

Merged
merged 18 commits into from
Apr 12, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ object ModuleAutoShoot : ClientModule("AutoShoot", Category.COMBAT) {

// If both is false, we have to find the item in the hotbar
return if (!mainHand && !offHand) {
val throwableSlot = Slots.Hotbar.findSlotIndex(item) ?: return null
val throwableSlot = Slots.Hotbar.findSlot(item)?.hotbarSlot ?: return null
Hand.MAIN_HAND to throwableSlot
} else if (offHand) {
Hand.OFF_HAND to -1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import net.ccbluex.liquidbounce.features.module.ClientModule
import net.ccbluex.liquidbounce.utils.client.SilentHotbar
import net.ccbluex.liquidbounce.utils.entity.warp
import net.ccbluex.liquidbounce.utils.inventory.Slots
import net.ccbluex.liquidbounce.utils.inventory.findClosestSlot
import net.minecraft.item.Items
import net.minecraft.util.shape.VoxelShapes
import kotlin.math.abs
Expand All @@ -50,9 +51,9 @@ object ModuleMaceKill : ClientModule("MaceKill", Category.COMBAT) {

if (mainHandStack.item != Items.MACE) {
// Auto Select Mace
val maceIndex = Slots.Hotbar.findSlotIndex(Items.MACE) ?: return@handler
val maceIndex = Slots.Hotbar.findClosestSlot(Items.MACE) ?: return@handler

SilentHotbar.selectSlotSilently(this, maceIndex, 1)
SilentHotbar.selectSlotSilently(this, maceIndex) { 1 }
}

val height = determineHeight()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.Category
import net.ccbluex.liquidbounce.features.module.ClientModule
import net.ccbluex.liquidbounce.features.module.modules.combat.autoarmor.AutoArmorSaveArmor.durabilityThreshold
import net.ccbluex.liquidbounce.utils.inventory.ArmorItemSlot
import net.ccbluex.liquidbounce.utils.inventory.HotbarItemSlot
import net.ccbluex.liquidbounce.utils.inventory.*
import net.ccbluex.liquidbounce.utils.item.ArmorPiece
Expand Down Expand Up @@ -111,7 +110,7 @@ object ModuleAutoArmor : ClientModule("AutoArmor", Category.COMBAT) {
isInArmorSlot: Boolean
): InventoryAction {
val inventorySlot = armorPiece.itemSlot
val armorPieceSlot = if (isInArmorSlot) ArmorItemSlot(armorPiece.entitySlotId) else inventorySlot
val armorPieceSlot = if (isInArmorSlot) Slots.Armor[armorPiece.entitySlotId] else inventorySlot

val canTryHotbarMove = booleanArrayOf(
UseHotbar.enabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ object SubmoduleBasePlace : ToggleableConfigurable(ModuleCrystalAura, "BasePlace
"Placing",
ModuleCrystalAura,
Priority.IMPORTANT_FOR_USAGE_2,
slotFinder = { _ -> Slots.Hotbar.findSlot(Items.OBSIDIAN) ?: Slots.Hotbar.findSlot(Items.BEDROCK) }
slotFinder = { _ ->
Slots.OffhandWithHotbar.findSlot(Items.OBSIDIAN) ?: Slots.OffhandWithHotbar.findSlot(Items.BEDROCK)
}
))

var currentTarget: PlacementPositionCandidate? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import net.ccbluex.liquidbounce.utils.block.SwingMode
import net.ccbluex.liquidbounce.utils.block.getState
import net.ccbluex.liquidbounce.utils.client.Chronometer
import net.ccbluex.liquidbounce.utils.client.clickBlockWithSlot
import net.ccbluex.liquidbounce.utils.inventory.OffHandSlot
import net.ccbluex.liquidbounce.utils.inventory.Slots
import net.ccbluex.liquidbounce.utils.inventory.findClosestSlot
import net.ccbluex.liquidbounce.utils.render.placement.PlacementRenderer
import net.minecraft.item.Items
import net.minecraft.util.hit.BlockHitResult
Expand Down Expand Up @@ -210,11 +210,7 @@ object SubmoduleCrystalPlacer : ToggleableConfigurable(ModuleCrystalAura, "Place
}

private fun getSlot(): Int? {
return if (OffHandSlot.itemStack.item == Items.END_CRYSTAL) {
OffHandSlot.hotbarSlotForServer
} else {
Slots.Hotbar.findSlotIndex(Items.END_CRYSTAL)
}
return Slots.OffhandWithHotbar.findClosestSlot(Items.END_CRYSTAL)?.hotbarSlotForServer
}

fun getMaxRange() = max(range, wallsRange)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import net.ccbluex.liquidbounce.event.events.PlayerNetworkMovementTickEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.exploit.disabler.ModuleDisabler
import net.ccbluex.liquidbounce.utils.inventory.Slots
import net.ccbluex.liquidbounce.utils.inventory.hasItem
import net.minecraft.item.Items
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket
Expand All @@ -46,7 +47,7 @@ internal object DisablerHypixel : ToggleableConfigurable(ModuleDisabler, "Hypixe
private val notWhenStarAvailable by boolean("NotWithStar", true)

private val hasStar
get() = Slots.Hotbar.findSlotIndex(Items.NETHER_STAR) != null
get() = Slots.Hotbar.hasItem(Items.NETHER_STAR)

@Suppress("unused")
private val networkTickHandler = handler<PlayerNetworkMovementTickEvent> { event ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import java.util.*
object DupePaper1204 : DupeExploit("Paper-1.20.4") {

override fun enable() {
val slot = Slots.Hotbar.findSlotIndex(Items.WRITABLE_BOOK) ?: run {
val slot = Slots.Hotbar.findSlot(Items.WRITABLE_BOOK)?.hotbarSlot ?: run {
chat(markAsError(ModuleDupe.requiresItem(Items.WRITABLE_BOOK)))
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ object ModuleElytraSwap : ClientModule(
private val constraints = tree(PlayerInventoryConstraints())

private val slotsToSearch = Slots.Hotbar + Slots.Inventory + Slots.OffHand
private val chestplateSlot = ArmorItemSlot(2)
private val chestplateSlot = Slots.Armor[2]

@Suppress("unused")
private val scheduleInventoryActionHandler = handler<ScheduleInventoryActionEvent>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal object FlyEnderpearl : Choice("Enderpearl") {
}

val repeatable = tickHandler {
val slot = Slots.Hotbar.findSlotIndex(Items.ENDER_PEARL)
val slot = Slots.Hotbar.findSlot(Items.ENDER_PEARL)?.hotbarSlot

if (player.isDead || player.isSpectator || player.abilities.creativeMode) {
return@tickHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,18 @@ object ModuleSmartEat : ClientModule("SmartEat", Category.PLAYER) {
private val notDuringCombat by boolean("NotDuringCombat", false)

private object Estimator {
fun findBestFood(): HotbarItemSlot? {
val comparator = ComparatorChain<Pair<HotbarItemSlot, FoodEstimationData>>(
// If there is an indication for a special item, we should use it. Items with lower health threshold
// are preferred since their usage is probably more urgent.
compareByDescending { it.second.healthThreshold },
compareBy { it.second.restoredHunger },
// Use the closest slot
compareByDescending { (it.first.hotbarSlot - SilentHotbar.serversideSlot).absoluteValue },
// Just for stabilization reasons
compareBy { SilentHotbar.serversideSlot }
)
private val comparator = ComparatorChain<Pair<HotbarItemSlot, FoodEstimationData>>(
// If there is an indication for a special item, we should use it. Items with lower health threshold
// are preferred since their usage is probably more urgent.
compareByDescending { it.second.healthThreshold },
compareBy { it.second.restoredHunger },
// Use the closest slot
compareByDescending { (it.first.hotbarSlot - SilentHotbar.serversideSlot).absoluteValue },
// Just for stabilization reasons
compareBy { SilentHotbar.serversideSlot }
)

fun findBestFood(): HotbarItemSlot? {
return Slots.Hotbar
.mapNotNull { slot -> getFoodEstimationData(slot.itemStack)?.let { slot to it } }
.maxWithOrNull(comparator)?.first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,18 @@ package net.ccbluex.liquidbounce.features.module.modules.player.autobuff

import net.ccbluex.liquidbounce.config.types.ToggleableConfigurable
import net.ccbluex.liquidbounce.event.Sequence
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.ModuleAutoBuff.AutoSwap
import net.ccbluex.liquidbounce.utils.inventory.HotbarItemSlot
import net.ccbluex.liquidbounce.utils.inventory.OffHandSlot
import net.ccbluex.liquidbounce.utils.client.SilentHotbar
import net.ccbluex.liquidbounce.utils.combat.CombatManager
import net.ccbluex.liquidbounce.utils.inventory.InventoryManager
import net.ccbluex.liquidbounce.utils.inventory.Slots
import net.ccbluex.liquidbounce.utils.inventory.findClosestSlot
import net.minecraft.item.ItemStack

abstract class Buff(
name: String,
val isValidItem: (ItemStack, Boolean) -> Boolean,
) : ToggleableConfigurable(ModuleAutoBuff, name, true) {

internal open val passesRequirements: Boolean
Expand All @@ -47,46 +48,32 @@ abstract class Buff(
return false
}

// Check main hand for item
val mainHandStack = player.mainHandStack
if (isValidItem(mainHandStack, true)) {
CombatManager.pauseCombatForAtLeast(ModuleAutoBuff.combatPauseTime)
execute(sequence, HotbarItemSlot(player.inventory.selectedSlot))
return true
}

// Check off-hand for item
val offHandStack = player.offHandStack
if (isValidItem(offHandStack, true)) {
CombatManager.pauseCombatForAtLeast(ModuleAutoBuff.combatPauseTime)
execute(sequence, OffHandSlot)
return true
}

// Check if we should auto swap
ModuleAutoBuff.AutoSwap.takeIf { autoSwap -> autoSwap.enabled }?.run {
// Check if the item is in the hotbar
val slot = Slots.Hotbar.findSlotIndex { stack: ItemStack -> isValidItem(stack, true) }
// Check if the item is in the hotbar
val slot = Slots.OffhandWithHotbar.findClosestSlot { isValidItem(it, true) } ?: return false

if (slot != null) {
CombatManager.pauseCombatForAtLeast(ModuleAutoBuff.combatPauseTime)
CombatManager.pauseCombatForAtLeast(ModuleAutoBuff.combatPauseTime)

// todo: do not hardcode ticksUntilReset
SilentHotbar.selectSlotSilently(ModuleAutoBuff, slot, ticksUntilReset = 300)
sequence.waitTicks(delayIn.random())
execute(sequence, HotbarItemSlot(slot))
sequence.waitTicks(delayOut.random())
SilentHotbar.resetSlot(ModuleAutoBuff)
return true
}
if (slot.isSelected || slot is OffHandSlot) {
// Check main hand and offhand
execute(sequence, slot)
return true
} else if (AutoSwap.enabled) {
// Check if we should auto swap
// todo: do not hardcode ticksUntilReset
SilentHotbar.selectSlotSilently(ModuleAutoBuff, slot) { 300 }
sequence.waitTicks(AutoSwap.delayIn.random())
execute(sequence, slot)
sequence.waitTicks(AutoSwap.delayOut.random())
SilentHotbar.resetSlot(ModuleAutoBuff)
return true
} else {
return false
}
return false
}

abstract suspend fun execute(sequence: Sequence, slot: HotbarItemSlot)
abstract fun isValidItem(stack: ItemStack, forUse: Boolean): Boolean

internal fun getStack(slot: Int): ItemStack =
if (slot == -1) player.offHandStack else player.inventory.getStack(slot)
abstract suspend fun execute(sequence: Sequence, slot: HotbarItemSlot)

}

Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@

package net.ccbluex.liquidbounce.features.module.modules.player.autobuff

import net.minecraft.item.ItemStack

abstract class HealthBasedBuff(name: String, isValidItem: (ItemStack, Boolean) -> Boolean) : Buff(name, isValidItem) {
abstract class HealthBasedBuff(name: String) : Buff(name) {

private val healthPercent by int("Health", 40, 1..100, "%HP")
private val considerAbsorption by boolean("ConsiderAbsorption", true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import net.ccbluex.liquidbounce.event.Sequence
import net.ccbluex.liquidbounce.event.events.KeybindIsPressedEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.Buff
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.features.Drink.isPotion
import net.ccbluex.liquidbounce.utils.inventory.HotbarItemSlot
import net.ccbluex.liquidbounce.utils.item.getPotionEffects
import net.ccbluex.liquidbounce.utils.item.isNothing
Expand All @@ -36,7 +35,7 @@ import net.minecraft.item.ItemStack
import net.minecraft.item.PotionItem
import net.minecraft.item.SplashPotionItem

object Drink : Buff("Drink", isValidItem = { stack, forUse -> isPotion(stack, forUse) }) {
object Drink : Buff("Drink") {

private object HealthPotion : ToggleableConfigurable(Drink, "HealthPotion", true) {
private val healthPercent by int("Health", 40, 1..100, "%HP")
Expand Down Expand Up @@ -82,7 +81,7 @@ object Drink : Buff("Drink", isValidItem = { stack, forUse -> isPotion(stack, fo
super.disable()
}

private fun isPotion(stack: ItemStack, forUse: Boolean): Boolean {
override fun isValidItem(stack: ItemStack, forUse: Boolean): Boolean {
if (stack.isNothing() || !isValidPotion(stack)) {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,17 @@ import net.ccbluex.liquidbounce.event.events.KeybindIsPressedEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.HealthBasedBuff
import net.ccbluex.liquidbounce.utils.inventory.HotbarItemSlot
import net.minecraft.item.ItemStack
import net.minecraft.item.Items

object Gapple : HealthBasedBuff("Gapple", isValidItem = { stack, _ -> stack.item == Items.GOLDEN_APPLE }) {
object Gapple : HealthBasedBuff("Gapple") {

private var forceUseKey = false

override fun isValidItem(stack: ItemStack, forUse: Boolean): Boolean {
return stack.item == Items.GOLDEN_APPLE
}

override suspend fun execute(sequence: Sequence, slot: HotbarItemSlot) {
forceUseKey = true
sequence.waitUntil { !passesRequirements }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,21 @@ import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.HealthBa
import net.ccbluex.liquidbounce.utils.inventory.HotbarItemSlot
import net.ccbluex.liquidbounce.utils.client.Chronometer
import net.ccbluex.liquidbounce.utils.inventory.useHotbarSlotOrOffhand
import net.minecraft.item.ItemStack
import net.minecraft.item.Items

object Head : HealthBasedBuff("Head", isValidItem = { stack, _ -> stack.item == Items.PLAYER_HEAD }) {
object Head : HealthBasedBuff("Head") {

val cooldown by int("Cooldown", 0, 0..120, "s")
val chronometer = Chronometer()

override val passesRequirements: Boolean
get() = passesHealthRequirements && chronometer.hasElapsed(cooldown * 1000L)

override fun isValidItem(stack: ItemStack, forUse: Boolean): Boolean {
return stack.item == Items.PLAYER_HEAD
}

override suspend fun execute(sequence: Sequence, slot: HotbarItemSlot) {
useHotbarSlotOrOffhand(slot)
chronometer.reset()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import net.ccbluex.liquidbounce.event.Sequence
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.Buff
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.ModuleAutoBuff
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.ModuleAutoBuff.AutoBuffRotationsConfigurable.RotationTimingMode.*
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.features.Pot.isPotion
import net.ccbluex.liquidbounce.utils.aiming.RotationManager
import net.ccbluex.liquidbounce.utils.aiming.RotationManager.currentRotation
import net.ccbluex.liquidbounce.utils.aiming.data.Rotation
Expand All @@ -50,7 +49,7 @@ import net.minecraft.item.ItemStack
import net.minecraft.item.LingeringPotionItem
import net.minecraft.item.SplashPotionItem

object Pot : Buff("Pot", isValidItem = { stack, forUse -> isPotion(stack, forUse) }) {
object Pot : Buff("Pot") {

private const val BENEFICIAL_SQUARE_RANGE = 16.0

Expand Down Expand Up @@ -163,7 +162,7 @@ object Pot : Buff("Pot", isValidItem = { stack, forUse -> isPotion(stack, forUse
sequence.waitTicks(1)
}

private fun isPotion(stack: ItemStack, forUse: Boolean): Boolean {
override fun isValidItem(stack: ItemStack, forUse: Boolean): Boolean {
if (stack.isNothing() || !isValidPotion(stack)) {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import net.ccbluex.liquidbounce.config.types.ToggleableConfigurable
import net.ccbluex.liquidbounce.event.events.ScheduleInventoryActionEvent
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.ModuleAutoBuff
import net.ccbluex.liquidbounce.features.module.modules.player.autobuff.ModuleAutoBuff.features
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.ItemSlotType
import net.ccbluex.liquidbounce.utils.inventory.*
import net.ccbluex.liquidbounce.utils.item.isNothing
import net.ccbluex.liquidbounce.utils.kotlin.Priority
Expand All @@ -40,13 +39,12 @@ object Refill : ToggleableConfigurable(ModuleAutoBuff, "Refill", true) {
return
}

val validFeatures = features.filter { it.enabled }

// Find valid items in the inventory
val validItems = Slots.Inventory.filter {
it.itemStack.let { itemStack ->
features.any { f ->
f.isValidItem(itemStack, false)
}
}
val itemStack = it.itemStack
validFeatures.any { f -> f.isValidItem(itemStack, false) }
}

// Check if we have any valid items
Expand All @@ -64,9 +62,7 @@ object Refill : ToggleableConfigurable(ModuleAutoBuff, "Refill", true) {
}

private fun findEmptyHotbarSlot(): Boolean {
return Slots.All.find {
it.slotType == ItemSlotType.HOTBAR && it.itemStack.isNothing()
} != null
return Slots.Hotbar.findSlot { it.isNothing() } != null
}

}
Loading
Loading