From 32530066691cee95ad349eba1906bb92dbfd648f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Feb 2025 01:38:39 +0000 Subject: [PATCH 001/364] build task packet mine start (not working) --- .../com/lambda/config/groups/BuildConfig.kt | 22 +- .../com/lambda/config/groups/BuildSettings.kt | 11 +- .../lambda/config/groups/InventoryConfig.kt | 2 + .../lambda/config/groups/InventorySettings.kt | 1 + .../construction/context/BreakContext.kt | 50 +++- .../construction/context/BuildContext.kt | 4 +- .../construction/context/PlaceContext.kt | 29 +-- .../construction/result/BreakResult.kt | 11 +- .../construction/result/BuildResult.kt | 1 + .../construction/simulation/BuildSimulator.kt | 42 ++- .../material/container/ContainerManager.kt | 24 +- .../container/containers/MainHandContainer.kt | 2 +- .../request/hotbar/HotbarManager.kt | 4 + .../request/hotbar/HotbarRequest.kt | 2 +- .../module/modules/client/TaskFlowModule.kt | 3 +- .../com/lambda/task/tasks/AcquireMaterial.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 244 ++++++++++++++++-- .../main/kotlin/com/lambda/util/BlockUtils.kt | 71 +++++ .../kotlin/com/lambda/util/item/ItemUtils.kt | 14 + .../com/lambda/util/player/SlotUtils.kt | 3 + .../src/main/resources/lambda.accesswidener | 2 + 21 files changed, 435 insertions(+), 109 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 65590984f..3c66c5c0a 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -28,15 +28,33 @@ interface BuildConfig { val interactionTimeout: Int // Breaking + val breakMode: BreakMode + val breakThreshold: Float + val doubleBreak: Boolean + val breakDelay: Int + val sounds: Boolean + val particles: Boolean + val breakingTexture: Boolean val rotateForBreak: Boolean - val breakConfirmation: Boolean + val breakConfirmation: BreakConfirmationMode val breaksPerTick: Int - val breakWeakBlocks: Boolean val forceSilkTouch: Boolean val ignoredBlocks: Set + val breakWeakBlocks: Boolean + // Placing val rotateForPlace: Boolean val placeConfirmation: Boolean val placementsPerTick: Int + + enum class BreakMode { + Vanilla, Packets + } + + enum class BreakConfirmationMode { + None, + AwaitThenBreak, + BreakThenAwait + } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 88251114b..bed2adab3 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -37,8 +37,15 @@ class BuildSettings( override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking + override val breakMode by c.setting("Break Mode", BuildConfig.BreakMode.Vanilla) + override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.1f, "The break amount at which the block is considered broken") { vis() && page == Page.Break } + override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() && page == Page.Break } + override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.Break } + override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() && page == Page.Break } + override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() && page == Page.Break } + override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() && page == Page.Break } override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.Break } - override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { vis() && page == Page.Break } + override val breakConfirmation by c.setting("Break Confirmation", BuildConfig.BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() && page == Page.Break } override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.Break } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.Break } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.Break } @@ -49,5 +56,5 @@ class BuildSettings( override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.Place } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.Place } - override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakConfirmation) } + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakConfirmation != BuildConfig.BreakConfirmationMode.None) } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt index 55aef899a..6694eb039 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt @@ -30,6 +30,8 @@ interface InventoryConfig { val providerPriority: Priority val storePriority: Priority + val silentSwap: Boolean + enum class Priority { WithMinItems, WithMaxItems; diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index ee9727557..55fec78c7 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -29,4 +29,5 @@ class InventorySettings( override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones", vis) override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from", vis) override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to", vis) + override val silentSwap by c.setting("Silent Swap", true, "", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 193dba34b..34a672e20 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -22,15 +22,20 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState -import net.minecraft.util.Hand +import net.minecraft.client.network.ClientPlayNetworkHandler +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d +import net.minecraft.world.BlockView import java.awt.Color data class BreakContext( @@ -39,21 +44,13 @@ data class BreakContext( override val rotation: RotationRequest, override val checkedState: BlockState, override val targetState: TargetState, - override var hand: Hand, + override var slotIndex: Int?, val instantBreak: Boolean, + val buildConfig: BuildConfig, ) : BuildContext { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) - override fun interact(swingHand: Boolean) { - runSafe { - if (interaction.updateBlockBreakingProgress(result.blockPos, result.side)) { - if (player.isCreative) interaction.blockBreakingCooldown = 0 - if (swingHand) player.swingHand(hand) - } - } - } - override val expectedPos: BlockPos get() = result.blockPos @@ -84,4 +81,33 @@ data class BreakContext( withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) withState(checkedState, expectedPos, sideColor, result.side) } + + fun getBlockBreakingProgress(breakingTicks: Int, player: PlayerEntity, world: BlockView): Int { + val currentItemStack = HotbarManager.mainHandStack ?: return -1 + val breakDelta = checkedState.calcItemBlockBreakingDelta(player, world, expectedPos, currentItemStack) + val progress = breakDelta * breakingTicks + return if (progress > 0.0f) + ((progress / buildConfig.breakThreshold) * 10.0f).toInt() + else + -1 + } + + fun startBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = + breakPacket(Action.START_DESTROY_BLOCK, sequence, connection) + + fun stopBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = + breakPacket(Action.STOP_DESTROY_BLOCK, sequence, connection) + + fun abortBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = + breakPacket(Action.ABORT_DESTROY_BLOCK, sequence, connection) + + private fun breakPacket(action: Action, sequence: Int, connection: ClientPlayNetworkHandler) = + connection.sendPacket( + PlayerActionC2SPacket( + action, + expectedPos, + result.side, + sequence + ) + ) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 7ff9d5209..bfddd0865 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -22,7 +22,6 @@ import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.block.BlockState -import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d @@ -35,9 +34,8 @@ interface BuildContext : Comparable, Drawable { val targetState: TargetState val expectedPos: BlockPos val checkedState: BlockState - val hand: Hand + val slotIndex: Int? val rotation: RotationRequest - fun interact(swingHand: Boolean) fun shouldRotate(config: BuildConfig): Boolean } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index fb6ede627..de903aaa1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -23,12 +23,9 @@ import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.threading.runSafe import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState -import com.lambda.util.Communication.warn import net.minecraft.block.BlockState -import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -42,44 +39,22 @@ data class PlaceContext( override val distance: Double, override val expectedState: BlockState, override val checkedState: BlockState, - override val hand: Hand, + override val slotIndex: Int?, override val expectedPos: BlockPos, override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, - val primeDirection: Direction?, + val primeDirection: Direction? ) : BuildContext { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) - override fun interact(swingHand: Boolean) { - runSafe { - val actionResult = interaction.interactBlock( - player, hand, result - ) - - if (actionResult.isAccepted) { - if (actionResult.shouldSwingHand() && swingHand) { - player.swingHand(hand) - } - - if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { - mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) - } - } else { - warn("Internal interaction failed with $actionResult") - } - } - } - override fun compareTo(other: BuildContext) = when (other) { is PlaceContext -> compareBy { BlockUtils.fluids.indexOf(it.checkedState.fluidState.fluid) }.thenByDescending { it.checkedState.fluidState.level - }.thenBy { - it.hand }.thenBy { it.sneak }.thenBy { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 183f27117..1548a8408 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -22,9 +22,7 @@ import baritone.api.pathing.goals.GoalInverted import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack -import com.lambda.interaction.material.container.ContainerManager.findBestAvailableTool import com.lambda.interaction.material.container.ContainerManager.transfer import com.lambda.interaction.material.container.MaterialContainer import com.lambda.interaction.material.container.containers.MainHandContainer @@ -99,12 +97,9 @@ sealed class BreakResult : BuildResult() { override val pausesParent get() = true override fun resolve() = - findBestAvailableTool(blockState, inventory = inventory) - ?.select() - ?.transfer(MainHandContainer, inventory) - ?: selectStack { - isItem(badItem).not() - }.transfer(MainHandContainer, inventory) + selectStack { + isItem(badItem).not() + }.transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find a tool for ${blockState.block.name.string} with $badItem in main hand.") override fun SafeContext.buildRenderer() { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 99318fb4e..810db9bb2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -195,6 +195,7 @@ abstract class BuildResult : ComparableResult, Nameable { data class WrongItem( override val blockPos: BlockPos, val context: BuildContext, + //TODO: probably need to make this a list of items val neededItem: Item, val currentItem: ItemStack, val inventory: InventoryConfig diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 65e98bc9d..5b761f12c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -29,7 +29,6 @@ import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.material.container.ContainerManager.findBestAvailableTool import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig @@ -43,10 +42,12 @@ import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.item.ItemUtils.findBestAvailableTool import com.lambda.util.math.distSq import com.lambda.util.player.copyPlayer import com.lambda.util.world.raycast.RayCastUtils.blockResult @@ -291,7 +292,8 @@ object BuildSimulator { eye.distanceTo(blockHit.pos), resultState, blockState(blockHit.blockPos), - Hand.MAIN_HAND, + //TODO: idk if this is the right input here + player.inventory.selectedSlot, context.blockPos, target, shouldSneak, @@ -405,7 +407,13 @@ object BuildSimulator { lookAtBlock(pos, config = interact), rotation ) val breakContext = BreakContext( - eye, blockHit, rotationRequest, state, targetState, player.activeHand, instantBreakable(state, pos) + eye, + blockHit, + rotationRequest, + state, + targetState, + player.inventory.selectedSlot, instantBreakable(state, pos), + build ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -447,6 +455,7 @@ object BuildSimulator { interact.pointSelection.select(validHits)?.let { checkedHit -> val blockHit = checkedHit.hit.blockResult ?: return@let + val bestTools = findBestAvailableTool(state) val breakContext = BreakContext( eye, @@ -454,22 +463,27 @@ object BuildSimulator { RotationRequest(lookAt(checkedHit.targetRotation, 0.001), rotation), state, targetState, - player.activeHand, - instantBreakable(state, pos) + player.inventory.selectedSlot, + instantBreakable(state, pos), + build ) /* player has a better tool for the job available */ - if (!player.isCreative) findBestAvailableTool(state)?.let { bestTool -> - Hand.entries.firstOrNull { - val stack = player.getStackInHand(it) - stack.item == bestTool - }?.let { hand -> - breakContext.hand = hand + if (!player.isCreative) findBestAvailableTool(state).let { bestTools -> + Hand.entries.map { + player.getStackInHand(it) + }.filter { stack -> + bestTools.any { tool -> tool == stack.item } + }.sortedByDescending { + state.calcItemBlockBreakingDelta(player, world, pos, it) + }.let { stackList -> + if (stackList.isEmpty()) { + acc.add(BuildResult.WrongItem(pos, breakContext, bestTools.first(), player.activeItem, inventory)) + return acc + } + breakContext.slotIndex = player.inventory.getSlotWithStack(stackList.first()) acc.add(BreakResult.Break(pos, breakContext)) return acc - } ?: run { - acc.add(BuildResult.WrongItem(pos, breakContext, bestTool, player.activeItem, inventory)) - return acc } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 31642add3..c4181f67f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -31,13 +31,10 @@ import com.lambda.util.BlockUtils.blockEntity import com.lambda.util.BlockUtils.item import com.lambda.util.Communication.info import com.lambda.util.extension.containerStacks -import com.lambda.util.item.ItemUtils import com.lambda.util.reflections.getInstances -import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.ChestBlockEntity import net.minecraft.block.entity.EnderChestBlockEntity -import net.minecraft.item.Item import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.ScreenHandlerType @@ -93,17 +90,16 @@ object ContainerManager : Loadable { fun container() = container.flatMap { setOf(it) + it.shulkerContainer }.sorted() fun StackSelection.transfer(destination: MaterialContainer, inventory: InventoryConfig = TaskFlowModule.inventory) = - findContainerWithMaterial(this, inventory)?.transfer(this, destination) + this.findContainerWithMaterial(inventory)?.transfer(this, destination) fun findContainer( block: (MaterialContainer) -> Boolean, ): MaterialContainer? = container().find(block) - fun findContainerWithMaterial( - selection: StackSelection, + fun StackSelection.findContainerWithMaterial( inventory: InventoryConfig ): MaterialContainer? = - containerWithMaterial(selection, inventory).firstOrNull() + containerWithMaterial(this, inventory).firstOrNull() fun findContainerWithSpace( selection: StackSelection, @@ -126,20 +122,6 @@ object ContainerManager : Loadable { .sortedWith(inventory.providerPriority.spaceComparator(selection)) .filter { it.spaceAvailable(selection) >= selection.count } - fun findBestAvailableTool( - blockState: BlockState, - availableTools: Set = ItemUtils.tools, - inventory: InventoryConfig = TaskFlowModule.inventory, - ) = availableTools.map { - it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) - }.filter { (item, speed) -> - speed > 1.0 - && item.isSuitableFor(blockState) - && containerWithMaterial(item.select(), inventory).isNotEmpty() - }.maxByOrNull { - it.second - }?.first - fun findDisposable(inventory: InventoryConfig = TaskFlowModule.inventory) = container().find { container -> inventory.disposables.any { container.materialAvailable(it.item.select()) >= 0 } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt index da830cb09..ca0569ba3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt @@ -43,7 +43,7 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { override val name: String get() = "Depositing [$selection] to ${hand.name.lowercase().replace("_", " ")}" override fun SafeContext.onStart() { - val moveStack = InventoryContainer.matchingStacks(selection).firstOrNull() ?: run { + val moveStack = matchingStacks(selection).firstOrNull() ?: run { failure("No matching stacks found in inventory") return } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index ed8419418..cf205c7bd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -23,11 +23,15 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.RequestHandler import com.lambda.threading.runSafe +import com.lambda.util.player.SlotUtils.hotbar object HotbarManager : RequestHandler(), Loadable { val serverSlot get() = runSafe { interaction.lastSelectedSlot } ?: -1 + val mainHandStack get() = runSafe { + player.hotbar.getOrNull(serverSlot - 1) + } override fun load() = "Loaded Hotbar Manager" diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index dc297aeee..9d8b620dd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -22,7 +22,7 @@ import com.lambda.interaction.request.Request class HotbarRequest( val slot: Int, - priority: Priority, + priority: Priority = 0, var keepTicks: Int = 3, var switchPause: Int = 0, ) : Request(priority) { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt index a815c7149..ce43bda29 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt @@ -31,7 +31,7 @@ object TaskFlowModule : Module( defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.AUTOMATION) ) { enum class Page { - Build, Rotation, Interaction, Inventory, Debug + Build, Rotation, Interaction, Inventory, Hotbar, Debug } private val page by setting("Page", Page.Build) @@ -39,6 +39,7 @@ object TaskFlowModule : Module( val rotation = RotationSettings(this) { page == Page.Rotation } val interact = InteractionSettings(this, InteractionMask.Both) { page == Page.Interaction } val inventory = InventorySettings(this) { page == Page.Inventory } + val hotbar = HotbarSettings(this) { page == Page.Hotbar } val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree") { page == Page.Debug } val shrinkFactor by setting("Shrink Factor", 0.001, 0.0..1.0, 0.001) { page == Page.Debug } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index 24852f94e..27963be28 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -33,7 +33,7 @@ class AcquireMaterial @Ta5kBuilder constructor( get() = "Acquiring $selection" override fun SafeContext.onStart() { - findContainerWithMaterial(selection, inventory) + selection.findContainerWithMaterial(inventory) ?.withdraw(selection) ?.finally { success(selection) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index d8732ad01..64a959647 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -44,11 +44,13 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer +import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils -import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta +import com.lambda.util.BlockUtils.fluidState import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.Formatting.string @@ -57,7 +59,16 @@ import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.SlotUtils.hotbarAndStorage +import net.minecraft.block.BlockState +import net.minecraft.block.OperatorBlock +import net.minecraft.client.sound.PositionedSoundInstance +import net.minecraft.client.sound.SoundInstance import net.minecraft.entity.ItemEntity +import net.minecraft.item.ItemStack +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action +import net.minecraft.sound.SoundCategory +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos class BuildTask @Ta5kBuilder constructor( @@ -77,6 +88,10 @@ class BuildTask @Ta5kBuilder constructor( private var currentInteraction: BuildContext? = null private val instantBreaks = mutableSetOf() + var breaking = false + var breakingTicks= 0 + var soundsCooldown = 0.0f + private var placements = 0 private var breaks = 0 private val dropsToCollect = mutableSetOf() @@ -88,14 +103,23 @@ class BuildTask @Ta5kBuilder constructor( init { listen { + val currentItemStack = HotbarManager.mainHandStack ?: return@listen + currentInteraction?.let { context -> // TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let - if (context is PlaceContext && context.sneak && !player.isSneaking) return@let - context.interact(interact.swingHand) + when (context) { + is PlaceContext -> { + if (context.sneak && !player.isSneaking) return@let + placeBlock(Hand.MAIN_HAND, context) + } + is BreakContext -> { + updateBlockBreakingProgress(context, currentItemStack) + } + } } instantBreaks.forEach { context -> - context.interact(interact.swingHand) + updateBlockBreakingProgress(context, currentItemStack) pendingInteractions.add(context) } instantBreaks.clear() @@ -215,22 +239,27 @@ class BuildTask @Ta5kBuilder constructor( if (context.sneak) it.input.sneaking = true } - listen { event -> - val context = currentInteraction ?: return@listen - if (context.expectedPos != event.pos) return@listen - currentInteraction = null - pendingInteractions.add(context) - } +// listen { event -> +// val context = currentInteraction ?: return@listen +// if (context.expectedPos != event.pos) return@listen +// currentInteraction = null +// pendingInteractions.add(context) +// } listen(alwaysListen = true) { event -> - pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { context -> - pendingInteractions.remove(context) - if (!context.targetState.matches(event.newState, event.pos, world)) { - this@BuildTask.warn("Update at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${context.targetState}") + pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { ctx -> + pendingInteractions.remove(ctx) + if (!ctx.targetState.matches(event.newState, event.pos, world)) { + this@BuildTask.warn("Update at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${ctx.targetState}") return@let } - when (context) { - is BreakContext -> breaks++ + when (ctx) { + is BreakContext -> { + if (ctx.buildConfig.breakConfirmation == BuildConfig.BreakConfirmationMode.AwaitThenBreak) { + breakBlock(ctx) + } + breaks++ + } is PlaceContext -> placements++ } } @@ -250,6 +279,189 @@ class BuildTask @Ta5kBuilder constructor( } } + fun SafeContext.placeBlock(hand: Hand, ctx: PlaceContext) { + with(ctx) { + val actionResult = interaction.interactBlock( + player, hand, result + ) + + if (actionResult.isAccepted) { + if (actionResult.shouldSwingHand() && interact.swingHand) { + player.swingHand(hand) + } + + if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + } + } else { + warn("Internal interaction failed with $actionResult") + } + } + } + + private fun SafeContext.updateBlockBreakingProgress(ctx: BreakContext, item: ItemStack): Boolean { + if (interaction.blockBreakingCooldown > 0) { + interaction.blockBreakingCooldown-- + return true + } + + val hitResult = ctx.result + + if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { + interaction.blockBreakingCooldown = ctx.buildConfig.breakDelay + interaction.sendSequencedPacket(world) { sequence: Int -> + onBlockBreak(ctx) + PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + } + return true + } + + if (!breaking) return attackBlock(ctx) + + val blockState = blockState(ctx.expectedPos) + if (blockState.isAir) return false + + breakingTicks++ + val progress = blockState.calcItemBlockBreakingDelta( + player, + world, + ctx.expectedPos, + item + ) + + if (ctx.buildConfig.sounds) { + if (soundsCooldown % 4.0f == 0.0f) { + val blockSoundGroup = blockState.soundGroup + mc + .soundManager + .play( + PositionedSoundInstance( + blockSoundGroup.hitSound, + SoundCategory.BLOCKS, + (blockSoundGroup.getVolume() + 1.0f) / 8.0f, + blockSoundGroup.getPitch() * 0.5f, + SoundInstance.createRandom(), + ctx.expectedPos + ) + ) + } + soundsCooldown++ + } + + if (ctx.buildConfig.particles) { + mc.particleManager.addBlockBreakingParticles( + ctx.expectedPos, + hitResult.side + ) + } + + if (progress >= ctx.buildConfig.breakThreshold) { + interaction.sendSequencedPacket(world) { sequence: Int -> + onBlockBreak(ctx) + PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + } + } + + if (ctx.buildConfig.breakingTexture) { + setBreakingTextureStage(ctx) + } + + return true + } + + private fun SafeContext.onBlockBreak(ctx: BreakContext) { + when (ctx.buildConfig.breakConfirmation) { + BuildConfig.BreakConfirmationMode.None -> { + breakBlock(ctx) + breaks++ + } + BuildConfig.BreakConfirmationMode.BreakThenAwait -> { + breakBlock(ctx) + pendingInteractions.add(ctx) + } + BuildConfig.BreakConfirmationMode.AwaitThenBreak -> pendingInteractions.add(ctx) + } + } + + private fun SafeContext.breakBlock(ctx: BreakContext): Boolean { + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false + + if (HotbarManager.mainHandStack?.item?.canMine(ctx.checkedState, world, ctx.expectedPos, player) == false) + return false + val block = ctx.checkedState.block; + if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false + if (ctx.checkedState.isAir) return false + + block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) + val fluidState = fluidState(ctx.expectedPos) + val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) + if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) + + if (ctx.buildConfig.breakingTexture) setBreakingTextureStage(ctx, -1) + + return setState + } + + private fun SafeContext.setBreakingTextureStage( + ctx: BreakContext, + stage: Int = ctx.getBlockBreakingProgress( + breakingTicks, + player, world + ) + ) { + world.setBlockBreakingInfo( + player.id, + ctx.expectedPos, + stage + ) + } + + private fun SafeContext.attackBlock(ctx: BreakContext): Boolean { + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false + if (!world.worldBorder.contains(ctx.expectedPos)) return false + + if (interaction.currentGameMode.isCreative) { + interaction.sendSequencedPacket(world) { sequence: Int -> + onBlockBreak(ctx) + PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) + } + interaction.blockBreakingCooldown = 5 + return true + } + if (breaking) return false + + val blockState: BlockState = world.getBlockState(ctx.expectedPos) + var pendingUpdateManager = world.pendingUpdateManager.incrementSequence() + val sequence = pendingUpdateManager.sequence + val notAir = !blockState.isAir + if (notAir && breakingTicks == 0) { + blockState.onBlockBreakStart(world, ctx.expectedPos, player) + } + + val currentItemStack = HotbarManager.mainHandStack ?: return false + + if (notAir && blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, currentItemStack) >= build.breakThreshold) { + onBlockBreak(ctx) + return true + } else { + breaking = true + soundsCooldown = 0.0f + if (ctx.buildConfig.breakingTexture) { + setBreakingTextureStage(ctx) + } + } + + ctx.abortBreakPacket(sequence, connection) + ctx.stopBreakPacket(sequence + 1, connection) + ctx.startBreakPacket(sequence + 2, connection) + ctx.stopBreakPacket(sequence + 3, connection) + (0..3).forEach { i -> + pendingUpdateManager.incrementSequence() + } + + return true + } + companion object { @Ta5kBuilder fun build( diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index d775a5fa7..fc27e4319 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -23,6 +23,8 @@ import net.minecraft.block.AbstractCauldronBlock import net.minecraft.block.AbstractFurnaceBlock import net.minecraft.block.AbstractSignBlock import net.minecraft.block.AnvilBlock +import net.minecraft.block.BambooBlock +import net.minecraft.block.BambooShootBlock import net.minecraft.block.BarrelBlock import net.minecraft.block.BeaconBlock import net.minecraft.block.BedBlock @@ -80,10 +82,19 @@ import net.minecraft.block.StructureBlock import net.minecraft.block.SweetBerryBushBlock import net.minecraft.block.TntBlock import net.minecraft.block.TrapdoorBlock +import net.minecraft.enchantment.EnchantmentHelper +import net.minecraft.enchantment.Enchantments +import net.minecraft.entity.effect.StatusEffectUtil +import net.minecraft.entity.effect.StatusEffects +import net.minecraft.entity.player.PlayerEntity import net.minecraft.fluid.FluidState import net.minecraft.fluid.Fluids import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.item.SwordItem +import net.minecraft.registry.tag.FluidTags import net.minecraft.util.math.* +import net.minecraft.world.BlockView object BlockUtils { @@ -223,6 +234,66 @@ object BlockUtils { return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative } + fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, item: ItemStack): Boolean { + val ticksNeeded = 1 / blockState.calcItemBlockBreakingDelta(player, world, blockPos, item) + return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative + } + + fun BlockState.calcItemBlockBreakingDelta( + player: PlayerEntity, + world: BlockView, + blockPos: BlockPos, + item: ItemStack + ): Float = + if ((block is BambooShootBlock || block is BambooBlock) && item.item is SwordItem) { + 1.0f + } else { + val hardness = getHardness(world, blockPos) + if (hardness == -1.0f) 0.0f else { + val harvestMultiplier = if (item.canHarvest(this)) 30 else 100 + player.getItemBlockBreakingSpeed(this, item) / hardness / harvestMultiplier + } + } + + fun ItemStack.canHarvest(blockState: BlockState) = + !blockState.isToolRequired || isSuitableFor(blockState) + + fun PlayerEntity.getItemBlockBreakingSpeed(blockState: BlockState, item: ItemStack): Float { + var speedMultiplier = item.getMiningSpeedMultiplier(blockState) + if (speedMultiplier > 1.0f) { + val level = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, item) + if (level > 0 && !item.isEmpty) { + speedMultiplier += (level * level + 1) + } + } + + if (StatusEffectUtil.hasHaste(this)) { + speedMultiplier *= 1.0f + (StatusEffectUtil.getHasteAmplifier(this) + 1) * 0.2f + } + + getStatusEffect(StatusEffects.MINING_FATIGUE)?.amplifier?.let { fatigue -> + val fatigueMultiplier = when (fatigue) { + 0 -> 0.3f + 1 -> 0.09f + 2 -> 0.0027f + 3 -> 8.1E-4f + else -> 8.1E-4f + } + + speedMultiplier *= fatigueMultiplier + } + + if (isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(this)) { + speedMultiplier /= 5.0f + } + + if (!isOnGround) { + speedMultiplier /= 5.0f + } + + return speedMultiplier + } + val Vec3i.blockPos: BlockPos get() = BlockPos(this) val Block.item: Item get() = asItem() val Vec3d.flooredPos: BlockPos get() = BlockPos(x.floorToInt(), y.floorToInt(), z.floorToInt()) diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index 653e3e03a..ca0cbfdad 100644 --- a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -18,6 +18,7 @@ package com.lambda.util.item import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.item.Item import net.minecraft.item.Items @@ -120,6 +121,19 @@ object ItemUtils { val Item.block: Block get() = Block.getBlockFromItem(this) + fun findBestAvailableTool( + blockState: BlockState, + availableTools: Set = ItemUtils.tools, + ) = availableTools.map { + it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) + }.filter { (item, speed) -> + speed > 1.0 && item.isSuitableFor(blockState) + }.sortedByDescending { + it.second + }.map { + it.first + } + fun Int.toItemCount(): String { if (this < 0) { return "Invalid input" diff --git a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt index d9017c807..54cd6d089 100644 --- a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -19,7 +19,9 @@ package com.lambda.util.player import com.lambda.context.SafeContext import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType object SlotUtils { @@ -28,6 +30,7 @@ object SlotUtils { val ClientPlayerEntity.hotbarAndStorage: List get() = inventory.main.subList(0, 36) val ClientPlayerEntity.combined: List get() = inventory.main + inventory.armor + inventory.offHand val ClientPlayerEntity.offhand: ItemStack get() = offHandStack + val Slot.hotbarIndex: Int? get() = if (inventory is PlayerInventory) index + 1 else null fun SafeContext.clickSlot( slotId: Int, diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index bb35ed9b5..0ed5dd377 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -66,6 +66,8 @@ accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket encryp accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket nonce [B accessible method net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket (IZLnet/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler;)V accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket ATTACK Lnet/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler; +accessible method net/minecraft/client/network/ClientPlayerInteractionManager sendSequencedPacket (Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)V +accessible field net/minecraft/client/world/ClientWorld pendingUpdateManager Lnet/minecraft/client/network/PendingUpdateManager; # Other accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; From ed69d46bf366a67df7aacc854810022002a75cf7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Feb 2025 01:46:09 +0000 Subject: [PATCH 002/364] change start packet for packet modes --- .../com/lambda/config/groups/BuildConfig.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 3c66c5c0a..f11b352dd 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -49,7 +49,7 @@ interface BuildConfig { val placementsPerTick: Int enum class BreakMode { - Vanilla, Packets + Vanilla, Packet } enum class BreakConfirmationMode { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 64a959647..2e5b22135 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -451,12 +451,16 @@ class BuildTask @Ta5kBuilder constructor( } } - ctx.abortBreakPacket(sequence, connection) - ctx.stopBreakPacket(sequence + 1, connection) - ctx.startBreakPacket(sequence + 2, connection) - ctx.stopBreakPacket(sequence + 3, connection) - (0..3).forEach { i -> - pendingUpdateManager.incrementSequence() + if (ctx.buildConfig.breakMode == BuildConfig.BreakMode.Packet) { + ctx.abortBreakPacket(sequence, connection) + ctx.stopBreakPacket(sequence + 1, connection) + ctx.startBreakPacket(sequence + 2, connection) + ctx.stopBreakPacket(sequence + 3, connection) + (0..3).forEach { i -> + pendingUpdateManager.incrementSequence() + } + } else { + ctx.startBreakPacket(sequence, connection) } return true From 13475306957a73fb15391020b5787fe38ba6ec5a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Feb 2025 02:01:20 +0000 Subject: [PATCH 003/364] nullify the currentInteraction on block break (i forgor to include this) --- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2e5b22135..e4250eb77 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -381,6 +381,7 @@ class BuildTask @Ta5kBuilder constructor( } BuildConfig.BreakConfirmationMode.AwaitThenBreak -> pendingInteractions.add(ctx) } + currentInteraction = null } private fun SafeContext.breakBlock(ctx: BreakContext): Boolean { From 5776f0d1ecefaf347d4ce2bd3d904a0fc37958e1 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Feb 2025 02:03:35 +0000 Subject: [PATCH 004/364] reset the breaking values --- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index e4250eb77..4fa35256b 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -89,7 +89,7 @@ class BuildTask @Ta5kBuilder constructor( private val instantBreaks = mutableSetOf() var breaking = false - var breakingTicks= 0 + var breakingTicks = 0 var soundsCooldown = 0.0f private var placements = 0 @@ -382,6 +382,8 @@ class BuildTask @Ta5kBuilder constructor( BuildConfig.BreakConfirmationMode.AwaitThenBreak -> pendingInteractions.add(ctx) } currentInteraction = null + breaking = false + breakingTicks = 0 } private fun SafeContext.breakBlock(ctx: BreakContext): Boolean { @@ -389,7 +391,7 @@ class BuildTask @Ta5kBuilder constructor( if (HotbarManager.mainHandStack?.item?.canMine(ctx.checkedState, world, ctx.expectedPos, player) == false) return false - val block = ctx.checkedState.block; + val block = ctx.checkedState.block if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false if (ctx.checkedState.isAir) return false From bb201a8a954322504d955c88c833a5147ec15a1a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Feb 2025 02:26:56 +0000 Subject: [PATCH 005/364] break mode visibility --- .../src/main/kotlin/com/lambda/config/groups/BuildSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index bed2adab3..a21afef07 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -37,7 +37,7 @@ class BuildSettings( override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking - override val breakMode by c.setting("Break Mode", BuildConfig.BreakMode.Vanilla) + override val breakMode by c.setting("Break Mode", BuildConfig.BreakMode.Vanilla) { vis() && page == Page.Break } override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.1f, "The break amount at which the block is considered broken") { vis() && page == Page.Break } override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() && page == Page.Break } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.Break } From d9c6a8b7292bac241f14b3ed055230035f46f520 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Feb 2025 02:38:28 +0000 Subject: [PATCH 006/364] forgor to put the branch in feature --- .../interaction/construction/simulation/BuildSimulator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 5b761f12c..baf6565de 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -412,7 +412,8 @@ object BuildSimulator { rotationRequest, state, targetState, - player.inventory.selectedSlot, instantBreakable(state, pos), + player.inventory.selectedSlot, + instantBreakable(state, pos), build ) acc.add(BreakResult.Break(pos, breakContext)) @@ -455,7 +456,6 @@ object BuildSimulator { interact.pointSelection.select(validHits)?.let { checkedHit -> val blockHit = checkedHit.hit.blockResult ?: return@let - val bestTools = findBestAvailableTool(state) val breakContext = BreakContext( eye, From 982d6922432ec01e63d39bb424e020d8bd9d8f10 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 19 Feb 2025 03:48:07 +0100 Subject: [PATCH 007/364] Add hotbar spoof test and fix dsync issues --- .../mixin/entity/PlayerInventoryMixin.java | 11 ++++ .../construction/context/BreakContext.kt | 2 +- .../request/hotbar/HotbarManager.kt | 5 +- .../lambda/module/modules/debug/SilentSwap.kt | 51 +++++++++++++++++++ .../kotlin/com/lambda/task/tasks/BuildTask.kt | 9 ++-- 5 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt diff --git a/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java b/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java index 6c42a71db..04ff67490 100644 --- a/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java @@ -17,11 +17,15 @@ package com.lambda.mixin.entity; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -30,6 +34,8 @@ @Mixin(PlayerInventory.class) public class PlayerInventoryMixin { + @Shadow @Final public PlayerEntity player; + @Inject(method = "getMainHandStack", at = @At(value = "HEAD"), cancellable = true) public void handleSpoofedMainHandStack(CallbackInfoReturnable cir) { PlayerInventory instance = (PlayerInventory) (Object) this; @@ -44,4 +50,9 @@ public void handleSpoofedMainHandStack(CallbackInfoReturnable cir) { isValidHotbarIndex(actualSlot) ? instance.main.get(actualSlot) : ItemStack.EMPTY ); } + + @Inject(method = "getBlockBreakingSpeed", at = @At(value = "HEAD"), cancellable = true) + public void handleSpoofedBlockBreakingSpeed(BlockState block, CallbackInfoReturnable cir) { + cir.setReturnValue(player.getMainHandStack().getMiningSpeedMultiplier(block)); + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 34a672e20..3b2dcbe46 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -83,7 +83,7 @@ data class BreakContext( } fun getBlockBreakingProgress(breakingTicks: Int, player: PlayerEntity, world: BlockView): Int { - val currentItemStack = HotbarManager.mainHandStack ?: return -1 + val currentItemStack = player.mainHandStack ?: return -1 val breakDelta = checkedState.calcItemBlockBreakingDelta(player, world, expectedPos, currentItemStack) val progress = breakDelta * breakingTicks return if (progress > 0.0f) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index cf205c7bd..417bfbd32 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -28,10 +28,7 @@ import com.lambda.util.player.SlotUtils.hotbar object HotbarManager : RequestHandler(), Loadable { val serverSlot get() = runSafe { interaction.lastSelectedSlot - } ?: -1 - val mainHandStack get() = runSafe { - player.hotbar.getOrNull(serverSlot - 1) - } + } ?: 0 override fun load() = "Loaded Hotbar Manager" diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt new file mode 100644 index 000000000..c41d5c37e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2024 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.module.modules.debug + +import com.lambda.config.groups.HotbarSettings +import com.lambda.event.events.PlayerEvent +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.builders.ofBox +import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.Communication.info +import com.lambda.util.world.blockSearch +import net.minecraft.block.Blocks +import net.minecraft.util.math.Vec3i +import java.awt.Color + +object SilentSwap : Module( + name = "SilentSwap", + description = "SilentSwap", + defaultTags = setOf(ModuleTag.DEBUG), +) { + private val hotbar = HotbarSettings(this) + + init { + listen { + if (!hotbar.request(HotbarRequest(0)).done) { + it.cancel() + return@listen + } + info("${interaction.lastSelectedSlot} ${player.mainHandStack}") + } + } +} diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 4fa35256b..2d73c990a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -103,7 +103,7 @@ class BuildTask @Ta5kBuilder constructor( init { listen { - val currentItemStack = HotbarManager.mainHandStack ?: return@listen + val currentItemStack = player.mainHandStack ?: return@listen currentInteraction?.let { context -> // TaskFlowModule.drawables = listOf(context) @@ -389,7 +389,7 @@ class BuildTask @Ta5kBuilder constructor( private fun SafeContext.breakBlock(ctx: BreakContext): Boolean { if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false - if (HotbarManager.mainHandStack?.item?.canMine(ctx.checkedState, world, ctx.expectedPos, player) == false) + if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) return false val block = ctx.checkedState.block if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false @@ -441,9 +441,8 @@ class BuildTask @Ta5kBuilder constructor( blockState.onBlockBreakStart(world, ctx.expectedPos, player) } - val currentItemStack = HotbarManager.mainHandStack ?: return false - - if (notAir && blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, currentItemStack) >= build.breakThreshold) { + val breakingDelta = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, player.mainHandStack) + if (notAir && breakingDelta >= build.breakThreshold) { onBlockBreak(ctx) return true } else { From 21a4137c226bb6caab3d0c0c69d7ea24b077648e Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 19 Feb 2025 04:07:19 +0100 Subject: [PATCH 008/364] Show fake stack in hand and in hud tooltip --- .../java/com/lambda/mixin/render/InGameHudMixin.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java b/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java index 8d058a9a1..d245090e9 100644 --- a/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java @@ -20,15 +20,25 @@ import com.lambda.graphics.RenderMain; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import static net.minecraft.entity.player.PlayerInventory.isValidHotbarIndex; + @Mixin(InGameHud.class) public class InGameHudMixin { @Inject(method = "render", at = @At("TAIL")) private void onRender(DrawContext context, float tickDelta, CallbackInfo ci) { RenderMain.render2D(); } + + @Redirect(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerInventory;getMainHandStack()Lnet/minecraft/item/ItemStack;")) + private ItemStack onTick(PlayerInventory inventory) { + return isValidHotbarIndex(inventory.selectedSlot) ? inventory.main.get(inventory.selectedSlot) : ItemStack.EMPTY; + } } From 0a2d160b5b7b5025939d4cbff013437d61d432bf Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 19 Feb 2025 04:11:12 +0100 Subject: [PATCH 009/364] Link mixins to manager so people can find them --- .../interaction/request/hotbar/HotbarManager.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 417bfbd32..87192602b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -23,8 +23,15 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.RequestHandler import com.lambda.threading.runSafe -import com.lambda.util.player.SlotUtils.hotbar - +import com.lambda.mixin.entity.PlayerInventoryMixin +import com.lambda.mixin.render.InGameHudMixin + +/** + * See mixins: + * @see PlayerInventoryMixin.handleSpoofedMainHandStack + * @see PlayerInventoryMixin.handleSpoofedBlockBreakingSpeed + * @see InGameHudMixin.onTick + */ object HotbarManager : RequestHandler(), Loadable { val serverSlot get() = runSafe { interaction.lastSelectedSlot From 8993fd7e166a20b5851551e7161a8b119ba88ce5 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 20 Feb 2025 01:51:17 +0100 Subject: [PATCH 010/364] WIP container selection --- .../command/commands/TransferCommand.kt | 2 +- .../construction/simulation/BuildSimulator.kt | 109 +++++++++++------- .../material/ContainerSelection.kt | 88 ++++++++++++++ .../interaction/material/StackSelection.kt | 17 +++ .../material/container/ContainerManager.kt | 14 ++- .../material/container/MaterialContainer.kt | 5 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 100 +++++++--------- .../main/kotlin/com/lambda/util/BlockUtils.kt | 15 ++- .../kotlin/com/lambda/util/item/ItemUtils.kt | 25 ++-- .../com/lambda/util/player/SlotUtils.kt | 5 +- 10 files changed, 242 insertions(+), 138 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index 367c288c8..588f65901 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -48,7 +48,7 @@ object TransferCommand : LambdaCommand( val selection = selectStack(count) { isItem(stack(ctx).value().item) } - containerWithMaterial(selection).forEachIndexed { i, container -> + selection.containerWithMaterial().forEachIndexed { i, container -> builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) } builder.buildFuture() diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index baf6565de..6fe995e5d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -29,6 +29,13 @@ import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.StackSelection.Companion.selectStack +import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial +import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial +import com.lambda.interaction.material.container.MaterialContainer import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig @@ -47,12 +54,13 @@ import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal -import com.lambda.util.item.ItemUtils.findBestAvailableTool +import com.lambda.util.item.ItemUtils.findBestToolsForBreaking import com.lambda.util.math.distSq import com.lambda.util.player.copyPlayer import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition +import net.minecraft.enchantment.Enchantments import net.minecraft.item.BlockItem import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemUsageContext @@ -280,7 +288,7 @@ object BuildSimulator { val blockHit = checkedResult.blockResult ?: return@forEach val hitBlock = blockState(blockHit.blockPos).block - val shouldSneak = hitBlock::class in BlockUtils.interactionClasses + val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks val primeDirection = (target as? TargetState.State)?.blockState?.getOrEmpty(Properties.HORIZONTAL_FACING)?.getOrNull() @@ -373,15 +381,6 @@ object BuildSimulator { return acc } - /* The current selected item cant mine the block */ - Hand.entries.forEach { - val stack = player.getStackInHand(it) - if (stack.isEmpty) return@forEach - if (stack.item.canMine(state, world, pos, player)) return@forEach - acc.add(BreakResult.ItemCantMine(pos, state, stack.item, inventory)) - return acc - } - val currentRotation = RotationManager.currentRotation val currentCast = currentRotation.rayCast(interact.interactReach, eye) @@ -412,7 +411,7 @@ object BuildSimulator { rotationRequest, state, targetState, - player.inventory.selectedSlot, + player.inventory.selectedSlot + 1, instantBreakable(state, pos), build ) @@ -426,6 +425,7 @@ object BuildSimulator { val reachSq = interact.interactReach.pow(2) boxes.forEach { box -> + // ToDo: Rewrite Rotation request system to allow support for all sim features and use the rotation finder scanSurfaces(box, Direction.entries.toSet(), interact.resolution) { side, vec -> if (eye distSq vec > reachSq) { misses.add(vec) @@ -454,41 +454,62 @@ object BuildSimulator { return acc } - interact.pointSelection.select(validHits)?.let { checkedHit -> - val blockHit = checkedHit.hit.blockResult ?: return@let - - val breakContext = BreakContext( - eye, - blockHit, - RotationRequest(lookAt(checkedHit.targetRotation, 0.001), rotation), - state, - targetState, - player.inventory.selectedSlot, - instantBreakable(state, pos), - build - ) - - /* player has a better tool for the job available */ - if (!player.isCreative) findBestAvailableTool(state).let { bestTools -> - Hand.entries.map { - player.getStackInHand(it) - }.filter { stack -> - bestTools.any { tool -> tool == stack.item } - }.sortedByDescending { - state.calcItemBlockBreakingDelta(player, world, pos, it) - }.let { stackList -> - if (stackList.isEmpty()) { - acc.add(BuildResult.WrongItem(pos, breakContext, bestTools.first(), player.activeItem, inventory)) - return acc - } - breakContext.slotIndex = player.inventory.getSlotWithStack(stackList.first()) - acc.add(BreakResult.Break(pos, breakContext)) - return acc - } - } + val bestHit = interact.pointSelection.select(validHits) ?: return acc + val blockHit = bestHit.hit.blockResult ?: return acc + val target = lookAt(bestHit.targetRotation, 0.001) + val request = RotationRequest(target, rotation) + val instant = instantBreakable(state, pos) + val useSlotIndex = player.inventory.selectedSlot + 1 + + val breakContext = BreakContext( + eye, blockHit, request, state, targetState, useSlotIndex, instant, build + ) + if (player.isCreative) { acc.add(BreakResult.Break(pos, breakContext)) + return acc } + + val bestTools = findBestToolsForBreaking(state) + + /* there is no good tool for the job */ + if (bestTools.isEmpty()) { + /* The current selected item cant mine the block */ + Hand.entries.forEach { + val stack = player.getStackInHand(it) + if (stack.isEmpty) return@forEach + if (stack.item.canMine(state, world, pos, player)) return@forEach + acc.add(BreakResult.ItemCantMine(pos, state, stack.item, inventory)) + return acc + } + // ToDo: Switch to non destroyable item + acc.add(BreakResult.Break(pos, breakContext)) + return acc + } + + val bestTool = bestTools.firstOrNull() ?: return acc + val toolSelection = if (build.forceSilkTouch) { + selectStack { isItem(bestTool) and hasEnchantment(Enchantments.SILK_TOUCH) } + } else { + bestTool.select() + } + val containerSelection = selectContainer { + matches(toolSelection) and ofAnyType(MaterialContainer.Rank.OFF_HAND, MaterialContainer.Rank.HOTBAR) + } + val allContainersWithTools = toolSelection.containerWithMaterial(inventory, containerSelection) + val matchingStacks = allContainersWithTools.associateWith { it.matchingStacks(toolSelection) } + val bestDeltaTool = matchingStacks.mapValues { (_, stacks) -> + stacks.associateWith { state.calcItemBlockBreakingDelta(player, world, pos, it) } + .maxByOrNull { it.value } + ?.toPair() + }.entries.maxByOrNull { it.value?.second ?: 0f }?.toPair() ?: return acc + + if (bestDeltaTool.second == null) { + acc.add(BuildResult.WrongItem(pos, breakContext, bestTools.first(), player.activeItem, inventory)) + return acc + } + + acc.add(BreakResult.Break(pos, breakContext)) return acc } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt new file mode 100644 index 000000000..ff75273e4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.material + +import com.lambda.interaction.material.container.MaterialContainer + +/** + * ContainerSelection is a class that holds a predicate for matching MaterialContainers. + * It can be combined using "and", "or", etc. + */ +class ContainerSelection { + private var selector: (MaterialContainer) -> Boolean = { true } + + /** + * Tests whether the provided container matches this selection. + */ + @ContainerSelectionDsl + fun matches(container: MaterialContainer): Boolean = selector(container) + + /** + * Returns a function that matches containers having at least one stack + * which matches the given StackSelection. + */ + @ContainerSelectionDsl + fun matches(stackSelection: StackSelection): (MaterialContainer) -> Boolean = + { container -> container.matchingStacks(stackSelection).isNotEmpty() } + + /** + * Returns a function that matches containers whose rank is any of the types provided. + */ + @ContainerSelectionDsl + fun ofAnyType(vararg types: MaterialContainer.Rank): (MaterialContainer) -> Boolean = + { container -> types.contains(container.rank) } + + /** + * Returns a function that matches containers whose rank is not any of the types provided. + */ + @ContainerSelectionDsl + fun noneOfType(vararg types: MaterialContainer.Rank): (MaterialContainer) -> Boolean = + { container -> !types.contains(container.rank) } + + /** + * Returns a function that combines two container predicates using logical AND. + */ + @ContainerSelectionDsl + infix fun ((MaterialContainer) -> Boolean).and(other: (MaterialContainer) -> Boolean): (MaterialContainer) -> Boolean = + { container -> this(container) && other(container) } + + /** + * Returns a function that combines two container predicates using logical OR. + */ + @ContainerSelectionDsl + infix fun ((MaterialContainer) -> Boolean).or(other: (MaterialContainer) -> Boolean): (MaterialContainer) -> Boolean = + { container -> this(container) || other(container) } + + /** + * Returns a function that negates the current selection predicate. + */ + @ContainerSelectionDsl + fun ((MaterialContainer) -> Boolean).negate(): (MaterialContainer) -> Boolean = + { container -> !this(container) } + + companion object { + @DslMarker + annotation class ContainerSelectionDsl + + @ContainerSelectionDsl + fun selectContainer( + block: ContainerSelection.() -> (MaterialContainer) -> Boolean + ): ContainerSelection = + ContainerSelection().apply { selector = block() } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 9af923612..8b5bb27f2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.material +import com.lambda.interaction.material.ContainerSelection.Companion.ContainerSelectionDsl import com.lambda.util.BlockUtils.item import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import net.minecraft.block.Block @@ -103,6 +104,7 @@ class StackSelection { * @param item The [Item] to be matched. * @return A predicate that matches the [Item]. */ + @StackSelectionDsl fun isItem(item: Item): (ItemStack) -> Boolean { this.item = item return { it.item == item } @@ -113,6 +115,7 @@ class StackSelection { * @param T The instance of [Item] to be matched. * @return A predicate that matches the [Item]. */ + @StackSelectionDsl inline fun isItem(): (ItemStack) -> Boolean { itemClass = T::class return { it.item is T } @@ -123,6 +126,7 @@ class StackSelection { * @param block The [Block] to be matched. * @return A predicate that matches the [Block]. */ + @StackSelectionDsl fun isBlock(block: Block): (ItemStack) -> Boolean { item = block.item return { it.item == block.item } @@ -133,6 +137,7 @@ class StackSelection { * @param stack The [ItemStack] to be matched. * @return A predicate that matches the [ItemStack]. */ + @StackSelectionDsl fun isItemStack(stack: ItemStack): (ItemStack) -> Boolean { this.itemStack = stack return { ItemStack.areEqual(it, stack) } @@ -143,6 +148,7 @@ class StackSelection { * @param damage The damage value to be matched. * @return A predicate that matches the damage value. */ + @StackSelectionDsl fun hasDamage(damage: Int): (ItemStack) -> Boolean { this.damage = damage return { it.damage == damage } @@ -154,6 +160,7 @@ class StackSelection { * @param level The level to be matched (if -1 will look for any level above 0). * @return A predicate that matches the [Enchantment] and `level`. */ + @StackSelectionDsl fun hasEnchantment(enchantment: Enchantment, level: Int = -1): (ItemStack) -> Boolean = { if (level < 0) { EnchantmentHelper.getLevel(enchantment, it) > 0 @@ -166,6 +173,7 @@ class StackSelection { * Returns the negation of the original predicate. * @return A new predicate that matches if the original predicate does not match. */ + @StackSelectionDsl fun ((ItemStack) -> Boolean).not(): (ItemStack) -> Boolean { return { !this(it) } } @@ -175,6 +183,7 @@ class StackSelection { * @param otherPredicate The second predicate. * @return A new predicate that matches if both inputs predicate match. */ + @StackSelectionDsl infix fun ((ItemStack) -> Boolean).and(otherPredicate: (ItemStack) -> Boolean): (ItemStack) -> Boolean { return { this(it) && otherPredicate(it) } } @@ -184,6 +193,7 @@ class StackSelection { * @param otherPredicate The second predicate. * @return A new predicate that matches if either input predicate matches. */ + @StackSelectionDsl infix fun ((ItemStack) -> Boolean).or(otherPredicate: (ItemStack) -> Boolean): (ItemStack) -> Boolean { return { this(it) || otherPredicate(it) } } @@ -197,6 +207,9 @@ class StackSelection { } companion object { + @DslMarker + annotation class StackSelectionDsl + const val DEFAULT_AMOUNT = 1 val FULL_SHULKERS: (ItemStack) -> Boolean = { stack -> stack.shulkerBoxContents.none { it.isEmpty } @@ -206,8 +219,11 @@ class StackSelection { } val EVERYTHING: (ItemStack) -> Boolean = { true } + @ContainerSelectionDsl fun Item.select(): StackSelection = selectStack { isItem(this@select) } + @ContainerSelectionDsl fun ItemStack.select(): StackSelection = selectStack { isItemStack(this@select) } + @ContainerSelectionDsl fun ((ItemStack) -> Boolean).select() = selectStack { this@select } /** @@ -216,6 +232,7 @@ class StackSelection { * @param block The predicate to be used to select the items. * @return A [StackSelection] with the given parameters. */ + @StackSelectionDsl fun selectStack( count: Int = DEFAULT_AMOUNT, inShulkerBox: Boolean = false, diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index c4181f67f..3679e155e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -22,6 +22,7 @@ import com.lambda.core.Loadable import com.lambda.event.events.InventoryEvent import com.lambda.event.events.PlayerEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.material.ContainerSelection import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.containers.ChestContainer @@ -90,7 +91,7 @@ object ContainerManager : Loadable { fun container() = container.flatMap { setOf(it) + it.shulkerContainer }.sorted() fun StackSelection.transfer(destination: MaterialContainer, inventory: InventoryConfig = TaskFlowModule.inventory) = - this.findContainerWithMaterial(inventory)?.transfer(this, destination) + findContainerWithMaterial(inventory)?.transfer(this, destination) fun findContainer( block: (MaterialContainer) -> Boolean, @@ -99,20 +100,21 @@ object ContainerManager : Loadable { fun StackSelection.findContainerWithMaterial( inventory: InventoryConfig ): MaterialContainer? = - containerWithMaterial(this, inventory).firstOrNull() + containerWithMaterial(inventory).firstOrNull() fun findContainerWithSpace( selection: StackSelection, ): MaterialContainer? = containerWithSpace(selection).firstOrNull() - fun containerWithMaterial( - selection: StackSelection, + fun StackSelection.containerWithMaterial( inventory: InventoryConfig = TaskFlowModule.inventory, + containerSelection: ContainerSelection = ContainerSelection(), ): List = container() - .sortedWith(inventory.providerPriority.materialComparator(selection)) - .filter { it.materialAvailable(selection) >= selection.count } + .sortedWith(inventory.providerPriority.materialComparator(this)) + .filter { it.materialAvailable(this) >= count } + .filter { containerSelection.matches(it) } fun containerWithSpace( selection: StackSelection, diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 233337b83..925743a70 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -28,6 +28,7 @@ import com.lambda.util.item.ItemStackUtils.empty import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import com.lambda.util.item.ItemStackUtils.spaceLeft import com.lambda.util.item.ItemUtils +import com.lambda.util.item.ItemUtils.toItemCount import com.lambda.util.text.* import net.minecraft.item.ItemStack import net.minecraft.text.Text @@ -44,13 +45,13 @@ abstract class MaterialContainer( literal("\n") literal("Contains ") val available = materialAvailable(selection) - highlighted(if (available == Int.MAX_VALUE) "∞" else available.toString()) + highlighted(if (available == Int.MAX_VALUE) "∞" else available.toItemCount()) literal(" of ") highlighted("${selection.optimalStack?.name?.string}") literal("\n") literal("Could store ") val left = spaceAvailable(selection) - highlighted(if (left == Int.MAX_VALUE) "∞" else left.toString()) + highlighted(if (left == Int.MAX_VALUE) "∞" else left.toItemCount()) literal(" of ") highlighted("${selection.optimalStack?.name?.string}") } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2d73c990a..c18f2f1a3 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -89,13 +89,13 @@ class BuildTask @Ta5kBuilder constructor( private val instantBreaks = mutableSetOf() var breaking = false - var breakingTicks = 0 - var soundsCooldown = 0.0f + private var breakingTicks = 0 + private var soundsCooldown = 0.0f private var placements = 0 private var breaks = 0 private val dropsToCollect = mutableSetOf() -// private var goodPositions = setOf() + // private var goodPositions = setOf() override fun SafeContext.onStart() { (blueprint as? PropagatingBlueprint)?.next() @@ -106,7 +106,7 @@ class BuildTask @Ta5kBuilder constructor( val currentItemStack = player.mainHandStack ?: return@listen currentInteraction?.let { context -> -// TaskFlowModule.drawables = listOf(context) + // TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let when (context) { is PlaceContext -> { @@ -161,17 +161,17 @@ class BuildTask @Ta5kBuilder constructor( onRotate { if (collectDrops && dropsToCollect.isNotEmpty()) return@onRotate -// val sim = blueprint.simulation(interact, rotation, inventory) -// BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> -// sim.simulate(pos.toFastVec()) -// } + // val sim = blueprint.simulation(interact, rotation, inventory) + // BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> + // sim.simulate(pos.toFastVec()) + // } // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) TaskFlowModule.drawables = results.filterIsInstance() .plus(pendingInteractions.toList()) -// .plus(sim.goodPositions()) + // .plus(sim.goodPositions()) if (build.breaksPerTick > 1) { val instantResults = results.filterIsInstance() @@ -239,12 +239,12 @@ class BuildTask @Ta5kBuilder constructor( if (context.sneak) it.input.sneaking = true } -// listen { event -> -// val context = currentInteraction ?: return@listen -// if (context.expectedPos != event.pos) return@listen -// currentInteraction = null -// pendingInteractions.add(context) -// } + // listen { event -> + // val context = currentInteraction ?: return@listen + // if (context.expectedPos != event.pos) return@listen + // currentInteraction = null + // pendingInteractions.add(context) + // } listen(alwaysListen = true) { event -> pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { ctx -> @@ -273,29 +273,27 @@ class BuildTask @Ta5kBuilder constructor( val inRange = context.expectedPos.toCenterPos().isInRange(it.entity.pos, 0.5) val correctMaterial = context.checkedState.block == it.entity.stack.item.block inRange && correctMaterial - }?.let { context -> + }?.let { _ -> dropsToCollect.add(it.entity) } } } - fun SafeContext.placeBlock(hand: Hand, ctx: PlaceContext) { - with(ctx) { - val actionResult = interaction.interactBlock( - player, hand, result - ) + private fun SafeContext.placeBlock(hand: Hand, ctx: PlaceContext) { + val actionResult = interaction.interactBlock( + player, hand, ctx.result + ) - if (actionResult.isAccepted) { - if (actionResult.shouldSwingHand() && interact.swingHand) { - player.swingHand(hand) - } + if (actionResult.isAccepted) { + if (actionResult.shouldSwingHand() && interact.swingHand) { + player.swingHand(hand) + } - if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { - mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) - } - } else { - warn("Internal interaction failed with $actionResult") + if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) } + } else { + warn("Internal interaction failed with $actionResult") } } @@ -309,7 +307,7 @@ class BuildTask @Ta5kBuilder constructor( if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { interaction.blockBreakingCooldown = ctx.buildConfig.breakDelay - interaction.sendSequencedPacket(world) { sequence: Int -> + interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(ctx) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } @@ -322,28 +320,21 @@ class BuildTask @Ta5kBuilder constructor( if (blockState.isAir) return false breakingTicks++ - val progress = blockState.calcItemBlockBreakingDelta( - player, - world, - ctx.expectedPos, - item - ) + val progress = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, item) if (ctx.buildConfig.sounds) { if (soundsCooldown % 4.0f == 0.0f) { val blockSoundGroup = blockState.soundGroup - mc - .soundManager - .play( - PositionedSoundInstance( - blockSoundGroup.hitSound, - SoundCategory.BLOCKS, - (blockSoundGroup.getVolume() + 1.0f) / 8.0f, - blockSoundGroup.getPitch() * 0.5f, - SoundInstance.createRandom(), - ctx.expectedPos - ) + mc.soundManager.play( + PositionedSoundInstance( + blockSoundGroup.hitSound, + SoundCategory.BLOCKS, + (blockSoundGroup.getVolume() + 1.0f) / 8.0f, + blockSoundGroup.getPitch() * 0.5f, + SoundInstance.createRandom(), + ctx.expectedPos ) + ) } soundsCooldown++ } @@ -356,7 +347,7 @@ class BuildTask @Ta5kBuilder constructor( } if (progress >= ctx.buildConfig.breakThreshold) { - interaction.sendSequencedPacket(world) { sequence: Int -> + interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(ctx) PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } @@ -407,10 +398,7 @@ class BuildTask @Ta5kBuilder constructor( private fun SafeContext.setBreakingTextureStage( ctx: BreakContext, - stage: Int = ctx.getBlockBreakingProgress( - breakingTicks, - player, world - ) + stage: Int = ctx.getBlockBreakingProgress(breakingTicks, player, world) ) { world.setBlockBreakingInfo( player.id, @@ -433,8 +421,8 @@ class BuildTask @Ta5kBuilder constructor( } if (breaking) return false - val blockState: BlockState = world.getBlockState(ctx.expectedPos) - var pendingUpdateManager = world.pendingUpdateManager.incrementSequence() + val blockState = blockState(ctx.expectedPos) + val pendingUpdateManager = world.pendingUpdateManager.incrementSequence() val sequence = pendingUpdateManager.sequence val notAir = !blockState.isAir if (notAir && breakingTicks == 0) { @@ -458,7 +446,7 @@ class BuildTask @Ta5kBuilder constructor( ctx.stopBreakPacket(sequence + 1, connection) ctx.startBreakPacket(sequence + 2, connection) ctx.stopBreakPacket(sequence + 3, connection) - (0..3).forEach { i -> + repeat(3) { pendingUpdateManager.incrementSequence() } } else { diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index fc27e4319..574b260cd 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -97,6 +97,12 @@ import net.minecraft.util.math.* import net.minecraft.world.BlockView object BlockUtils { + val Vec3i.blockPos: BlockPos get() = BlockPos(this) + val Block.item: Item get() = asItem() + val Vec3d.flooredPos: BlockPos get() = BlockPos(x.floorToInt(), y.floorToInt(), z.floorToInt()) + fun BlockPos.vecOf(direction: Direction): Vec3d = toCenterPos().add(Vec3d.of(direction.vector).multiply(0.5)) + fun BlockPos.offset(eightWayDirection: EightWayDirection, amount: Int): BlockPos = + add(eightWayDirection.offsetX * amount, 0, eightWayDirection.offsetZ * amount) val signs = setOf( Blocks.OAK_SIGN, @@ -156,7 +162,7 @@ object BlockUtils { val allSigns = signs + wallSigns + hangingSigns + hangingWallSigns - val interactionClasses = setOf( + val interactionBlocks = setOf( AbstractCauldronBlock::class, AbstractFurnaceBlock::class, AbstractSignBlock::class, @@ -293,11 +299,4 @@ object BlockUtils { return speedMultiplier } - - val Vec3i.blockPos: BlockPos get() = BlockPos(this) - val Block.item: Item get() = asItem() - val Vec3d.flooredPos: BlockPos get() = BlockPos(x.floorToInt(), y.floorToInt(), z.floorToInt()) - fun BlockPos.vecOf(direction: Direction): Vec3d = toCenterPos().add(Vec3d.of(direction.vector).multiply(0.5)) - fun BlockPos.offset(eightWayDirection: EightWayDirection, amount: Int): BlockPos = - add(eightWayDirection.offsetX * amount, 0, eightWayDirection.offsetZ * amount) } diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index ca0cbfdad..442275204 100644 --- a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -21,6 +21,7 @@ import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.item.Item +import net.minecraft.item.ItemStack import net.minecraft.item.Items object ItemUtils { @@ -121,9 +122,9 @@ object ItemUtils { val Item.block: Block get() = Block.getBlockFromItem(this) - fun findBestAvailableTool( + fun findBestToolsForBreaking( blockState: BlockState, - availableTools: Set = ItemUtils.tools, + availableTools: Set = tools, ) = availableTools.map { it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) }.filter { (item, speed) -> @@ -146,29 +147,19 @@ object ItemUtils { if (dubs > 0) { append("$dubs dub") - if (dubs > 1) { - append("s") - } - if (shulkers > 0 || remainingItems > 0) { - append(" ") - } + if (dubs > 1) append("s") + if (shulkers > 0 || remainingItems > 0) append(" ") } if (shulkers > 0) { append("$shulkers shulker") - if (shulkers > 1) { - append("s") - } - if (remainingItems > 0) { - append(" ") - } + if (shulkers > 1) append("s") + if (remainingItems > 0) append(" ") } if (remainingItems > 0) { append("$remainingItems item") - if (remainingItems > 1) { - append("s") - } + if (remainingItems > 1) append("s") } } } diff --git a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt index 54cd6d089..1050a99bc 100644 --- a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -19,9 +19,7 @@ package com.lambda.util.player import com.lambda.context.SafeContext import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack -import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType object SlotUtils { @@ -29,8 +27,7 @@ object SlotUtils { val ClientPlayerEntity.storage: List get() = inventory.main.subList(9, 36) val ClientPlayerEntity.hotbarAndStorage: List get() = inventory.main.subList(0, 36) val ClientPlayerEntity.combined: List get() = inventory.main + inventory.armor + inventory.offHand - val ClientPlayerEntity.offhand: ItemStack get() = offHandStack - val Slot.hotbarIndex: Int? get() = if (inventory is PlayerInventory) index + 1 else null + val ClientPlayerEntity.handStacks: List get() = listOf(mainHandStack, offHandStack) fun SafeContext.clickSlot( slotId: Int, From f1fb9b12820f674e37baf1a5dfeab00f1910e78e Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 22 Feb 2025 18:33:23 +0100 Subject: [PATCH 011/364] Refactor tool, inventory, and block usage logic. Unify tool selection logic with new configuration options, improving flexibility for allowed tools and container access. Replace `wrong item` handling with a more robust `wrong item selection` mechanism, enhancing context-aware resolutions. Adjust block-breaking and material-handling processes for cleaner, modular, and efficient code. --- .../com/lambda/config/groups/BuildConfig.kt | 2 + .../com/lambda/config/groups/BuildSettings.kt | 6 ++- .../lambda/config/groups/InventoryConfig.kt | 49 ++++++++++++++++-- .../lambda/config/groups/InventorySettings.kt | 30 ++++++++--- .../construction/context/BreakContext.kt | 4 +- .../construction/context/BuildContext.kt | 6 ++- .../construction/context/PlaceContext.kt | 2 +- .../construction/result/BuildResult.kt | 19 ++++--- .../construction/simulation/BuildSimulator.kt | 50 +++++++++++-------- .../material/ContainerSelection.kt | 8 +++ .../interaction/material/StackSelection.kt | 37 +++++++++++--- .../material/container/ContainerManager.kt | 3 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 27 ++++++---- .../kotlin/com/lambda/util/item/ItemUtils.kt | 1 - 14 files changed, 180 insertions(+), 64 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index f11b352dd..b3d1a2d17 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -39,6 +39,8 @@ interface BuildConfig { val breakConfirmation: BreakConfirmationMode val breaksPerTick: Int val forceSilkTouch: Boolean + val forceFortunePickaxe: Boolean + val minFortuneLevel: Int val ignoredBlocks: Set val breakWeakBlocks: Boolean diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index a21afef07..28c72bc57 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -38,18 +38,20 @@ class BuildSettings( // Breaking override val breakMode by c.setting("Break Mode", BuildConfig.BreakMode.Vanilla) { vis() && page == Page.Break } - override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.1f, "The break amount at which the block is considered broken") { vis() && page == Page.Break } + override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() && page == Page.Break } override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() && page == Page.Break } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.Break } override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() && page == Page.Break } override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() && page == Page.Break } override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() && page == Page.Break } override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.Break } + override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.Break } override val breakConfirmation by c.setting("Break Confirmation", BuildConfig.BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() && page == Page.Break } override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.Break } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.Break } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.Break } - override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.Break } + override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() && page == Page.Break } + override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && page == Page.Break && forceFortunePickaxe } // Placing override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.Place } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt index 6694eb039..2fe47b03c 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt @@ -17,20 +17,61 @@ package com.lambda.config.groups +import com.lambda.interaction.material.ContainerSelection +import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.util.item.ItemUtils import net.minecraft.block.Block +import net.minecraft.item.Item +import net.minecraft.item.Items +import net.minecraft.item.ToolItem +import net.minecraft.item.ToolMaterial +import net.minecraft.item.ToolMaterials interface InventoryConfig { val disposables: Set - val accessEnderChest: Boolean - val swapWithDisposables: Boolean - val providerPriority: Priority val storePriority: Priority - val silentSwap: Boolean + val accessShulkerBoxes: Boolean + val accessEnderChest: Boolean + val accessChests: Boolean + val accessStashes: Boolean + + val containerSelection: ContainerSelection get() = selectContainer { + val allowedContainers = mutableSetOf().apply { + addAll(MaterialContainer.Rank.entries) + if (!accessShulkerBoxes) remove(MaterialContainer.Rank.SHULKER_BOX) + if (!accessEnderChest) remove(MaterialContainer.Rank.ENDER_CHEST) + if (!accessChests) remove(MaterialContainer.Rank.CHEST) + if (!accessStashes) remove(MaterialContainer.Rank.STASH) + } + ofAnyType(*allowedContainers.toTypedArray()) + } + + val useWoodenTools: Boolean + val useStoneTools: Boolean + val useIronTools: Boolean + val useDiamondTools: Boolean + val useNetheriteTools: Boolean + val useGoldTools: Boolean + val useShears: Boolean + val useFlintAndSteel: Boolean + + val allowedTools get() = mutableSetOf().apply { + addAll(ItemUtils.tools) + if (!useWoodenTools) removeIf { it is ToolItem && it.material == ToolMaterials.WOOD } + if (!useStoneTools) removeIf { it is ToolItem && it.material == ToolMaterials.STONE } + if (!useIronTools) removeIf { it is ToolItem && it.material == ToolMaterials.IRON } + if (!useDiamondTools) removeIf { it is ToolItem && it.material == ToolMaterials.DIAMOND } + if (!useNetheriteTools) removeIf { it is ToolItem && it.material == ToolMaterials.NETHERITE } + if (!useGoldTools) removeIf { it is ToolItem && it.material == ToolMaterials.GOLD } + if (!useShears) removeIf { it == Items.SHEARS } + if (!useFlintAndSteel) removeIf { it == Items.FLINT_AND_STEEL } + } enum class Priority { WithMinItems, diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 55fec78c7..5c281a8de 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -24,10 +24,28 @@ class InventorySettings( c: Configurable, vis: () -> Boolean = { true }, ) : InventoryConfig { - override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be ignored when checking for a free slot", vis) - override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest", vis) - override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones", vis) - override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from", vis) - override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to", vis) - override val silentSwap by c.setting("Silent Swap", true, "", vis) + val page by c.setting("Inventory Page", Page.Container, "The page to open when the module is enabled", vis) + + override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container} + override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones") { vis() && page == Page.Container} + override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container} + override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container} + + override val accessShulkerBoxes by c.setting("Access Shulker Boxes", true, "Allow access to the player's shulker boxes") { vis() && page == Page.Access} + override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest") { vis() && page == Page.Access} + override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests") { vis() && page == Page.Access} + override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes") { vis() && page == Page.Access} + + override val useWoodenTools by c.setting("Use Wooden Tools", false, "Use wooden tools to mine blocks") { vis() && page == Page.Tools} + override val useStoneTools by c.setting("Use Stone Tools", false, "Use stone tools to mine blocks") { vis() && page == Page.Tools} + override val useIronTools by c.setting("Use Iron Tools", false, "Use iron tools to mine blocks") { vis() && page == Page.Tools} + override val useDiamondTools by c.setting("Use Diamond Tools", true, "Use diamond tools to mine blocks") { vis() && page == Page.Tools} + override val useNetheriteTools by c.setting("Use Netherite Tools", true, "Use netherite tools to mine blocks") { vis() && page == Page.Tools} + override val useGoldTools by c.setting("Use Gold Tools", false, "Use gold tools to mine blocks") { vis() && page == Page.Tools} + override val useShears by c.setting("Use Shears", true, "Use shears to mine blocks") { vis() && page == Page.Tools} + override val useFlintAndSteel by c.setting("Use Flint and Steel", true, "Use flint and steel to mine blocks?") { vis() && page == Page.Tools} + + enum class Page { + Container, Access, Tools + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 3b2dcbe46..d99420cfb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.construction.context import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude @@ -44,9 +45,10 @@ data class BreakContext( override val rotation: RotationRequest, override val checkedState: BlockState, override val targetState: TargetState, - override var slotIndex: Int?, + override var hotbarIndex: Int, val instantBreak: Boolean, val buildConfig: BuildConfig, + val inventoryConfig: InventoryConfig ) : BuildContext { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index bfddd0865..4801e0337 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -20,8 +20,10 @@ package com.lambda.interaction.construction.context import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.material.container.MaterialContainer import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.block.BlockState +import net.minecraft.item.ItemStack import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d @@ -34,8 +36,10 @@ interface BuildContext : Comparable, Drawable { val targetState: TargetState val expectedPos: BlockPos val checkedState: BlockState - val slotIndex: Int? + val hotbarIndex: Int val rotation: RotationRequest fun shouldRotate(config: BuildConfig): Boolean + + data class LocalizedStack(val container: MaterialContainer, val stack: ItemStack) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index de903aaa1..b46e7d698 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -39,7 +39,7 @@ data class PlaceContext( override val distance: Double, override val expectedState: BlockState, override val checkedState: BlockState, - override val slotIndex: Int?, + override val hotbarIndex: Int, override val expectedPos: BlockPos, override val targetState: TargetState, val sneak: Boolean, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 810db9bb2..1477b9c33 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -22,6 +22,7 @@ import baritone.api.pathing.goals.GoalNear import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ContainerManager.transfer import com.lambda.interaction.material.container.MaterialContainer @@ -29,7 +30,6 @@ import com.lambda.interaction.material.container.containers.MainHandContainer import com.lambda.util.BlockUtils.blockState import com.lambda.util.Nameable import net.minecraft.block.BlockState -import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box @@ -190,24 +190,23 @@ abstract class BuildResult : ComparableResult, Nameable { /** * Player has an inefficient tool equipped. - * @param neededItem The best tool for the block state. + * @param neededSelection The best tool for the block state. */ - data class WrongItem( + data class WrongItemSelection( override val blockPos: BlockPos, val context: BuildContext, - //TODO: probably need to make this a list of items - val neededItem: Item, + val neededSelection: StackSelection, val currentItem: ItemStack, val inventory: InventoryConfig ) : Drawable, Resolvable, BuildResult() { - override val name: String get() = "Wrong item ($currentItem) for ${blockPos.toShortString()} need ${neededItem.name.string}" + override val name: String get() = "Wrong item ($currentItem) for ${blockPos.toShortString()} need $neededSelection" override val rank = Rank.WRONG_ITEM private val color = Color(3, 252, 169, 25) override val pausesParent get() = true - override fun resolve() = neededItem.select() - .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find ${neededItem.name.string} anywhere.") + override fun resolve() = neededSelection + .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find $neededSelection anywhere.") override fun SafeContext.buildRenderer() { if (blockState(blockPos).isAir) { @@ -219,7 +218,7 @@ abstract class BuildResult : ComparableResult, Nameable { override fun compareTo(other: ComparableResult): Int { return when (other) { - is WrongItem -> context.compareTo(other.context) + is WrongItemSelection -> context.compareTo(other.context) else -> super.compareTo(other) } } @@ -256,7 +255,7 @@ abstract class BuildResult : ComparableResult, Nameable { override fun compareTo(other: ComparableResult): Int { return when (other) { - is WrongItem -> context.compareTo(other.context) + is WrongItemSelection -> context.compareTo(other.context) else -> super.compareTo(other) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 6fe995e5d..a69126ce1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -23,6 +23,7 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.processing.ProcessorRegistry.findProcessorForState import com.lambda.interaction.construction.result.BreakResult @@ -32,10 +33,9 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack -import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial -import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.material.container.containers.MainHandContainer import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig @@ -56,6 +56,7 @@ import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal import com.lambda.util.item.ItemUtils.findBestToolsForBreaking import com.lambda.util.math.distSq +import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.copyPlayer import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock @@ -300,7 +301,6 @@ object BuildSimulator { eye.distanceTo(blockHit.pos), resultState, blockState(blockHit.blockPos), - //TODO: idk if this is the right input here player.inventory.selectedSlot, context.blockPos, target, @@ -316,7 +316,7 @@ object BuildSimulator { } if (optimalStack.item != currentHandStack.item) { - acc.add(BuildResult.WrongItem(pos, placeContext, optimalStack.item, currentHandStack, inventory)) + acc.add(BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), currentHandStack, inventory)) return@forEach } @@ -411,9 +411,10 @@ object BuildSimulator { rotationRequest, state, targetState, - player.inventory.selectedSlot + 1, + player.inventory.selectedSlot, instantBreakable(state, pos), - build + build, + inventory ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -459,10 +460,9 @@ object BuildSimulator { val target = lookAt(bestHit.targetRotation, 0.001) val request = RotationRequest(target, rotation) val instant = instantBreakable(state, pos) - val useSlotIndex = player.inventory.selectedSlot + 1 val breakContext = BreakContext( - eye, blockHit, request, state, targetState, useSlotIndex, instant, build + eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant, build, inventory ) if (player.isCreative) { @@ -470,7 +470,7 @@ object BuildSimulator { return acc } - val bestTools = findBestToolsForBreaking(state) + val bestTools = findBestToolsForBreaking(state, inventory.allowedTools) /* there is no good tool for the job */ if (bestTools.isEmpty()) { @@ -487,28 +487,36 @@ object BuildSimulator { return acc } - val bestTool = bestTools.firstOrNull() ?: return acc val toolSelection = if (build.forceSilkTouch) { - selectStack { isItem(bestTool) and hasEnchantment(Enchantments.SILK_TOUCH) } + selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.SILK_TOUCH) } + } else if (build.forceFortunePickaxe) { + selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.minFortuneLevel) } } else { - bestTool.select() + bestTools.select() + } + val silentSwapSelection = selectContainer { + matches(toolSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) } - val containerSelection = selectContainer { - matches(toolSelection) and ofAnyType(MaterialContainer.Rank.OFF_HAND, MaterialContainer.Rank.HOTBAR) + val fullSelection = selectContainer { + matches(toolSelection) and matches(inventory.containerSelection) + } + + val swapCandidates = toolSelection.containerWithMaterial(inventory, silentSwapSelection) + if (swapCandidates.isEmpty()) { + acc.add(BuildResult.WrongItemSelection(pos, breakContext, toolSelection, player.mainHandStack, inventory)) + return acc } - val allContainersWithTools = toolSelection.containerWithMaterial(inventory, containerSelection) - val matchingStacks = allContainersWithTools.associateWith { it.matchingStacks(toolSelection) } - val bestDeltaTool = matchingStacks.mapValues { (_, stacks) -> + + val matchingStacks = swapCandidates.associateWith { it.matchingStacks(toolSelection) } + val (container, toolPair) = matchingStacks.mapValues { (_, stacks) -> stacks.associateWith { state.calcItemBlockBreakingDelta(player, world, pos, it) } .maxByOrNull { it.value } ?.toPair() }.entries.maxByOrNull { it.value?.second ?: 0f }?.toPair() ?: return acc - if (bestDeltaTool.second == null) { - acc.add(BuildResult.WrongItem(pos, breakContext, bestTools.first(), player.activeItem, inventory)) - return acc - } + if (toolPair == null) return acc + breakContext.hotbarIndex = player.hotbar.indexOf(toolPair.first) acc.add(BreakResult.Break(pos, breakContext)) return acc } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt index ff75273e4..9d975bf9c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt @@ -40,6 +40,14 @@ class ContainerSelection { fun matches(stackSelection: StackSelection): (MaterialContainer) -> Boolean = { container -> container.matchingStacks(stackSelection).isNotEmpty() } + /** + * Returns a function that checks whether a given MaterialContainer matches the criteria + * defined in the provided ContainerSelection. + */ + @ContainerSelectionDsl + fun matches(containerSelection: ContainerSelection): (MaterialContainer) -> Boolean = + { container -> containerSelection.matches(container) } + /** * Returns a function that matches containers whose rank is any of the types provided. */ diff --git a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 8b5bb27f2..05fae125d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.material -import com.lambda.interaction.material.ContainerSelection.Companion.ContainerSelectionDsl import com.lambda.util.BlockUtils.item import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import net.minecraft.block.Block @@ -110,6 +109,24 @@ class StackSelection { return { it.item == item } } + /** + * Returns a predicate that matches if the `ItemStack`'s item is one of the specified items in the collection. + * + * @param items The collection of `Item` instances to match against. + * @return A predicate that checks if the `ItemStack`'s item is contained in the provided collection. + */ + @StackSelectionDsl + fun isOneOfItems(items: Collection): (ItemStack) -> Boolean = { it.item in items } + + /** + * Returns a predicate that checks if a given `ItemStack` exists within the provided collection of `ItemStack`s. + * + * @param stacks A collection of `ItemStack` instances to be checked against. + * @return A predicate that evaluates to `true` if the given `ItemStack` is within the specified collection, otherwise `false`. + */ + @StackSelectionDsl + fun isOneOfStacks(stacks: Collection): (ItemStack) -> Boolean = { it in stacks } + /** * [isItem] returns a predicate that matches a specific [Item] instance. * @param T The instance of [Item] to be matched. @@ -218,12 +235,20 @@ class StackSelection { stack.shulkerBoxContents.all { it.isEmpty } } val EVERYTHING: (ItemStack) -> Boolean = { true } + val NOTHING: (ItemStack) -> Boolean = { false } - @ContainerSelectionDsl - fun Item.select(): StackSelection = selectStack { isItem(this@select) } - @ContainerSelectionDsl - fun ItemStack.select(): StackSelection = selectStack { isItemStack(this@select) } - @ContainerSelectionDsl + @StackSelectionDsl + fun Item.select() = selectStack { isItem(this@select) } + @StackSelectionDsl + fun ItemStack.select() = selectStack { isItemStack(this@select) } + @StackSelectionDsl + @JvmName("selectStacks") + fun Collection.select() = selectStack { isOneOfStacks(this@select) } + @StackSelectionDsl + @JvmName("selectItems") + fun Collection.select() = selectStack { isOneOfItems(this@select) } + + @StackSelectionDsl fun ((ItemStack) -> Boolean).select() = selectStack { this@select } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 3679e155e..f37ae9442 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -109,7 +109,7 @@ object ContainerManager : Loadable { fun StackSelection.containerWithMaterial( inventory: InventoryConfig = TaskFlowModule.inventory, - containerSelection: ContainerSelection = ContainerSelection(), + containerSelection: ContainerSelection = inventory.containerSelection, ): List = container() .sortedWith(inventory.providerPriority.materialComparator(this)) @@ -123,6 +123,7 @@ object ContainerManager : Loadable { container() .sortedWith(inventory.providerPriority.spaceComparator(selection)) .filter { it.spaceAvailable(selection) >= selection.count } + .filter { inventory.containerSelection.matches(it) } fun findDisposable(inventory: InventoryConfig = TaskFlowModule.inventory) = container().find { container -> inventory.disposables.any { container.materialAvailable(it.item.select()) >= 0 } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index c18f2f1a3..fdde14818 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -44,7 +44,8 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer -import com.lambda.interaction.request.hotbar.HotbarManager +import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -58,8 +59,8 @@ import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block +import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.SlotUtils.hotbarAndStorage -import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance @@ -79,6 +80,7 @@ class BuildTask @Ta5kBuilder constructor( private val rotation: RotationConfig = TaskFlowModule.rotation, private val interact: InteractionConfig = TaskFlowModule.interact, private val inventory: InventoryConfig = TaskFlowModule.inventory, + private val hotbar: HotbarConfig = TaskFlowModule.hotbar, ) : Task() { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" @@ -108,18 +110,23 @@ class BuildTask @Ta5kBuilder constructor( currentInteraction?.let { context -> // TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let + if (!hotbar.request(HotbarRequest(context.hotbarIndex)).done) return@let when (context) { is PlaceContext -> { if (context.sneak && !player.isSneaking) return@let placeBlock(Hand.MAIN_HAND, context) } is BreakContext -> { - updateBlockBreakingProgress(context, currentItemStack) + if (updateBlockBreakingProgress(context, currentItemStack)) { + if (interact.swingHand) player.swingHand(Hand.MAIN_HAND) + } } } } instantBreaks.forEach { context -> - updateBlockBreakingProgress(context, currentItemStack) + if (updateBlockBreakingProgress(context, currentItemStack)) { + if (interact.swingHand) player.swingHand(Hand.MAIN_HAND) + } pendingInteractions.add(context) } instantBreaks.clear() @@ -256,7 +263,7 @@ class BuildTask @Ta5kBuilder constructor( when (ctx) { is BreakContext -> { if (ctx.buildConfig.breakConfirmation == BuildConfig.BreakConfirmationMode.AwaitThenBreak) { - breakBlock(ctx) + destroyBlock(ctx) } breaks++ } @@ -363,11 +370,11 @@ class BuildTask @Ta5kBuilder constructor( private fun SafeContext.onBlockBreak(ctx: BreakContext) { when (ctx.buildConfig.breakConfirmation) { BuildConfig.BreakConfirmationMode.None -> { - breakBlock(ctx) + destroyBlock(ctx) breaks++ } BuildConfig.BreakConfirmationMode.BreakThenAwait -> { - breakBlock(ctx) + destroyBlock(ctx) pendingInteractions.add(ctx) } BuildConfig.BreakConfirmationMode.AwaitThenBreak -> pendingInteractions.add(ctx) @@ -377,7 +384,7 @@ class BuildTask @Ta5kBuilder constructor( breakingTicks = 0 } - private fun SafeContext.breakBlock(ctx: BreakContext): Boolean { + private fun SafeContext.destroyBlock(ctx: BreakContext): Boolean { if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) @@ -412,11 +419,11 @@ class BuildTask @Ta5kBuilder constructor( if (!world.worldBorder.contains(ctx.expectedPos)) return false if (interaction.currentGameMode.isCreative) { - interaction.sendSequencedPacket(world) { sequence: Int -> + interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(ctx) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } - interaction.blockBreakingCooldown = 5 + interaction.blockBreakingCooldown = 0 return true } if (breaking) return false diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index 442275204..e5bb6d2bf 100644 --- a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -21,7 +21,6 @@ import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.item.Item -import net.minecraft.item.ItemStack import net.minecraft.item.Items object ItemUtils { From 3e15fd53b21cc28741abb17b2ff37af164b893c5 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 25 Feb 2025 17:06:56 +0000 Subject: [PATCH 012/364] semi working break handler --- .../com/lambda/config/groups/BreakSettings.kt | 45 +++ .../com/lambda/config/groups/BuildConfig.kt | 29 +- .../com/lambda/config/groups/BuildSettings.kt | 20 +- .../construction/context/BreakContext.kt | 17 +- .../construction/simulation/BuildSimulator.kt | 12 +- .../interaction/request/RequestHandler.kt | 2 +- .../request/breaking/BreakConfig.kt | 62 +++ .../request/breaking/BreakManager.kt | 354 ++++++++++++++++++ .../request/breaking/BreakRequest.kt | 33 ++ .../modules/player/PacketMineRewrite.kt | 50 +++ .../kotlin/com/lambda/task/tasks/BuildTask.kt | 237 ++---------- 11 files changed, 595 insertions(+), 266 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt new file mode 100644 index 000000000..849c8b3d6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.config.groups + +import com.lambda.config.Configurable +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.breaking.BreakConfig +import com.lambda.util.BlockUtils.allSigns + +class BreakSettings( + c: Configurable, + priority: Priority = 0, + vis: () -> Boolean = { true } +) : BreakConfig(priority) { + override val breakMode by c.setting("Break Mode", BreakMode.Vanilla) { vis() } + override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } + override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } + override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } + override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() } + override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } + override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } + override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() } + override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } + override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } + override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } + override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } + override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } + override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() } + override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index b3d1a2d17..29b9334af 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -17,8 +17,6 @@ package com.lambda.config.groups -import net.minecraft.block.Block - interface BuildConfig { // General val pathing: Boolean @@ -28,35 +26,10 @@ interface BuildConfig { val interactionTimeout: Int // Breaking - val breakMode: BreakMode - val breakThreshold: Float - val doubleBreak: Boolean - val breakDelay: Int - val sounds: Boolean - val particles: Boolean - val breakingTexture: Boolean - val rotateForBreak: Boolean - val breakConfirmation: BreakConfirmationMode - val breaksPerTick: Int - val forceSilkTouch: Boolean - val forceFortunePickaxe: Boolean - val minFortuneLevel: Int - val ignoredBlocks: Set - - val breakWeakBlocks: Boolean + val breakSettings: BreakSettings // Placing val rotateForPlace: Boolean val placeConfirmation: Boolean val placementsPerTick: Int - - enum class BreakMode { - Vanilla, Packet - } - - enum class BreakConfirmationMode { - None, - AwaitThenBreak, - BreakThenAwait - } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 28c72bc57..1f93deab0 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -18,7 +18,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.util.BlockUtils.allSigns +import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode class BuildSettings( c: Configurable, @@ -37,26 +37,12 @@ class BuildSettings( override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking - override val breakMode by c.setting("Break Mode", BuildConfig.BreakMode.Vanilla) { vis() && page == Page.Break } - override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() && page == Page.Break } - override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() && page == Page.Break } - override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.Break } - override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() && page == Page.Break } - override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() && page == Page.Break } - override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() && page == Page.Break } - override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.Break } - override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.Break } - override val breakConfirmation by c.setting("Break Confirmation", BuildConfig.BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() && page == Page.Break } - override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.Break } - override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.Break } - override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.Break } - override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() && page == Page.Break } - override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && page == Page.Break && forceFortunePickaxe } + override val breakSettings = BreakSettings(c) { page == Page.Break && vis() } // Placing override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.Place } override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.Place } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.Place } - override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakConfirmation != BuildConfig.BreakConfirmationMode.None) } + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index d99420cfb..8da92dc7e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -18,25 +18,22 @@ package com.lambda.interaction.construction.context import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.BuildSettings import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.client.network.ClientPlayNetworkHandler -import net.minecraft.entity.player.PlayerEntity import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d -import net.minecraft.world.BlockView import java.awt.Color data class BreakContext( @@ -77,23 +74,13 @@ data class BreakContext( } } - override fun shouldRotate(config: BuildConfig) = config.rotateForBreak + override fun shouldRotate(config: BuildConfig) = config.breakSettings.rotateForBreak override fun SafeContext.buildRenderer() { withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) withState(checkedState, expectedPos, sideColor, result.side) } - fun getBlockBreakingProgress(breakingTicks: Int, player: PlayerEntity, world: BlockView): Int { - val currentItemStack = player.mainHandStack ?: return -1 - val breakDelta = checkedState.calcItemBlockBreakingDelta(player, world, expectedPos, currentItemStack) - val progress = breakDelta * breakingTicks - return if (progress > 0.0f) - ((progress / buildConfig.breakThreshold) * 10.0f).toInt() - else - -1 - } - fun startBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = breakPacket(Action.START_DESTROY_BLOCK, sequence, connection) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index a69126ce1..239ad47c3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -118,7 +118,7 @@ object BuildSimulator { } /* block should be ignored */ - if (state.block in build.ignoredBlocks && target.type == TargetState.Type.AIR) { + if (state.block in build.breakSettings.ignoredBlocks && target.type == TargetState.Type.AIR) { return BuildResult.Ignored(pos) } @@ -339,7 +339,7 @@ object BuildSimulator { val state = blockState(pos) /* is a block that will be destroyed by breaking adjacent blocks */ - if (build.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (build.breakSettings.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { acc.add(BuildResult.Ignored(pos)) return acc } @@ -414,7 +414,7 @@ object BuildSimulator { player.inventory.selectedSlot, instantBreakable(state, pos), build, - inventory + inventory ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -487,10 +487,10 @@ object BuildSimulator { return acc } - val toolSelection = if (build.forceSilkTouch) { + val toolSelection = if (build.breakSettings.forceSilkTouch) { selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.SILK_TOUCH) } - } else if (build.forceFortunePickaxe) { - selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.minFortuneLevel) } + } else if (build.breakSettings.forceFortunePickaxe) { + selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.breakSettings.minFortuneLevel) } } else { bestTools.select() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index d09e6213b..a789e8aef 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -51,7 +51,7 @@ abstract class RequestHandler { * * @return True, if the request was updated. */ - protected fun updateRequest( + protected open fun updateRequest( keepIfNull: Boolean = false, filter: (Map.Entry, R>) -> Boolean = { true } ): Boolean { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt new file mode 100644 index 000000000..670e4e118 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +import com.lambda.config.Configurable +import com.lambda.config.groups.BuildSettings.Page +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.RequestConfig +import com.lambda.interaction.request.hotbar.HotbarManager +import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.util.BlockUtils.allSigns +import net.minecraft.block.Block + +abstract class BreakConfig( + priority: Priority = 0 +) : RequestConfig(priority) { + abstract val breakMode: BreakMode + abstract val breakThreshold: Float + abstract val doubleBreak: Boolean + abstract val breakDelay: Int + abstract val sounds: Boolean + abstract val particles: Boolean + abstract val breakingTexture: Boolean + abstract val rotateForBreak: Boolean + abstract val breakConfirmation: BreakConfirmationMode + abstract val breaksPerTick: Int + abstract val breakWeakBlocks: Boolean + abstract val forceSilkTouch: Boolean + abstract val forceFortunePickaxe: Boolean + abstract val minFortuneLevel: Int + abstract val ignoredBlocks: Set + + override fun requestInternal(request: BreakRequest) { + BreakManager.registerRequest(this, request) + } + + enum class BreakMode { + Vanilla, + Packet + } + + enum class BreakConfirmationMode { + None, + BreakThenAwait, + AwaitThenBreak + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt new file mode 100644 index 000000000..215b50c4e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -0,0 +1,354 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +import com.lambda.config.groups.BuildConfig +import com.lambda.context.SafeContext +import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.request.RequestConfig +import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode +import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta +import com.lambda.util.BlockUtils.fluidState +import com.lambda.util.Communication.info +import com.lambda.util.Communication.warn +import com.lambda.util.collections.LimitedDecayQueue +import net.minecraft.block.OperatorBlock +import net.minecraft.client.sound.PositionedSoundInstance +import net.minecraft.client.sound.SoundInstance +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.ItemStack +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.sound.SoundCategory + +object BreakManager : RequestHandler() { + var buildConfig: BuildConfig = TaskFlowModule.build + var primaryBreakingInfo: BreakInfo? + get() = breakingInfos[0] + set(value) { breakingInfos[0] = value } + var secondaryBreakingInfo: BreakInfo? + get() = breakingInfos[1] + set(value) { breakingInfos[1] = value } + val breakingInfos = arrayOfNulls(2) + + val pendingInteractions = LimitedDecayQueue( + buildConfig.maxPendingInteractions, buildConfig.interactionTimeout * 50L + ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out"); it.nullify() } + + init { + listen(Int.MIN_VALUE) { + updateRequest(true) { true } + + for (it in breakingInfos.reversed()) { + if (interaction.blockBreakingCooldown > 0) { + interaction.blockBreakingCooldown-- + break + } + it?.let { info -> + if (pendingInteractions.contains(info)) return@let + updateBlockBreakingProgress(info, player.mainHandStack) + if (info is BreakInfo.SecondaryBreakInfo) + primaryBreakingInfo?.startedWithSecondary = true + } + } + + val request = currentRequest ?: return@listen + + request.contexts.forEach { requestCtx -> + if (requestCtx == null) return@forEach + if (!canAccept(requestCtx)) return@forEach + + primaryBreakingInfo?.let { primaryInfo -> + if (primaryInfo.startedWithSecondary) return@let + secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx) + } ?: run { + primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx) + } + } + } + + listen(alwaysListen = true) { event -> + var broken = false + val info = pendingInteractions + .firstOrNull { it.context.expectedPos == event.pos } + ?.also { pendingInteractions.remove(it) } + ?: breakingInfos + .firstOrNull { it?.context?.expectedPos == event.pos } + ?.also { + breakBlock(it) + broken = true + } + ?: return@listen + + info.nullify() + + if (!info.context.targetState.matches(event.newState, event.pos, world)) { + this@BreakManager.warn("Update at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.targetState}") + return@listen + } + if (buildConfig.breakSettings.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { + if (!broken) breakBlock(info) + } + currentRequest?.onBreak() + } + } + + fun canAccept(ctx: BreakContext) = + pendingInteractions.none { it.context.expectedPos == ctx.expectedPos } + && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } + + override fun updateRequest( + keepIfNull: Boolean, + filter: (Map.Entry, BreakRequest>) -> Boolean + ): Boolean { + val updatedCurrentRequest = super.updateRequest(keepIfNull, filter) + buildConfig = currentRequest?.primaryContext?.buildConfig ?: TaskFlowModule.build + return updatedCurrentRequest + } + + private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo, item: ItemStack): Boolean { + val ctx = info.context + val hitResult = ctx.result + + if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { + interaction.blockBreakingCooldown = ctx.buildConfig.breakSettings.breakDelay + interaction.sendSequencedPacket(world) { sequence -> + onBlockBreak(info) + PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + } + return true + } + + if (!info.breaking) { + if (!attackBlock(info)) { + info.nullify() + return false + } + return true + } + + val blockState = blockState(ctx.expectedPos) + if (blockState.isAir) { + info.nullify() + return false + } + + info.breakingTicks++ + val progress = blockState.calcItemBlockBreakingDelta( + player, + world, + ctx.expectedPos, + item + ) * info.breakingTicks + + if (ctx.buildConfig.breakSettings.sounds) { + if (info.soundsCooldown % 4.0f == 0.0f) { + val blockSoundGroup = blockState.soundGroup + mc.soundManager.play( + PositionedSoundInstance( + blockSoundGroup.hitSound, + SoundCategory.BLOCKS, + (blockSoundGroup.getVolume() + 1.0f) / 8.0f, + blockSoundGroup.getPitch() * 0.5f, + SoundInstance.createRandom(), + ctx.expectedPos + ) + ) + } + info.soundsCooldown++ + } + + if (ctx.buildConfig.breakSettings.particles) { + mc.particleManager.addBlockBreakingParticles( + ctx.expectedPos, + hitResult.side + ) + } + + if (progress >= info.getBreakThreshold()) { + interaction.sendSequencedPacket(world) { sequence -> + onBlockBreak(info) + PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + } + } + + if (ctx.buildConfig.breakSettings.breakingTexture) { + setBreakingTextureStage(info) + } + + return true + } + + private fun SafeContext.attackBlock(info: BreakInfo): Boolean { + val ctx = info.context + + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false + if (!world.worldBorder.contains(ctx.expectedPos)) return false + + if (interaction.currentGameMode.isCreative) { + interaction.sendSequencedPacket(world) { sequence: Int -> + onBlockBreak(info) + PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) + } + interaction.blockBreakingCooldown = buildConfig.breakSettings.breakDelay + return true + } + if (info.breaking) return false + + val blockState = blockState(ctx.expectedPos) + val pendingUpdateManager = world.pendingUpdateManager.incrementSequence() + val sequence = pendingUpdateManager.sequence + val notAir = !blockState.isAir + if (notAir && info.breakingTicks == 0) { + blockState.onBlockBreakStart(world, ctx.expectedPos, player) + } + + val breakingDelta = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, player.mainHandStack) + if (notAir && breakingDelta >= info.getBreakThreshold()) { + onBlockBreak(info) + } else { + info.apply { + breaking = true + breakingTicks = 1 + soundsCooldown = 0.0f + } + if (ctx.buildConfig.breakSettings.breakingTexture) { + setBreakingTextureStage(info) + } + } + + if (ctx.buildConfig.breakSettings.breakMode == BreakMode.Packet) { + ctx.abortBreakPacket(sequence, connection) + ctx.stopBreakPacket(sequence + 1, connection) + ctx.startBreakPacket(sequence + 2, connection) + ctx.stopBreakPacket(sequence + 3, connection) + repeat(3) { + pendingUpdateManager.incrementSequence() + } + } else { + ctx.startBreakPacket(sequence, connection) + if (breakingDelta < 1) { + ctx.stopBreakPacket(sequence + 1, connection) + pendingUpdateManager.incrementSequence() + } + } + + return true + } + + private fun SafeContext.onBlockBreak(info: BreakInfo) { + when (info.context.buildConfig.breakSettings.breakConfirmation) { + BreakConfirmationMode.None -> { + breakBlock(info) + currentRequest?.onBreak() + info.nullify() + } + BreakConfirmationMode.BreakThenAwait -> { + breakBlock(info) + pendingInteractions.add(info) + } + BreakConfirmationMode.AwaitThenBreak -> { + pendingInteractions.add(info) + } + } + } + + private fun SafeContext.breakBlock(info: BreakInfo): Boolean { + val ctx = info.context + + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false + + if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) + return false + val block = ctx.checkedState.block + if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false + if (ctx.checkedState.isAir) return false + + block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) + val fluidState = fluidState(ctx.expectedPos) + val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) + if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) + + if (ctx.buildConfig.breakSettings.breakingTexture) setBreakingTextureStage(info, -1) + + return setState + } + + private fun SafeContext.setBreakingTextureStage( + info: BreakInfo, + stage: Int = info.getBreakTextureProgress(player, world) + ) { + world.setBlockBreakingInfo( + player.id, + info.context.expectedPos, + stage + ) + } + + abstract class BreakInfo( + var context: BreakContext + ) { + var breaking = false + var breakingTicks = 0 + var soundsCooldown = 0.0f + var startedWithSecondary = false + + fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { + val breakDelta = context.checkedState.calcItemBlockBreakingDelta( + player, + world, + context.expectedPos, + player.mainHandStack + ) + + val progress = breakDelta * breakingTicks + return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 + } + + open fun getBreakThreshold() = + context.buildConfig.breakSettings.breakThreshold + + open fun nullify() {} + + class PrimaryBreakInfo( + ctx: BreakContext + ) : BreakInfo(ctx) { + override fun nullify() { + primaryBreakingInfo = null + } + } + + class SecondaryBreakInfo( + ctx: BreakContext + ) : BreakInfo(ctx) { + override fun getBreakThreshold() = + 1.0f + + override fun nullify() { + secondaryBreakingInfo = null + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt new file mode 100644 index 000000000..8c4bef4c3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.Request + +data class BreakRequest( + val primaryContext: BreakContext, + val secondaryContext: BreakContext? = null, + val prio: Priority = 0, + val onBreak: () -> Unit +) : Request(prio) { + override val done: Boolean + get() = false + val contexts = listOf(primaryContext, secondaryContext) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt new file mode 100644 index 000000000..cf4520dc0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.module.modules.player + +import com.lambda.config.groups.BuildSettings +import com.lambda.event.events.PlayerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.blueprint.StaticBlueprint +import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.task.RootTask.run +import com.lambda.task.tasks.BuildTask +import com.lambda.task.tasks.BuildTask.Companion.build + +object PacketMineRewrite : Module( + "Packet Mine Rewrite", + "automatically breaks blocks, and does it faster", + setOf(ModuleTag.PLAYER) +) { + private val buildConfig = BuildSettings(this) + + var blueprint: StaticBlueprint? = null + var task: BuildTask? = null + + init { + listen { + blueprint = setOf(player.blockPos.add(1, 0, 0), player.blockPos.add(1, 1, 0)).associateWith { TargetState.Air }.toBlueprint() + task?.cancel() + + task = blueprint?.build(build = buildConfig)?.run() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index fdde14818..798cee417 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -44,14 +44,13 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer +import com.lambda.interaction.request.breaking.BreakManager +import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils -import com.lambda.util.BlockUtils.blockState -import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta -import com.lambda.util.BlockUtils.fluidState import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.Formatting.string @@ -65,10 +64,6 @@ import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.entity.ItemEntity -import net.minecraft.item.ItemStack -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action -import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos @@ -97,18 +92,27 @@ class BuildTask @Ta5kBuilder constructor( private var placements = 0 private var breaks = 0 private val dropsToCollect = mutableSetOf() - // private var goodPositions = setOf() +// private var goodPositions = setOf() override fun SafeContext.onStart() { (blueprint as? PropagatingBlueprint)?.next() } + override fun SafeContext.onCancel() { +// currentInteraction?.let { ctx -> +// if (ctx !is BreakContext) return +// if (ctx.buildConfig.breakSettings.breakingTexture) { +// setBreakingTextureStage(ctx, -1) +// } +// } + } + init { listen { val currentItemStack = player.mainHandStack ?: return@listen currentInteraction?.let { context -> - // TaskFlowModule.drawables = listOf(context) +// TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let if (!hotbar.request(HotbarRequest(context.hotbarIndex)).done) return@let when (context) { @@ -116,18 +120,10 @@ class BuildTask @Ta5kBuilder constructor( if (context.sneak && !player.isSneaking) return@let placeBlock(Hand.MAIN_HAND, context) } - is BreakContext -> { - if (updateBlockBreakingProgress(context, currentItemStack)) { - if (interact.swingHand) player.swingHand(Hand.MAIN_HAND) - } - } } } instantBreaks.forEach { context -> - if (updateBlockBreakingProgress(context, currentItemStack)) { - if (interact.swingHand) player.swingHand(Hand.MAIN_HAND) - } - pendingInteractions.add(context) + BreakManager.registerRequest(build.breakSettings, BreakRequest(context) { breaks++ }) } instantBreaks.clear() @@ -168,33 +164,33 @@ class BuildTask @Ta5kBuilder constructor( onRotate { if (collectDrops && dropsToCollect.isNotEmpty()) return@onRotate - // val sim = blueprint.simulation(interact, rotation, inventory) - // BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> - // sim.simulate(pos.toFastVec()) - // } +// val sim = blueprint.simulation(interact, rotation, inventory) +// BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> +// sim.simulate(pos.toFastVec()) +// } // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) TaskFlowModule.drawables = results.filterIsInstance() .plus(pendingInteractions.toList()) - // .plus(sim.goodPositions()) +// .plus(sim.goodPositions()) - if (build.breaksPerTick > 1) { + if (build.breakSettings.breaksPerTick > 1) { val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } .sorted() - .take(build.breaksPerTick) + .take(build.breakSettings.breaksPerTick) instantBreaks.addAll(instantResults.map { it.context }) if (instantResults.isNotEmpty()) return@onRotate } - val resultsWithoutPending = results.filterNot { result -> + val resultsNotBlocked= results.filterNot { result -> result.blockPos in pendingInteractions.map { it.expectedPos } - } - val bestResult = resultsWithoutPending.minOrNull() ?: return@onRotate + }.sorted() + val bestResult = resultsNotBlocked.firstOrNull() ?: return@onRotate when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, @@ -225,6 +221,14 @@ class BuildTask @Ta5kBuilder constructor( if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate currentInteraction = bestResult.context + if (bestResult !is BreakResult.Break) return@onRotate + + val breakRequest = BreakRequest( + bestResult.context, + (resultsNotBlocked.getOrNull(1) as? BreakResult.Break)?.context, + 0 + ) { breaks++ } + BreakManager.registerRequest(build.breakSettings, breakRequest) } is Resolvable -> { @@ -246,12 +250,12 @@ class BuildTask @Ta5kBuilder constructor( if (context.sneak) it.input.sneaking = true } - // listen { event -> - // val context = currentInteraction ?: return@listen - // if (context.expectedPos != event.pos) return@listen - // currentInteraction = null - // pendingInteractions.add(context) - // } +// listen { event -> +// val context = currentInteraction ?: return@listen +// if (context.expectedPos != event.pos) return@listen +// currentInteraction = null +// pendingInteractions.add(context) +// } listen(alwaysListen = true) { event -> pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { ctx -> @@ -261,12 +265,6 @@ class BuildTask @Ta5kBuilder constructor( return@let } when (ctx) { - is BreakContext -> { - if (ctx.buildConfig.breakConfirmation == BuildConfig.BreakConfirmationMode.AwaitThenBreak) { - destroyBlock(ctx) - } - breaks++ - } is PlaceContext -> placements++ } } @@ -304,165 +302,6 @@ class BuildTask @Ta5kBuilder constructor( } } - private fun SafeContext.updateBlockBreakingProgress(ctx: BreakContext, item: ItemStack): Boolean { - if (interaction.blockBreakingCooldown > 0) { - interaction.blockBreakingCooldown-- - return true - } - - val hitResult = ctx.result - - if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { - interaction.blockBreakingCooldown = ctx.buildConfig.breakDelay - interaction.sendSequencedPacket(world) { sequence -> - onBlockBreak(ctx) - PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) - } - return true - } - - if (!breaking) return attackBlock(ctx) - - val blockState = blockState(ctx.expectedPos) - if (blockState.isAir) return false - - breakingTicks++ - val progress = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, item) - - if (ctx.buildConfig.sounds) { - if (soundsCooldown % 4.0f == 0.0f) { - val blockSoundGroup = blockState.soundGroup - mc.soundManager.play( - PositionedSoundInstance( - blockSoundGroup.hitSound, - SoundCategory.BLOCKS, - (blockSoundGroup.getVolume() + 1.0f) / 8.0f, - blockSoundGroup.getPitch() * 0.5f, - SoundInstance.createRandom(), - ctx.expectedPos - ) - ) - } - soundsCooldown++ - } - - if (ctx.buildConfig.particles) { - mc.particleManager.addBlockBreakingParticles( - ctx.expectedPos, - hitResult.side - ) - } - - if (progress >= ctx.buildConfig.breakThreshold) { - interaction.sendSequencedPacket(world) { sequence -> - onBlockBreak(ctx) - PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) - } - } - - if (ctx.buildConfig.breakingTexture) { - setBreakingTextureStage(ctx) - } - - return true - } - - private fun SafeContext.onBlockBreak(ctx: BreakContext) { - when (ctx.buildConfig.breakConfirmation) { - BuildConfig.BreakConfirmationMode.None -> { - destroyBlock(ctx) - breaks++ - } - BuildConfig.BreakConfirmationMode.BreakThenAwait -> { - destroyBlock(ctx) - pendingInteractions.add(ctx) - } - BuildConfig.BreakConfirmationMode.AwaitThenBreak -> pendingInteractions.add(ctx) - } - currentInteraction = null - breaking = false - breakingTicks = 0 - } - - private fun SafeContext.destroyBlock(ctx: BreakContext): Boolean { - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false - - if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) - return false - val block = ctx.checkedState.block - if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false - if (ctx.checkedState.isAir) return false - - block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) - val fluidState = fluidState(ctx.expectedPos) - val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) - if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) - - if (ctx.buildConfig.breakingTexture) setBreakingTextureStage(ctx, -1) - - return setState - } - - private fun SafeContext.setBreakingTextureStage( - ctx: BreakContext, - stage: Int = ctx.getBlockBreakingProgress(breakingTicks, player, world) - ) { - world.setBlockBreakingInfo( - player.id, - ctx.expectedPos, - stage - ) - } - - private fun SafeContext.attackBlock(ctx: BreakContext): Boolean { - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false - if (!world.worldBorder.contains(ctx.expectedPos)) return false - - if (interaction.currentGameMode.isCreative) { - interaction.sendSequencedPacket(world) { sequence -> - onBlockBreak(ctx) - PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) - } - interaction.blockBreakingCooldown = 0 - return true - } - if (breaking) return false - - val blockState = blockState(ctx.expectedPos) - val pendingUpdateManager = world.pendingUpdateManager.incrementSequence() - val sequence = pendingUpdateManager.sequence - val notAir = !blockState.isAir - if (notAir && breakingTicks == 0) { - blockState.onBlockBreakStart(world, ctx.expectedPos, player) - } - - val breakingDelta = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, player.mainHandStack) - if (notAir && breakingDelta >= build.breakThreshold) { - onBlockBreak(ctx) - return true - } else { - breaking = true - soundsCooldown = 0.0f - if (ctx.buildConfig.breakingTexture) { - setBreakingTextureStage(ctx) - } - } - - if (ctx.buildConfig.breakMode == BuildConfig.BreakMode.Packet) { - ctx.abortBreakPacket(sequence, connection) - ctx.stopBreakPacket(sequence + 1, connection) - ctx.startBreakPacket(sequence + 2, connection) - ctx.stopBreakPacket(sequence + 3, connection) - repeat(3) { - pendingUpdateManager.incrementSequence() - } - } else { - ctx.startBreakPacket(sequence, connection) - } - - return true - } - companion object { @Ta5kBuilder fun build( From 0107cac22d68cf5f6e2787ebeab350d85b5a4837 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 25 Feb 2025 17:09:01 +0000 Subject: [PATCH 013/364] imports --- .../lambda/interaction/construction/context/BreakContext.kt | 1 - .../interaction/construction/simulation/BuildSimulator.kt | 2 -- .../com/lambda/interaction/request/breaking/BreakConfig.kt | 5 ----- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 ---- 4 files changed, 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 8da92dc7e..173b45c30 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.construction.context import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.BuildSettings import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 239ad47c3..a1bb983b7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -23,7 +23,6 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.processing.ProcessorRegistry.findProcessorForState import com.lambda.interaction.construction.result.BreakResult @@ -35,7 +34,6 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.container.containers.MainHandContainer import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 670e4e118..78f17b8ef 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -17,13 +17,8 @@ package com.lambda.interaction.request.breaking -import com.lambda.config.Configurable -import com.lambda.config.groups.BuildSettings.Page import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig -import com.lambda.interaction.request.hotbar.HotbarManager -import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.util.BlockUtils.allSigns import net.minecraft.block.Block abstract class BreakConfig( diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 798cee417..2473845e9 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -58,11 +58,7 @@ import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block -import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.SlotUtils.hotbarAndStorage -import net.minecraft.block.OperatorBlock -import net.minecraft.client.sound.PositionedSoundInstance -import net.minecraft.client.sound.SoundInstance import net.minecraft.entity.ItemEntity import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos From 8a06cad6bccdd7188e24165e64ac61ec182f8ed9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 25 Feb 2025 17:51:33 +0000 Subject: [PATCH 014/364] dont keep if null --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 215b50c4e..b67a81e4d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -59,7 +59,9 @@ object BreakManager : RequestHandler() { init { listen(Int.MIN_VALUE) { - updateRequest(true) { true } + info("${breakingInfos.count { it != null }}") + + updateRequest { true } for (it in breakingInfos.reversed()) { if (interaction.blockBreakingCooldown > 0) { From 819f02ec926edfd6a2e9b3ee179beb3a96b804d3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 25 Feb 2025 21:50:09 +0000 Subject: [PATCH 015/364] update breaking infos if request is updated and save the current request for break callbacks --- .../request/breaking/BreakManager.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index b67a81e4d..58ad1166e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -59,9 +59,7 @@ object BreakManager : RequestHandler() { init { listen(Int.MIN_VALUE) { - info("${breakingInfos.count { it != null }}") - - updateRequest { true } + val updated = updateRequest(true) { true } for (it in breakingInfos.reversed()) { if (interaction.blockBreakingCooldown > 0) { @@ -76,9 +74,9 @@ object BreakManager : RequestHandler() { } } - val request = currentRequest ?: return@listen + if (!updated) return@listen - request.contexts.forEach { requestCtx -> + currentRequest?.contexts?.forEach { requestCtx -> if (requestCtx == null) return@forEach if (!canAccept(requestCtx)) return@forEach @@ -92,15 +90,18 @@ object BreakManager : RequestHandler() { } listen(alwaysListen = true) { event -> - var broken = false + var breakBlock = false val info = pendingInteractions .firstOrNull { it.context.expectedPos == event.pos } - ?.also { pendingInteractions.remove(it) } + ?.also { + pendingInteractions.remove(it) + if (buildConfig.breakSettings.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) + breakBlock = true + } ?: breakingInfos .firstOrNull { it?.context?.expectedPos == event.pos } ?.also { - breakBlock(it) - broken = true + breakBlock = true } ?: return@listen @@ -110,8 +111,8 @@ object BreakManager : RequestHandler() { this@BreakManager.warn("Update at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.targetState}") return@listen } - if (buildConfig.breakSettings.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { - if (!broken) breakBlock(info) + if (breakBlock) { + breakBlock(info) } currentRequest?.onBreak() } From ff951125da04d3674d02bc1ccb50aaa2e14d6095 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 25 Feb 2025 22:50:43 +0000 Subject: [PATCH 016/364] update request before and include custom break threshold in break texture overlay stage --- .../request/breaking/BreakManager.kt | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 58ad1166e..44ba84249 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -59,7 +59,19 @@ object BreakManager : RequestHandler() { init { listen(Int.MIN_VALUE) { - val updated = updateRequest(true) { true } + if (updateRequest(true) { true }) { + currentRequest?.contexts?.forEach { requestCtx -> + if (requestCtx == null) return@forEach + if (!canAccept(requestCtx)) return@forEach + + primaryBreakingInfo?.let { primaryInfo -> + if (primaryInfo.startedWithSecondary) return@let + secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx) + } ?: run { + primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx) + } + } + } for (it in breakingInfos.reversed()) { if (interaction.blockBreakingCooldown > 0) { @@ -73,20 +85,6 @@ object BreakManager : RequestHandler() { primaryBreakingInfo?.startedWithSecondary = true } } - - if (!updated) return@listen - - currentRequest?.contexts?.forEach { requestCtx -> - if (requestCtx == null) return@forEach - if (!canAccept(requestCtx)) return@forEach - - primaryBreakingInfo?.let { primaryInfo -> - if (primaryInfo.startedWithSecondary) return@let - secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx) - } ?: run { - primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx) - } - } } listen(alwaysListen = true) { event -> @@ -326,7 +324,7 @@ object BreakManager : RequestHandler() { player.mainHandStack ) - val progress = breakDelta * breakingTicks + val progress = (breakDelta * breakingTicks) / buildConfig.breakSettings.breakThreshold return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } From f3c1b32ad82ae947b1fc60492d963cf93caa8856 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 26 Feb 2025 01:44:50 +0000 Subject: [PATCH 017/364] remove abort at start of packet mine --- .../interaction/request/breaking/BreakManager.kt | 7 +++---- .../lambda/module/modules/player/PacketMineRewrite.kt | 10 +++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 44ba84249..3b0c690a2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -241,11 +241,10 @@ object BreakManager : RequestHandler() { } if (ctx.buildConfig.breakSettings.breakMode == BreakMode.Packet) { - ctx.abortBreakPacket(sequence, connection) + ctx.stopBreakPacket(sequence, connection) + ctx.startBreakPacket(sequence + 1, connection) ctx.stopBreakPacket(sequence + 1, connection) - ctx.startBreakPacket(sequence + 2, connection) - ctx.stopBreakPacket(sequence + 3, connection) - repeat(3) { + repeat(2) { pendingUpdateManager.incrementSequence() } } else { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt index cf4520dc0..1a8f041cd 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt @@ -41,7 +41,15 @@ object PacketMineRewrite : Module( init { listen { - blueprint = setOf(player.blockPos.add(1, 0, 0), player.blockPos.add(1, 1, 0)).associateWith { TargetState.Air }.toBlueprint() + it.cancel() + blueprint = setOf( + player.blockPos.add(1, 0, 0), + player.blockPos.add(1, 1, 0), + player.blockPos.add(1, 0, 1), + player.blockPos.add(1, 1, 1), + player.blockPos.add(1, 0, -1), + player.blockPos.add(1, 1, -1), + ).associateWith { TargetState.Air }.toBlueprint() task?.cancel() task = blueprint?.build(build = buildConfig)?.run() From cc078034e64a7fd1b4ca7c4d2380d41ab559d03e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 26 Feb 2025 01:46:12 +0000 Subject: [PATCH 018/364] rename breakBlock to destroyBlock --- .../lambda/interaction/request/breaking/BreakManager.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 3b0c690a2..4bd048d36 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -110,7 +110,7 @@ object BreakManager : RequestHandler() { return@listen } if (breakBlock) { - breakBlock(info) + destroyBlock(info) } currentRequest?.onBreak() } @@ -261,12 +261,12 @@ object BreakManager : RequestHandler() { private fun SafeContext.onBlockBreak(info: BreakInfo) { when (info.context.buildConfig.breakSettings.breakConfirmation) { BreakConfirmationMode.None -> { - breakBlock(info) + destroyBlock(info) currentRequest?.onBreak() info.nullify() } BreakConfirmationMode.BreakThenAwait -> { - breakBlock(info) + destroyBlock(info) pendingInteractions.add(info) } BreakConfirmationMode.AwaitThenBreak -> { @@ -275,7 +275,7 @@ object BreakManager : RequestHandler() { } } - private fun SafeContext.breakBlock(info: BreakInfo): Boolean { + private fun SafeContext.destroyBlock(info: BreakInfo): Boolean { val ctx = info.context if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false From c2cb4aa183d4d166bb7b074e3f174c48d9645ca7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 26 Feb 2025 18:41:42 +0000 Subject: [PATCH 019/364] make double break setting actually work --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 4bd048d36..4e562557c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -65,6 +65,7 @@ object BreakManager : RequestHandler() { if (!canAccept(requestCtx)) return@forEach primaryBreakingInfo?.let { primaryInfo -> + if (!buildConfig.breakSettings.doubleBreak) return@let if (primaryInfo.startedWithSecondary) return@let secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx) } ?: run { From 508ccc87241d70a86574128cdfcef782f5452ca5 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 27 Feb 2025 15:12:12 +0000 Subject: [PATCH 020/364] make break context final --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 4e562557c..b82980625 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -309,7 +309,7 @@ object BreakManager : RequestHandler() { } abstract class BreakInfo( - var context: BreakContext + val context: BreakContext ) { var breaking = false var breakingTicks = 0 From 91f6a58a7ac1778751678b153239c7cc38aa0a64 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 27 Feb 2025 17:07:40 +0000 Subject: [PATCH 021/364] store onBreak in BreakInfo --- .../request/breaking/BreakManager.kt | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index b82980625..6ed102679 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -60,16 +60,18 @@ object BreakManager : RequestHandler() { init { listen(Int.MIN_VALUE) { if (updateRequest(true) { true }) { - currentRequest?.contexts?.forEach { requestCtx -> - if (requestCtx == null) return@forEach - if (!canAccept(requestCtx)) return@forEach - - primaryBreakingInfo?.let { primaryInfo -> - if (!buildConfig.breakSettings.doubleBreak) return@let - if (primaryInfo.startedWithSecondary) return@let - secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx) - } ?: run { - primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx) + currentRequest?.let { request -> + request.contexts.forEach { requestCtx -> + if (requestCtx == null) return@forEach + if (!canAccept(requestCtx)) return@forEach + + primaryBreakingInfo?.let { primaryInfo -> + if (!buildConfig.breakSettings.doubleBreak) return@let + if (primaryInfo.startedWithSecondary) return@let + secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx, request.onBreak) + } ?: run { + primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx, request.onBreak) + } } } } @@ -113,7 +115,7 @@ object BreakManager : RequestHandler() { if (breakBlock) { destroyBlock(info) } - currentRequest?.onBreak() + info.onBreak() } } @@ -263,7 +265,7 @@ object BreakManager : RequestHandler() { when (info.context.buildConfig.breakSettings.breakConfirmation) { BreakConfirmationMode.None -> { destroyBlock(info) - currentRequest?.onBreak() + info.onBreak() info.nullify() } BreakConfirmationMode.BreakThenAwait -> { @@ -309,7 +311,8 @@ object BreakManager : RequestHandler() { } abstract class BreakInfo( - val context: BreakContext + val context: BreakContext, + val onBreak: () -> Unit ) { var breaking = false var breakingTicks = 0 @@ -334,16 +337,18 @@ object BreakManager : RequestHandler() { open fun nullify() {} class PrimaryBreakInfo( - ctx: BreakContext - ) : BreakInfo(ctx) { + ctx: BreakContext, + onBreak: () -> Unit + ) : BreakInfo(ctx, onBreak) { override fun nullify() { primaryBreakingInfo = null } } class SecondaryBreakInfo( - ctx: BreakContext - ) : BreakInfo(ctx) { + ctx: BreakContext, + onBreak: () -> Unit + ) : BreakInfo(ctx, onBreak) { override fun getBreakThreshold() = 1.0f From f577f0d642c57ee2dda48ff756dd4da2a4739fee Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 27 Feb 2025 19:46:31 +0000 Subject: [PATCH 022/364] small cleanup --- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2473845e9..428e69bd9 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -105,8 +105,6 @@ class BuildTask @Ta5kBuilder constructor( init { listen { - val currentItemStack = player.mainHandStack ?: return@listen - currentInteraction?.let { context -> // TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let @@ -222,7 +220,7 @@ class BuildTask @Ta5kBuilder constructor( val breakRequest = BreakRequest( bestResult.context, (resultsNotBlocked.getOrNull(1) as? BreakResult.Break)?.context, - 0 + prio = 0 ) { breaks++ } BreakManager.registerRequest(build.breakSettings, breakRequest) } From ba5b7d60cd9173c36722ddf1c8e5aa1de84fdbab Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 1 Mar 2025 02:32:29 +0000 Subject: [PATCH 023/364] refactors and build task cleanup / small fixes --- .../construction/context/BreakContext.kt | 2 + .../construction/simulation/BuildSimulator.kt | 3 +- .../request/breaking/BreakManager.kt | 78 ++++++++++--------- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 28 ++++--- 4 files changed, 64 insertions(+), 47 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 173b45c30..b64e78851 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -23,6 +23,7 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState @@ -44,6 +45,7 @@ data class BreakContext( override var hotbarIndex: Int, val instantBreak: Boolean, val buildConfig: BuildConfig, + val rotationConfig: RotationConfig, val inventoryConfig: InventoryConfig ) : BuildContext { private val baseColor = Color(222, 0, 0, 25) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index a1bb983b7..63ed2e8b9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -412,6 +412,7 @@ object BuildSimulator { player.inventory.selectedSlot, instantBreakable(state, pos), build, + rotation, inventory ) acc.add(BreakResult.Break(pos, breakContext)) @@ -460,7 +461,7 @@ object BuildSimulator { val instant = instantBreakable(state, pos) val breakContext = BreakContext( - eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant, build, inventory + eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant, build, rotation, inventory ) if (player.isCreative) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 6ed102679..facfc5611 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -17,16 +17,16 @@ package com.lambda.interaction.request.breaking -import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.request.RequestConfig import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta @@ -44,44 +44,47 @@ import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory object BreakManager : RequestHandler() { - var buildConfig: BuildConfig = TaskFlowModule.build - var primaryBreakingInfo: BreakInfo? + private var primaryBreakingInfo: BreakInfo? get() = breakingInfos[0] set(value) { breakingInfos[0] = value } - var secondaryBreakingInfo: BreakInfo? + private var secondaryBreakingInfo: BreakInfo? get() = breakingInfos[1] set(value) { breakingInfos[1] = value } - val breakingInfos = arrayOfNulls(2) + private val breakingInfos = arrayOfNulls(2) - val pendingInteractions = LimitedDecayQueue( - buildConfig.maxPendingInteractions, buildConfig.interactionTimeout * 50L + private val pendingInteractions = LimitedDecayQueue( + TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out"); it.nullify() } init { - listen(Int.MIN_VALUE) { + listen(Int.MIN_VALUE, alwaysListen = true) { if (updateRequest(true) { true }) { currentRequest?.let { request -> request.contexts.forEach { requestCtx -> if (requestCtx == null) return@forEach if (!canAccept(requestCtx)) return@forEach - primaryBreakingInfo?.let { primaryInfo -> - if (!buildConfig.breakSettings.doubleBreak) return@let - if (primaryInfo.startedWithSecondary) return@let + primaryBreakingInfo?.let primaryInfo@ { primaryInfo -> + if (!primaryInfo.breakSettings.doubleBreak) return@primaryInfo + if (primaryInfo.startedWithSecondary) return@primaryInfo secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx, request.onBreak) } ?: run { primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx, request.onBreak) + pendingInteractions.setMaxSize(requestCtx.buildConfig.maxPendingInteractions) + pendingInteractions.setDecayTime(requestCtx.buildConfig.interactionTimeout * 50L) } } } } - for (it in breakingInfos.reversed()) { + for (info in breakingInfos.reversed()) { if (interaction.blockBreakingCooldown > 0) { interaction.blockBreakingCooldown-- break } - it?.let { info -> + + info?.let { info -> + if (info.breakSettings.rotateForBreak && !info.context.rotation.done) return@listen if (pendingInteractions.contains(info)) return@let updateBlockBreakingProgress(info, player.mainHandStack) if (info is BreakInfo.SecondaryBreakInfo) @@ -90,13 +93,26 @@ object BreakManager : RequestHandler() { } } + onRotate { + run forLoop@ { + breakingInfos.forEach { info -> + info?.let { info -> + if (!info.breakSettings.rotateForBreak) return@let + val rotate = info.context.rotation + RotationManager.registerRequest(info.context.rotationConfig, rotate) + return@forLoop + } + } + } + } + listen(alwaysListen = true) { event -> var breakBlock = false val info = pendingInteractions .firstOrNull { it.context.expectedPos == event.pos } - ?.also { - pendingInteractions.remove(it) - if (buildConfig.breakSettings.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) + ?.also { pending -> + pendingInteractions.remove(pending) + if (pending.breakSettings.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) breakBlock = true } ?: breakingInfos @@ -109,7 +125,7 @@ object BreakManager : RequestHandler() { info.nullify() if (!info.context.targetState.matches(event.newState, event.pos, world)) { - this@BreakManager.warn("Update at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.targetState}") + this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.targetState}") return@listen } if (breakBlock) { @@ -119,19 +135,10 @@ object BreakManager : RequestHandler() { } } - fun canAccept(ctx: BreakContext) = + private fun canAccept(ctx: BreakContext) = pendingInteractions.none { it.context.expectedPos == ctx.expectedPos } && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } - override fun updateRequest( - keepIfNull: Boolean, - filter: (Map.Entry, BreakRequest>) -> Boolean - ): Boolean { - val updatedCurrentRequest = super.updateRequest(keepIfNull, filter) - buildConfig = currentRequest?.primaryContext?.buildConfig ?: TaskFlowModule.build - return updatedCurrentRequest - } - private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo, item: ItemStack): Boolean { val ctx = info.context val hitResult = ctx.result @@ -216,7 +223,7 @@ object BreakManager : RequestHandler() { onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } - interaction.blockBreakingCooldown = buildConfig.breakSettings.breakDelay + interaction.blockBreakingCooldown = info.breakSettings.breakDelay return true } if (info.breaking) return false @@ -262,7 +269,7 @@ object BreakManager : RequestHandler() { } private fun SafeContext.onBlockBreak(info: BreakInfo) { - when (info.context.buildConfig.breakSettings.breakConfirmation) { + when (info.breakSettings.breakConfirmation) { BreakConfirmationMode.None -> { destroyBlock(info) info.onBreak() @@ -318,6 +325,8 @@ object BreakManager : RequestHandler() { var breakingTicks = 0 var soundsCooldown = 0.0f var startedWithSecondary = false + val buildConfig = context.buildConfig + val breakSettings = buildConfig.breakSettings fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { val breakDelta = context.checkedState.calcItemBlockBreakingDelta( @@ -327,18 +336,17 @@ object BreakManager : RequestHandler() { player.mainHandStack ) - val progress = (breakDelta * breakingTicks) / buildConfig.breakSettings.breakThreshold + val progress = (breakDelta * breakingTicks) / breakSettings.breakThreshold return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } - open fun getBreakThreshold() = - context.buildConfig.breakSettings.breakThreshold + open fun getBreakThreshold() = breakSettings.breakThreshold open fun nullify() {} class PrimaryBreakInfo( ctx: BreakContext, - onBreak: () -> Unit + onBreak: () -> Unit, ) : BreakInfo(ctx, onBreak) { override fun nullify() { primaryBreakingInfo = null @@ -347,7 +355,7 @@ object BreakManager : RequestHandler() { class SecondaryBreakInfo( ctx: BreakContext, - onBreak: () -> Unit + onBreak: () -> Unit, ) : BreakInfo(ctx, onBreak) { override fun getBreakThreshold() = 1.0f diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 428e69bd9..9d1a66fec 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -81,9 +81,7 @@ class BuildTask @Ta5kBuilder constructor( private var currentInteraction: BuildContext? = null private val instantBreaks = mutableSetOf() - var breaking = false - private var breakingTicks = 0 - private var soundsCooldown = 0.0f + var breakRequest: BreakRequest? = null private var placements = 0 private var breaks = 0 @@ -105,6 +103,14 @@ class BuildTask @Ta5kBuilder constructor( init { listen { + if (instantBreaks.isNotEmpty()) { + instantBreaks.forEach { context -> + BreakManager.registerRequest(build.breakSettings, BreakRequest(context) { breaks++ }) + } + instantBreaks.clear() + return@listen + } + currentInteraction?.let { context -> // TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let @@ -116,10 +122,6 @@ class BuildTask @Ta5kBuilder constructor( } } } - instantBreaks.forEach { context -> - BreakManager.registerRequest(build.breakSettings, BreakRequest(context) { breaks++ }) - } - instantBreaks.clear() dropsToCollect.firstOrNull()?.let { itemDrop -> if (!world.entities.contains(itemDrop)) { @@ -215,6 +217,7 @@ class BuildTask @Ta5kBuilder constructor( if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate currentInteraction = bestResult.context + if (instantBreaks.isNotEmpty()) return@onRotate if (bestResult !is BreakResult.Break) return@onRotate val breakRequest = BreakRequest( @@ -223,6 +226,7 @@ class BuildTask @Ta5kBuilder constructor( prio = 0 ) { breaks++ } BreakManager.registerRequest(build.breakSettings, breakRequest) + this@BuildTask.breakRequest = breakRequest } is Resolvable -> { @@ -232,10 +236,12 @@ class BuildTask @Ta5kBuilder constructor( } } - if (!build.rotateForPlace) return@onRotate - val rotateTo = currentInteraction?.rotation ?: return@onRotate - - rotation.request(rotateTo) + currentInteraction?.let { currentInteraction -> + if (currentInteraction is BreakContext) return@let + if (!currentInteraction.shouldRotate(build)) return@onRotate + val rotateTo = currentInteraction.rotation + rotation.request(rotateTo) + } } listen { From eb17f25ef1efa7546e9c9db9511c64c93fabb28c Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 3 Mar 2025 00:49:03 +0000 Subject: [PATCH 024/364] lots of refactors --- .../construction/context/BreakContext.kt | 5 - .../construction/simulation/BuildSimulator.kt | 7 +- .../request/breaking/BreakManager.kt | 233 +++++++++++------- .../request/breaking/BreakRequest.kt | 8 +- .../modules/player/PacketMineRewrite.kt | 2 + .../src/main/kotlin/com/lambda/task/Task.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 30 +-- 7 files changed, 157 insertions(+), 130 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index b64e78851..bd9f17791 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -18,12 +18,10 @@ package com.lambda.interaction.construction.context import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState @@ -44,9 +42,6 @@ data class BreakContext( override val targetState: TargetState, override var hotbarIndex: Int, val instantBreak: Boolean, - val buildConfig: BuildConfig, - val rotationConfig: RotationConfig, - val inventoryConfig: InventoryConfig ) : BuildContext { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 63ed2e8b9..27d3452e0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -410,10 +410,7 @@ object BuildSimulator { state, targetState, player.inventory.selectedSlot, - instantBreakable(state, pos), - build, - rotation, - inventory + instantBreakable(state, pos) ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -461,7 +458,7 @@ object BuildSimulator { val instant = instantBreakable(state, pos) val breakContext = BreakContext( - eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant, build, rotation, inventory + eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant ) if (player.isCreative) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index facfc5611..55da9f921 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -17,15 +17,17 @@ package com.lambda.interaction.request.breaking +import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode -import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState @@ -34,6 +36,7 @@ import com.lambda.util.BlockUtils.fluidState import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance @@ -42,6 +45,7 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory +import net.minecraft.util.math.BlockPos object BreakManager : RequestHandler() { private var primaryBreakingInfo: BreakInfo? @@ -54,84 +58,127 @@ object BreakManager : RequestHandler() { private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out"); it.nullify() } + ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } init { - listen(Int.MIN_VALUE, alwaysListen = true) { - if (updateRequest(true) { true }) { - currentRequest?.let { request -> - request.contexts.forEach { requestCtx -> - if (requestCtx == null) return@forEach - if (!canAccept(requestCtx)) return@forEach - - primaryBreakingInfo?.let primaryInfo@ { primaryInfo -> - if (!primaryInfo.breakSettings.doubleBreak) return@primaryInfo - if (primaryInfo.startedWithSecondary) return@primaryInfo - secondaryBreakingInfo = BreakInfo.SecondaryBreakInfo(requestCtx, request.onBreak) - } ?: run { - primaryBreakingInfo = BreakInfo.PrimaryBreakInfo(requestCtx, request.onBreak) - pendingInteractions.setMaxSize(requestCtx.buildConfig.maxPendingInteractions) - pendingInteractions.setDecayTime(requestCtx.buildConfig.interactionTimeout * 50L) - } - } - } + listen(Int.MIN_VALUE) { + if (interaction.blockBreakingCooldown > 0) { + interaction.blockBreakingCooldown-- + return@listen } - for (info in breakingInfos.reversed()) { - if (interaction.blockBreakingCooldown > 0) { - interaction.blockBreakingCooldown-- - break + if (updateRequest(false) { true }) { + currentRequest?.let request@ { request -> + var instaBreaks = 0 + request.contexts + .sortedBy { it.instantBreak } + .forEach { requestCtx -> + if (blockState(requestCtx.expectedPos).isAir) { + return@forEach + } + val infoIndex = handleRequestContext( + requestCtx, + request.onBreak, + request.buildConfig, + request.rotationConfig + ) + if (infoIndex == -1) return@request + if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { + breakingInfos.getOrNull(infoIndex)?.let { info -> + updateBlockBreakingProgress(info, player.mainHandStack) + instaBreaks++ + } + } + } } + } - info?.let { info -> - if (info.breakSettings.rotateForBreak && !info.context.rotation.done) return@listen - if (pendingInteractions.contains(info)) return@let - updateBlockBreakingProgress(info, player.mainHandStack) - if (info is BreakInfo.SecondaryBreakInfo) - primaryBreakingInfo?.startedWithSecondary = true - } + breakingInfos.reversed().filterNotNull().forEach { info -> + if (info.breakConfig.rotateForBreak && !info.context.rotation.done) return@listen + updateBlockBreakingProgress(info, player.mainHandStack) } } onRotate { - run forLoop@ { - breakingInfos.forEach { info -> - info?.let { info -> - if (!info.breakSettings.rotateForBreak) return@let - val rotate = info.context.rotation - RotationManager.registerRequest(info.context.rotationConfig, rotate) - return@forLoop - } + breakingInfos + .filterNotNull() + .firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> + info.rotationConfig.request(info.context.rotation) } - } } - listen(alwaysListen = true) { event -> - var breakBlock = false - val info = pendingInteractions + listen { event -> + pendingInteractions .firstOrNull { it.context.expectedPos == event.pos } - ?.also { pending -> + ?.let { pending -> pendingInteractions.remove(pending) - if (pending.breakSettings.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) - breakBlock = true + if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) return@listen + if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) + destroyBlock(pending) + pending.onBreak() } ?: breakingInfos - .firstOrNull { it?.context?.expectedPos == event.pos } - ?.also { - breakBlock = true + .filterNotNull() + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { info -> + if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) return@listen + info.nullify() + destroyBlock(info) + info.onBreak() } - ?: return@listen + } + } - info.nullify() + private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = + if (targetState.matches(newState, pos, world)) true + else { + this@BreakManager.warn("Break at ${pos.toShortString()} was rejected with $newState instead of $targetState") + false + } - if (!info.context.targetState.matches(event.newState, event.pos, world)) { - this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.targetState}") - return@listen - } - if (breakBlock) { - destroyBlock(info) + private fun handleRequestContext( + requestCtx: BreakContext, + onBreak: () -> Unit, + buildConfig: BuildConfig, + rotationConfig: RotationConfig + ): Int { + if (!canAccept(requestCtx)) return -1 + + primaryBreakingInfo?.let { primaryInfo -> + if (!primaryInfo.breakConfig.doubleBreak) return -1 + if (primaryInfo.startedWithSecondary) return -1 + if (!primaryInfo.breaking) { + secondaryBreakingInfo = BreakInfo( + requestCtx, + BreakInfo.BreakType.Secondary, + onBreak, + buildConfig.breakSettings, + rotationConfig + ) + return 1 + } else { + primaryInfo.type = BreakInfo.BreakType.Secondary + secondaryBreakingInfo = primaryInfo + primaryBreakingInfo = BreakInfo( + requestCtx, + BreakInfo.BreakType.Primary, + onBreak, + buildConfig.breakSettings, + rotationConfig + ) + return 0 } - info.onBreak() + } ?: run { + primaryBreakingInfo = BreakInfo( + requestCtx, + BreakInfo.BreakType.Primary, + onBreak, + buildConfig.breakSettings, + rotationConfig + ) + pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) + pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) + return 0 } } @@ -144,7 +191,7 @@ object BreakManager : RequestHandler() { val hitResult = ctx.result if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { - interaction.blockBreakingCooldown = ctx.buildConfig.breakSettings.breakDelay + interaction.blockBreakingCooldown = info.breakConfig.breakDelay interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) @@ -174,7 +221,7 @@ object BreakManager : RequestHandler() { item ) * info.breakingTicks - if (ctx.buildConfig.breakSettings.sounds) { + if (info.breakConfig.sounds) { if (info.soundsCooldown % 4.0f == 0.0f) { val blockSoundGroup = blockState.soundGroup mc.soundManager.play( @@ -191,7 +238,7 @@ object BreakManager : RequestHandler() { info.soundsCooldown++ } - if (ctx.buildConfig.breakSettings.particles) { + if (info.breakConfig.particles) { mc.particleManager.addBlockBreakingParticles( ctx.expectedPos, hitResult.side @@ -205,7 +252,7 @@ object BreakManager : RequestHandler() { } } - if (ctx.buildConfig.breakSettings.breakingTexture) { + if (info.breakConfig.breakingTexture) { setBreakingTextureStage(info) } @@ -223,7 +270,7 @@ object BreakManager : RequestHandler() { onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } - interaction.blockBreakingCooldown = info.breakSettings.breakDelay + interaction.blockBreakingCooldown = info.breakConfig.breakDelay return true } if (info.breaking) return false @@ -245,12 +292,14 @@ object BreakManager : RequestHandler() { breakingTicks = 1 soundsCooldown = 0.0f } - if (ctx.buildConfig.breakSettings.breakingTexture) { + if (info.breakConfig.breakingTexture) { setBreakingTextureStage(info) } } + if (info.type == BreakInfo.BreakType.Secondary) + primaryBreakingInfo?.startedWithSecondary = true - if (ctx.buildConfig.breakSettings.breakMode == BreakMode.Packet) { + if (info.breakConfig.breakMode == BreakMode.Packet) { ctx.stopBreakPacket(sequence, connection) ctx.startBreakPacket(sequence + 1, connection) ctx.stopBreakPacket(sequence + 1, connection) @@ -259,7 +308,7 @@ object BreakManager : RequestHandler() { } } else { ctx.startBreakPacket(sequence, connection) - if (breakingDelta < 1) { + if (breakingDelta < 1 && (breakingDelta >= 0.7 || info.breakConfig.doubleBreak)) { ctx.stopBreakPacket(sequence + 1, connection) pendingUpdateManager.incrementSequence() } @@ -269,11 +318,10 @@ object BreakManager : RequestHandler() { } private fun SafeContext.onBlockBreak(info: BreakInfo) { - when (info.breakSettings.breakConfirmation) { + when (info.breakConfig.breakConfirmation) { BreakConfirmationMode.None -> { destroyBlock(info) info.onBreak() - info.nullify() } BreakConfirmationMode.BreakThenAwait -> { destroyBlock(info) @@ -283,6 +331,7 @@ object BreakManager : RequestHandler() { pendingInteractions.add(info) } } + info.nullify() } private fun SafeContext.destroyBlock(info: BreakInfo): Boolean { @@ -301,7 +350,7 @@ object BreakManager : RequestHandler() { val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) - if (ctx.buildConfig.breakSettings.breakingTexture) setBreakingTextureStage(info, -1) + if (info.breakConfig.breakingTexture) setBreakingTextureStage(info, -1) return setState } @@ -317,16 +366,17 @@ object BreakManager : RequestHandler() { ) } - abstract class BreakInfo( + data class BreakInfo( val context: BreakContext, - val onBreak: () -> Unit + var type: BreakType, + val onBreak: () -> Unit, + val breakConfig: BreakConfig, + val rotationConfig: RotationConfig ) { var breaking = false var breakingTicks = 0 var soundsCooldown = 0.0f var startedWithSecondary = false - val buildConfig = context.buildConfig - val breakSettings = buildConfig.breakSettings fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { val breakDelta = context.checkedState.calcItemBlockBreakingDelta( @@ -336,33 +386,30 @@ object BreakManager : RequestHandler() { player.mainHandStack ) - val progress = (breakDelta * breakingTicks) / breakSettings.breakThreshold + val progress = (breakDelta * breakingTicks) / breakConfig.breakThreshold return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } - open fun getBreakThreshold() = breakSettings.breakThreshold + fun nullify() = type.nullify() - open fun nullify() {} + fun getBreakThreshold() = + type.getBreakThreshold(breakConfig) - class PrimaryBreakInfo( - ctx: BreakContext, - onBreak: () -> Unit, - ) : BreakInfo(ctx, onBreak) { - override fun nullify() { - primaryBreakingInfo = null - } - } + enum class BreakType { + Primary, + Secondary; - class SecondaryBreakInfo( - ctx: BreakContext, - onBreak: () -> Unit, - ) : BreakInfo(ctx, onBreak) { - override fun getBreakThreshold() = - 1.0f + fun getBreakThreshold(breakConfig: BreakConfig) = + when (this) { + Primary -> breakConfig.breakThreshold + Secondary -> 1.0f + } - override fun nullify() { - secondaryBreakingInfo = null - } + fun nullify() = + when (this) { + Primary -> primaryBreakingInfo = null + Secondary -> secondaryBreakingInfo = null + } } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 8c4bef4c3..fa9927e1d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -17,17 +17,19 @@ package com.lambda.interaction.request.breaking +import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request +import com.lambda.interaction.request.rotation.RotationConfig data class BreakRequest( - val primaryContext: BreakContext, - val secondaryContext: BreakContext? = null, + val contexts: List, + val buildConfig: BuildConfig, + val rotationConfig: RotationConfig, val prio: Priority = 0, val onBreak: () -> Unit ) : Request(prio) { override val done: Boolean get() = false - val contexts = listOf(primaryContext, secondaryContext) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt index 1a8f041cd..7bf28f178 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt @@ -28,6 +28,7 @@ import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask import com.lambda.task.tasks.BuildTask.Companion.build +import com.lambda.util.Communication.info object PacketMineRewrite : Module( "Packet Mine Rewrite", @@ -52,6 +53,7 @@ object PacketMineRewrite : Module( ).associateWith { TargetState.Air }.toBlueprint() task?.cancel() + info("requesting $blueprint") task = blueprint?.build(build = buildConfig)?.run() } } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 5da60e19c..a09d3f579 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -23,7 +23,6 @@ import com.lambda.event.EventFlow.unsubscribe import com.lambda.event.Muteable import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.Communication.logError import com.lambda.util.Nameable @@ -167,6 +166,7 @@ abstract class Task : Nameable, Muteable { @Ta5kBuilder fun cancel() { + runSafe { onCancel() } cancelSubTasks() if (this is RootTask) return if (state == State.COMPLETED || state == State.CANCELLED) return diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 9d1a66fec..45aa798c3 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -44,7 +44,6 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer -import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.hotbar.HotbarRequest @@ -79,9 +78,6 @@ class BuildTask @Ta5kBuilder constructor( build.maxPendingInteractions, build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } private var currentInteraction: BuildContext? = null - private val instantBreaks = mutableSetOf() - - var breakRequest: BreakRequest? = null private var placements = 0 private var breaks = 0 @@ -103,14 +99,6 @@ class BuildTask @Ta5kBuilder constructor( init { listen { - if (instantBreaks.isNotEmpty()) { - instantBreaks.forEach { context -> - BreakManager.registerRequest(build.breakSettings, BreakRequest(context) { breaks++ }) - } - instantBreaks.clear() - return@listen - } - currentInteraction?.let { context -> // TaskFlowModule.drawables = listOf(context) if (context.shouldRotate(build) && !context.rotation.done) return@let @@ -178,9 +166,10 @@ class BuildTask @Ta5kBuilder constructor( .sorted() .take(build.breakSettings.breaksPerTick) - instantBreaks.addAll(instantResults.map { it.context }) - - if (instantResults.isNotEmpty()) return@onRotate + if (instantResults.isNotEmpty()) { + build.breakSettings.request(BreakRequest(instantResults.map { it.context }, build, rotation) { breaks++ }) + return@onRotate + } } val resultsNotBlocked= results.filterNot { result -> @@ -217,16 +206,11 @@ class BuildTask @Ta5kBuilder constructor( if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate currentInteraction = bestResult.context - if (instantBreaks.isNotEmpty()) return@onRotate if (bestResult !is BreakResult.Break) return@onRotate - val breakRequest = BreakRequest( - bestResult.context, - (resultsNotBlocked.getOrNull(1) as? BreakResult.Break)?.context, - prio = 0 - ) { breaks++ } - BreakManager.registerRequest(build.breakSettings, breakRequest) - this@BuildTask.breakRequest = breakRequest + val contexts = resultsNotBlocked.filterIsInstance().take(2).map { it.context } + val request = BreakRequest(contexts, build, rotation) { breaks++ } + build.breakSettings.request(request) } is Resolvable -> { From 3654ee9fbefee2cb0cd9f60cabdfd5102a1d28ff Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 3 Mar 2025 16:41:46 +0000 Subject: [PATCH 025/364] swing --- .../com/lambda/config/groups/BreakSettings.kt | 2 ++ .../request/breaking/BreakConfig.kt | 15 +++++++++++++ .../request/breaking/BreakManager.kt | 22 +++++++++++++++---- .../com/lambda/util/player/PlayerUtils.kt | 9 ++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 849c8b3d6..e831da723 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -31,6 +31,8 @@ class BreakSettings( override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } + override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } + override val swingType by c.setting("Swing Type", SwingType.Vanilla, "The style of swing") override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() } override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 78f17b8ef..1f9ec0a37 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -28,6 +28,8 @@ abstract class BreakConfig( abstract val breakThreshold: Float abstract val doubleBreak: Boolean abstract val breakDelay: Int + abstract val swing: SwingMode + abstract val swingType: SwingType abstract val sounds: Boolean abstract val particles: Boolean abstract val breakingTexture: Boolean @@ -49,6 +51,19 @@ abstract class BreakConfig( Packet } + enum class SwingMode { + Constant, + StartAndEnd, + Start, + End + } + + enum class SwingType { + Vanilla, + Server, + Client + } + enum class BreakConfirmationMode { None, BreakThenAwait, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 55da9f921..a0f227518 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -36,6 +36,7 @@ import com.lambda.util.BlockUtils.fluidState import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +import com.lambda.util.player.swingHandClient import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance @@ -43,6 +44,7 @@ import net.minecraft.client.sound.SoundInstance import net.minecraft.client.world.ClientWorld import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack +import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory import net.minecraft.util.math.BlockPos @@ -204,6 +206,7 @@ object BreakManager : RequestHandler() { info.nullify() return false } + if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info) return true } @@ -245,15 +248,18 @@ object BreakManager : RequestHandler() { ) } + if (info.breakConfig.breakingTexture) { + setBreakingTextureStage(info) + } + if (progress >= info.getBreakThreshold()) { interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } - } - - if (info.breakConfig.breakingTexture) { - setBreakingTextureStage(info) + if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info) + } else { + if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info) } return true @@ -366,6 +372,14 @@ object BreakManager : RequestHandler() { ) } + private fun SafeContext.swingHand(info: BreakInfo) { + when (info.breakConfig.swingType) { + BreakConfig.SwingType.Vanilla -> player.swingHand(player.activeHand) + BreakConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(player.activeHand)) + BreakConfig.SwingType.Client -> swingHandClient(player.activeHand) + } + } + data class BreakInfo( val context: BreakContext, var type: BreakType, diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index 66bb329df..58de52bc5 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -6,6 +6,7 @@ import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.Hand fun SafeContext.copyPlayer(entity: ClientPlayerEntity) = ClientPlayerEntity(mc, world, mc.networkHandler, null, null, entity.isSneaking, entity.isSprinting).apply { @@ -39,3 +40,11 @@ fun SafeContext.spawnFakePlayer( return entity } + +fun SafeContext.swingHandClient(hand: Hand) { + if (!player.handSwinging || player.handSwingTicks >= player.handSwingDuration / 2 || player.handSwingTicks < 0) { + player.handSwingTicks = -1 + player.handSwinging = true + player.preferredHand = hand + } +} \ No newline at end of file From 51adeec0c2f369b93af055c3062744acdf9c266f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 3 Mar 2025 16:50:39 +0000 Subject: [PATCH 026/364] was accidentally breaking blocks a tick too fast and re added access widener --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- common/src/main/resources/lambda.accesswidener | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index a0f227518..4a8b11a63 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -295,7 +295,7 @@ object BreakManager : RequestHandler() { } else { info.apply { breaking = true - breakingTicks = 1 + breakingTicks = 0 soundsCooldown = 0.0f } if (info.breakConfig.breakingTexture) { diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 0ed5dd377..2028c1b13 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -28,6 +28,7 @@ accessible field net/minecraft/client/network/AbstractClientPlayerEntity playerL accessible field net/minecraft/entity/LivingEntity jumpingCooldown I accessible field net/minecraft/entity/Entity pos Lnet/minecraft/util/math/Vec3d; accessible field net/minecraft/client/network/ClientPlayerInteractionManager lastSelectedSlot I +accessible method net/minecraft/entity/LivingEntity getHandSwingDuration ()I # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V From dd58acbfa9581ddefa908070f5e825876c5154a3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 3 Mar 2025 17:05:15 +0000 Subject: [PATCH 027/364] wasnt accidentally breaking a tick too fast, minecraft code just deceived me --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 4a8b11a63..a0f227518 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -295,7 +295,7 @@ object BreakManager : RequestHandler() { } else { info.apply { breaking = true - breakingTicks = 0 + breakingTicks = 1 soundsCooldown = 0.0f } if (info.breakConfig.breakingTexture) { From 7c7da82ff81096c3b9ab502850bf5eb727dbd83e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 3 Mar 2025 19:47:18 +0000 Subject: [PATCH 028/364] fixed double break issue with AwaitThenBreak setting and fixed an unseen instamine bug --- .../request/breaking/BreakManager.kt | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index a0f227518..722d277f9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -75,7 +75,7 @@ object BreakManager : RequestHandler() { request.contexts .sortedBy { it.instantBreak } .forEach { requestCtx -> - if (blockState(requestCtx.expectedPos).isAir) { + if (!canAccept(requestCtx)) { return@forEach } val infoIndex = handleRequestContext( @@ -144,8 +144,6 @@ object BreakManager : RequestHandler() { buildConfig: BuildConfig, rotationConfig: RotationConfig ): Int { - if (!canAccept(requestCtx)) return -1 - primaryBreakingInfo?.let { primaryInfo -> if (!primaryInfo.breakConfig.doubleBreak) return -1 if (primaryInfo.startedWithSecondary) return -1 @@ -167,7 +165,9 @@ object BreakManager : RequestHandler() { onBreak, buildConfig.breakSettings, rotationConfig - ) + ).apply { + startedWithSecondary = true + } return 0 } } ?: run { @@ -177,16 +177,20 @@ object BreakManager : RequestHandler() { onBreak, buildConfig.breakSettings, rotationConfig - ) + ).apply { + if (secondaryBreakingInfo != null) + startedWithSecondary = true + } pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) return 0 } } - private fun canAccept(ctx: BreakContext) = + private fun SafeContext.canAccept(ctx: BreakContext) = pendingInteractions.none { it.context.expectedPos == ctx.expectedPos } && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } + && !blockState(ctx.expectedPos).isAir private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo, item: ItemStack): Boolean { val ctx = info.context @@ -301,9 +305,9 @@ object BreakManager : RequestHandler() { if (info.breakConfig.breakingTexture) { setBreakingTextureStage(info) } + if (info.type == BreakInfo.BreakType.Secondary) + primaryBreakingInfo?.startedWithSecondary = true } - if (info.type == BreakInfo.BreakType.Secondary) - primaryBreakingInfo?.startedWithSecondary = true if (info.breakConfig.breakMode == BreakMode.Packet) { ctx.stopBreakPacket(sequence, connection) From dd79350f434811dfd66c0381fdd030c0c94a0e44 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 4 Mar 2025 01:37:09 +0000 Subject: [PATCH 029/364] decoupled break type class and refactored startedWithSecondary checks --- .../request/breaking/BreakManager.kt | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 722d277f9..83781d0d6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -78,15 +78,15 @@ object BreakManager : RequestHandler() { if (!canAccept(requestCtx)) { return@forEach } - val infoIndex = handleRequestContext( + val breakType = handleRequestContext( requestCtx, request.onBreak, request.buildConfig, request.rotationConfig ) - if (infoIndex == -1) return@request + if (breakType == BreakType.Null) return@request if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { - breakingInfos.getOrNull(infoIndex)?.let { info -> + breakingInfos.getOrNull(breakType.index)?.let { info -> updateBlockBreakingProgress(info, player.mainHandStack) instaBreaks++ } @@ -143,47 +143,42 @@ object BreakManager : RequestHandler() { onBreak: () -> Unit, buildConfig: BuildConfig, rotationConfig: RotationConfig - ): Int { + ): BreakType { primaryBreakingInfo?.let { primaryInfo -> - if (!primaryInfo.breakConfig.doubleBreak) return -1 - if (primaryInfo.startedWithSecondary) return -1 + if (!primaryInfo.breakConfig.doubleBreak) return BreakType.Null + if (primaryInfo.startedWithSecondary) return BreakType.Null if (!primaryInfo.breaking) { secondaryBreakingInfo = BreakInfo( requestCtx, - BreakInfo.BreakType.Secondary, + BreakType.Secondary, onBreak, buildConfig.breakSettings, rotationConfig ) - return 1 + return BreakType.Secondary } else { - primaryInfo.type = BreakInfo.BreakType.Secondary + primaryInfo.type = BreakType.Secondary secondaryBreakingInfo = primaryInfo primaryBreakingInfo = BreakInfo( requestCtx, - BreakInfo.BreakType.Primary, + BreakType.Primary, onBreak, buildConfig.breakSettings, rotationConfig - ).apply { - startedWithSecondary = true - } - return 0 + ) + return BreakType.Primary } } ?: run { primaryBreakingInfo = BreakInfo( requestCtx, - BreakInfo.BreakType.Primary, + BreakType.Primary, onBreak, buildConfig.breakSettings, rotationConfig - ).apply { - if (secondaryBreakingInfo != null) - startedWithSecondary = true - } + ) pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) - return 0 + return BreakType.Primary } } @@ -305,7 +300,7 @@ object BreakManager : RequestHandler() { if (info.breakConfig.breakingTexture) { setBreakingTextureStage(info) } - if (info.type == BreakInfo.BreakType.Secondary) + if (secondaryBreakingInfo != null) primaryBreakingInfo?.startedWithSecondary = true } @@ -412,22 +407,25 @@ object BreakManager : RequestHandler() { fun getBreakThreshold() = type.getBreakThreshold(breakConfig) + } - enum class BreakType { - Primary, - Secondary; + enum class BreakType(val index: Int) { + Primary(0), + Secondary(1), + Null(-1); - fun getBreakThreshold(breakConfig: BreakConfig) = - when (this) { - Primary -> breakConfig.breakThreshold - Secondary -> 1.0f - } + fun getBreakThreshold(breakConfig: BreakConfig) = + when (this) { + Primary -> breakConfig.breakThreshold + Secondary -> 1.0f + else -> -1.0f + } - fun nullify() = - when (this) { - Primary -> primaryBreakingInfo = null - Secondary -> secondaryBreakingInfo = null - } - } + fun nullify() = + when (this) { + Primary -> primaryBreakingInfo = null + Secondary -> secondaryBreakingInfo = null + else -> {} + } } } \ No newline at end of file From 4c6c912e62cbbc9d5b32cb0356b7f5169a94d8eb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 4 Mar 2025 15:11:33 +0000 Subject: [PATCH 030/364] un-opened method --- .../kotlin/com/lambda/interaction/request/RequestHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index a789e8aef..d09e6213b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -51,7 +51,7 @@ abstract class RequestHandler { * * @return True, if the request was updated. */ - protected open fun updateRequest( + protected fun updateRequest( keepIfNull: Boolean = false, filter: (Map.Entry, R>) -> Boolean = { true } ): Boolean { From f8c40e0f62ca750f6499963bfd3f2a0aa2aca536 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 4 Mar 2025 18:25:43 +0000 Subject: [PATCH 031/364] fixed instamine with silent swap --- .../interaction/construction/context/BreakContext.kt | 2 +- .../interaction/construction/simulation/BuildSimulator.kt | 7 ++++--- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 3 ++- common/src/main/kotlin/com/lambda/util/BlockUtils.kt | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index bd9f17791..4a98f9864 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -41,7 +41,7 @@ data class BreakContext( override val checkedState: BlockState, override val targetState: TargetState, override var hotbarIndex: Int, - val instantBreak: Boolean, + var instantBreak: Boolean, ) : BuildContext { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 27d3452e0..bc491ce6b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -410,7 +410,7 @@ object BuildSimulator { state, targetState, player.inventory.selectedSlot, - instantBreakable(state, pos) + instantBreakable(state, pos, build.breakSettings.breakThreshold) ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -455,7 +455,7 @@ object BuildSimulator { val blockHit = bestHit.hit.blockResult ?: return acc val target = lookAt(bestHit.targetRotation, 0.001) val request = RotationRequest(target, rotation) - val instant = instantBreakable(state, pos) + val instant = instantBreakable(state, pos, build.breakSettings.breakThreshold) val breakContext = BreakContext( eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant @@ -513,7 +513,8 @@ object BuildSimulator { if (toolPair == null) return acc breakContext.hotbarIndex = player.hotbar.indexOf(toolPair.first) + breakContext.instantBreak = instantBreakable(state, pos, toolPair.first, build.breakSettings.breakThreshold) acc.add(BreakResult.Break(pos, breakContext)) return acc } -} +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 45aa798c3..d564b9c32 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -166,7 +166,8 @@ class BuildTask @Ta5kBuilder constructor( .sorted() .take(build.breakSettings.breaksPerTick) - if (instantResults.isNotEmpty()) { + instantResults.firstOrNull()?.let { firstInstant -> + if (!hotbar.request(HotbarRequest(firstInstant.context.hotbarIndex)).done) return@onRotate build.breakSettings.request(BreakRequest(instantResults.map { it.context }, build, rotation) { breaks++ }) return@onRotate } diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 574b260cd..e508c9795 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -235,13 +235,13 @@ object BlockUtils { fun SafeContext.fluidState(pos: BlockPos): FluidState = world.getFluidState(pos) fun SafeContext.blockEntity(pos: BlockPos) = world.getBlockEntity(pos) - fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos): Boolean { - val ticksNeeded = 1 / blockState.calcBlockBreakingDelta(player, world, blockPos) + fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, breakThreshold: Float): Boolean { + val ticksNeeded = 1 / (blockState.calcBlockBreakingDelta(player, world, blockPos) / breakThreshold) return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative } - fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, item: ItemStack): Boolean { - val ticksNeeded = 1 / blockState.calcItemBlockBreakingDelta(player, world, blockPos, item) + fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, item: ItemStack, breakThreshold: Float): Boolean { + val ticksNeeded = 1 / (blockState.calcItemBlockBreakingDelta(player, world, blockPos, item) / breakThreshold) return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative } From fe55de4f1fe0f74b39bd5e83bca7fb1936458bd2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 4 Mar 2025 22:32:11 +0000 Subject: [PATCH 032/364] hotbar swapping delegated to the break manager --- .../request/breaking/BreakManager.kt | 27 ++++++++++++------- .../request/breaking/BreakRequest.kt | 2 ++ .../kotlin/com/lambda/task/tasks/BuildTask.kt | 7 +++-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 83781d0d6..3916b7cca 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -27,6 +27,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule @@ -69,24 +70,23 @@ object BreakManager : RequestHandler() { return@listen } - if (updateRequest(false) { true }) { + var swapped = false + + if (updateRequest(true) { true }) { currentRequest?.let request@ { request -> var instaBreaks = 0 request.contexts .sortedBy { it.instantBreak } .forEach { requestCtx -> - if (!canAccept(requestCtx)) { - return@forEach - } - val breakType = handleRequestContext( - requestCtx, - request.onBreak, - request.buildConfig, - request.rotationConfig - ) + if (!canAccept(requestCtx)) return@forEach + val breakType = handleRequestContext(requestCtx, request.onBreak, request.buildConfig, request.rotationConfig) if (breakType == BreakType.Null) return@request if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { breakingInfos.getOrNull(breakType.index)?.let { info -> + if (!swapped) { + request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)) + swapped = true + } updateBlockBreakingProgress(info, player.mainHandStack) instaBreaks++ } @@ -95,6 +95,13 @@ object BreakManager : RequestHandler() { } } + currentRequest?.let { request -> + breakingInfos.firstOrNull()?.let { info -> + if (!swapped && !request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done) + return@listen + } + } + breakingInfos.reversed().filterNotNull().forEach { info -> if (info.breakConfig.rotateForBreak && !info.context.rotation.done) return@listen updateBlockBreakingProgress(info, player.mainHandStack) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index fa9927e1d..9d51aaddb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -21,12 +21,14 @@ import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request +import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotation.RotationConfig data class BreakRequest( val contexts: List, val buildConfig: BuildConfig, val rotationConfig: RotationConfig, + val hotbarConfig: HotbarConfig, val prio: Priority = 0, val onBreak: () -> Unit ) : Request(prio) { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index d564b9c32..06a4d48d5 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -166,9 +166,8 @@ class BuildTask @Ta5kBuilder constructor( .sorted() .take(build.breakSettings.breaksPerTick) - instantResults.firstOrNull()?.let { firstInstant -> - if (!hotbar.request(HotbarRequest(firstInstant.context.hotbarIndex)).done) return@onRotate - build.breakSettings.request(BreakRequest(instantResults.map { it.context }, build, rotation) { breaks++ }) + instantResults.firstOrNull()?.let { + build.breakSettings.request(BreakRequest(instantResults.map { it.context }, build, rotation, hotbar) { breaks++ }) return@onRotate } } @@ -210,7 +209,7 @@ class BuildTask @Ta5kBuilder constructor( if (bestResult !is BreakResult.Break) return@onRotate val contexts = resultsNotBlocked.filterIsInstance().take(2).map { it.context } - val request = BreakRequest(contexts, build, rotation) { breaks++ } + val request = BreakRequest(contexts, build, rotation, hotbar) { breaks++ } build.breakSettings.request(request) } From 9683348bebead74356e51d53e0a874004ab35019 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 6 Mar 2025 00:09:16 +0000 Subject: [PATCH 033/364] break cooldown fixes --- .../request/breaking/BreakManager.kt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 3916b7cca..0aa6bd6e2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -63,10 +63,13 @@ object BreakManager : RequestHandler() { TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + private var blockBreakingCooldown = 0 + init { listen(Int.MIN_VALUE) { - if (interaction.blockBreakingCooldown > 0) { - interaction.blockBreakingCooldown-- + if (isOnBreakCooldown()) { + blockBreakingCooldown-- + updateRequest(true) { true } return@listen } @@ -199,7 +202,7 @@ object BreakManager : RequestHandler() { val hitResult = ctx.result if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { - interaction.blockBreakingCooldown = info.breakConfig.breakDelay + setBreakCooldown(info.breakConfig.breakDelay) interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) @@ -264,6 +267,7 @@ object BreakManager : RequestHandler() { PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info) + setBreakCooldown(info.breakConfig.breakDelay) } else { if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info) } @@ -282,7 +286,7 @@ object BreakManager : RequestHandler() { onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } - interaction.blockBreakingCooldown = info.breakConfig.breakDelay + setBreakCooldown(info.breakConfig.breakDelay) return true } if (info.breaking) return false @@ -386,6 +390,11 @@ object BreakManager : RequestHandler() { } } + private fun isOnBreakCooldown() = blockBreakingCooldown > 0 + private fun setBreakCooldown(cooldown: Int) { + blockBreakingCooldown = cooldown + } + data class BreakInfo( val context: BreakContext, var type: BreakType, From 960f9c9b5d31ea4f809634862546225b5c493c1e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 6 Mar 2025 17:22:54 +0000 Subject: [PATCH 034/364] added place manager and update manager events --- .../com/lambda/config/groups/BreakSettings.kt | 2 +- .../com/lambda/config/groups/BuildConfig.kt | 4 +- .../com/lambda/config/groups/BuildSettings.kt | 9 +- .../com/lambda/config/groups/PlaceSettings.kt | 32 +++++++ .../lambda/event/events/UpdateManagerEvent.kt | 42 +++++++++ .../construction/context/PlaceContext.kt | 2 +- .../interaction/request/RequestHandler.kt | 4 + .../request/breaking/BreakManager.kt | 57 +++++++++--- .../request/breaking/BreakRequest.kt | 8 +- .../request/hotbar/HotbarManager.kt | 5 ++ .../request/placing/PlaceConfig.kt | 39 ++++++++ .../request/placing/PlaceManager.kt | 88 +++++++++++++++++++ .../request/placing/PlaceRequest.kt | 43 +++++++++ .../request/rotation/RotationManager.kt | 8 +- 14 files changed, 319 insertions(+), 24 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt create mode 100644 common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index e831da723..015d4861a 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -32,7 +32,7 @@ class BreakSettings( override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } - override val swingType by c.setting("Swing Type", SwingType.Vanilla, "The style of swing") + override val swingType by c.setting("Swing Type", SwingType.Vanilla, "The style of swing") { vis() } override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() } override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 29b9334af..54f5f9525 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -29,7 +29,5 @@ interface BuildConfig { val breakSettings: BreakSettings // Placing - val rotateForPlace: Boolean - val placeConfirmation: Boolean - val placementsPerTick: Int + val placeSettings: PlaceSettings } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 1f93deab0..2453d1f92 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -19,6 +19,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode +import com.lambda.interaction.request.placing.PlaceConfig class BuildSettings( c: Configurable, @@ -40,9 +41,7 @@ class BuildSettings( override val breakSettings = BreakSettings(c) { page == Page.Break && vis() } // Placing - override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.Place } - override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.Place } - override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.Place } + override val placeSettings = PlaceSettings(c) { page == Page.Place && vis() } - override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) } -} + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt new file mode 100644 index 000000000..b7916c788 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.config.groups + +import com.lambda.config.Configurable +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.placing.PlaceConfig + +class PlaceSettings( + c: Configurable, + priority: Priority = 0, + vis: () -> Boolean = { true } +) : PlaceConfig(priority) { + override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } + override val placeConfirmation by c.setting("Place Confirmation", PlaceConfirmation.PlaceThenAwait, "Wait for block placement confirmation") { vis() } + override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt new file mode 100644 index 000000000..ebc2c0715 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.event.events + +import com.lambda.event.Event + +sealed class UpdateManagerEvent { + sealed class Rotation { + class Pre : Event + class Post : Event + } + + sealed class Hotbar { + class Pre : Event + class Post : Event + } + + sealed class Break { + class Pre : Event + class Post : Event + } + + sealed class Place { + class Pre : Event + class Post : Event + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index b46e7d698..9b93971ae 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -73,5 +73,5 @@ data class PlaceContext( withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) } - override fun shouldRotate(config: BuildConfig) = config.rotateForPlace + override fun shouldRotate(config: BuildConfig) = config.placeSettings.rotateForPlace } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index d09e6213b..5394e551e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request +import com.lambda.event.Event import java.util.concurrent.ConcurrentHashMap /** @@ -66,4 +67,7 @@ abstract class RequestHandler { requestMap.clear() return prev != currentRequest } + + protected abstract fun preEvent(): Event + protected abstract fun postEvent(): Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 0aa6bd6e2..b52d00099 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -19,7 +19,9 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext +import com.lambda.event.EventFlow.post import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext @@ -27,6 +29,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate @@ -67,22 +70,32 @@ object BreakManager : RequestHandler() { init { listen(Int.MIN_VALUE) { + preEvent() + if (isOnBreakCooldown()) { blockBreakingCooldown-- updateRequest(true) { true } + postEvent() return@listen } var swapped = false - if (updateRequest(true) { true }) { + //ToDo: improve instamine / non instamine integration + if (updateRequest { true }) { currentRequest?.let request@ { request -> var instaBreaks = 0 request.contexts .sortedBy { it.instantBreak } .forEach { requestCtx -> if (!canAccept(requestCtx)) return@forEach - val breakType = handleRequestContext(requestCtx, request.onBreak, request.buildConfig, request.rotationConfig) + val breakType = handleRequestContext( + requestCtx, + request.onBreak, + request.buildConfig, + request.rotationConfig, + request.hotbarConfig + ) if (breakType == BreakType.Null) return@request if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { breakingInfos.getOrNull(breakType.index)?.let { info -> @@ -98,17 +111,19 @@ object BreakManager : RequestHandler() { } } - currentRequest?.let { request -> - breakingInfos.firstOrNull()?.let { info -> - if (!swapped && !request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done) - return@listen + breakingInfos.firstOrNull()?.let { info -> + if ((!swapped && !info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done) + || (info.breakConfig.rotateForBreak && !info.context.rotation.done)) { + postEvent() + return@listen } } breakingInfos.reversed().filterNotNull().forEach { info -> - if (info.breakConfig.rotateForBreak && !info.context.rotation.done) return@listen updateBlockBreakingProgress(info, player.mainHandStack) } + + postEvent() } onRotate { @@ -152,7 +167,8 @@ object BreakManager : RequestHandler() { requestCtx: BreakContext, onBreak: () -> Unit, buildConfig: BuildConfig, - rotationConfig: RotationConfig + rotationConfig: RotationConfig, + hotbarConfig: HotbarConfig ): BreakType { primaryBreakingInfo?.let { primaryInfo -> if (!primaryInfo.breakConfig.doubleBreak) return BreakType.Null @@ -163,7 +179,8 @@ object BreakManager : RequestHandler() { BreakType.Secondary, onBreak, buildConfig.breakSettings, - rotationConfig + rotationConfig, + hotbarConfig ) return BreakType.Secondary } else { @@ -174,7 +191,8 @@ object BreakManager : RequestHandler() { BreakType.Primary, onBreak, buildConfig.breakSettings, - rotationConfig + rotationConfig, + hotbarConfig ) return BreakType.Primary } @@ -184,7 +202,8 @@ object BreakManager : RequestHandler() { BreakType.Primary, onBreak, buildConfig.breakSettings, - rotationConfig + rotationConfig, + hotbarConfig ) pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) @@ -400,7 +419,8 @@ object BreakManager : RequestHandler() { var type: BreakType, val onBreak: () -> Unit, val breakConfig: BreakConfig, - val rotationConfig: RotationConfig + val rotationConfig: RotationConfig, + val hotbarConfig: HotbarConfig ) { var breaking = false var breakingTicks = 0 @@ -444,4 +464,17 @@ object BreakManager : RequestHandler() { else -> {} } } + + fun Any.onBreak( + alwaysListen: Boolean = false, + block: SafeContext.() -> Unit + ) = listen(0, alwaysListen) { block() } + + fun Any.onBreakPost( + alwaysListen: Boolean = false, + block: SafeContext.() -> Unit + ) = listen(0, alwaysListen) { block() } + + override fun preEvent() = UpdateManagerEvent.Break.Pre().post() + override fun postEvent() = UpdateManagerEvent.Break.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 9d51aaddb..1896964fc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -23,6 +23,8 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState data class BreakRequest( val contexts: List, @@ -33,5 +35,9 @@ data class BreakRequest( val onBreak: () -> Unit ) : Request(prio) { override val done: Boolean - get() = false + get() = runSafe { + contexts.all { + ctx -> ctx.targetState.matches(blockState(ctx.expectedPos), ctx.expectedPos, world) + } + } == true } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 87192602b..eb0312f26 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -18,8 +18,10 @@ package com.lambda.interaction.request.hotbar import com.lambda.core.Loadable +import com.lambda.event.EventFlow.post import com.lambda.event.events.InventoryEvent import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.RequestHandler import com.lambda.threading.runSafe @@ -59,4 +61,7 @@ object HotbarManager : RequestHandler(), Loadable { } } } + + override fun preEvent() = UpdateManagerEvent.Hotbar.Pre().post() + override fun postEvent() = UpdateManagerEvent.Hotbar.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt new file mode 100644 index 000000000..544d10882 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.placing + +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.RequestConfig + +abstract class PlaceConfig( + priority: Priority +) : RequestConfig(priority) { + abstract val rotateForPlace: Boolean + abstract val placeConfirmation: PlaceConfirmation + abstract val placementsPerTick: Int + + override fun requestInternal(request: PlaceRequest) { + PlaceManager.registerRequest(this, request) + } + + enum class PlaceConfirmation { + None, + PlaceThenAwait, + AwaitThenPlace + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt new file mode 100644 index 000000000..b90495519 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.placing + +import com.lambda.context.SafeContext +import com.lambda.event.EventFlow.post +import com.lambda.event.events.MovementEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.util.Communication.warn +import net.minecraft.util.Hand + +object PlaceManager : RequestHandler() { + init { + listen(Int.MIN_VALUE) { + preEvent() + + if (!updateRequest { true }) { + postEvent() + return@listen + } + + currentRequest?.let { request -> + if (request.placeContext.sneak && !player.isSneaking + || (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done) + || (!request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)).done) + ) { + postEvent() + return@listen + } + placeBlock(request, Hand.MAIN_HAND) + } + + postEvent() + } + + onRotate { + currentRequest?.let { request -> + if (request.buildConfig.placeSettings.rotateForPlace) + request.rotationConfig.request(request.placeContext.rotation) + } + } + + listen { + if (currentRequest?.placeContext?.sneak == true) it.input.sneaking = true + } + } + + private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) { + val actionResult = interaction.interactBlock( + player, hand, request.placeContext.result + ) + + if (actionResult.isAccepted) { + if (actionResult.shouldSwingHand() && request.interactionConfig.swingHand) { + player.swingHand(hand) + } + + if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + } + } else { + warn("Internal interaction failed with $actionResult") + } + } + + override fun preEvent() = UpdateManagerEvent.Place.Pre().post() + override fun postEvent() = UpdateManagerEvent.Place.Post().post() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt new file mode 100644 index 000000000..bce87c5a0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.placing + +import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.Request +import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState + +data class PlaceRequest( + val placeContext: PlaceContext, + val buildConfig: BuildConfig, + val rotationConfig: RotationConfig, + val hotbarConfig: HotbarConfig, + val interactionConfig: InteractionConfig, + val prio: Priority, + val onPlace: () -> Unit +) : Request(prio) { + override val done: Boolean + get() = runSafe { + placeContext.targetState.matches(blockState(placeContext.expectedPos), placeContext.expectedPos, world) + } == true +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 6937f7210..d838f3e00 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -25,7 +25,6 @@ import com.lambda.event.events.* import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.RequestHandler -import com.lambda.interaction.request.rotation.Rotation.Companion.fixSensitivity import com.lambda.interaction.request.rotation.Rotation.Companion.slerp import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.modules.client.Baritone @@ -69,6 +68,8 @@ object RotationManager : RequestHandler(), Loadable { // For some reason we have to update AFTER sending player packets // instead of updating on TickEvent.Pre (am I doing something wrong?) listen(Int.MIN_VALUE) { + preEvent() + // Update the request val changed = updateRequest(true) { entry -> // skip requests that have failed to build the rotation @@ -109,6 +110,8 @@ object RotationManager : RequestHandler(), Loadable { if (--it.decayTicks >= 0) return@let currentRequest = null } + + postEvent() } listen { event -> @@ -259,4 +262,7 @@ object RotationManager : RequestHandler(), Loadable { } } } + + override fun preEvent() = UpdateManagerEvent.Rotation.Pre() + override fun postEvent() = UpdateManagerEvent.Rotation.Post() } \ No newline at end of file From b0073336040845bb5bb6c62b46f90ab39215b604 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 6 Mar 2025 19:28:29 +0000 Subject: [PATCH 035/364] sync hotbar slot and added pending interactions collection to place manager for future use --- .../lambda/interaction/request/hotbar/HotbarManager.kt | 1 + .../lambda/interaction/request/placing/PlaceManager.kt | 10 ++++++++++ common/src/main/resources/lambda.accesswidener | 1 + 3 files changed, 12 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index eb0312f26..75b4df106 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -48,6 +48,7 @@ object HotbarManager : RequestHandler(), Loadable { listen { updateRequest() + interaction.syncSelectedSlot() } listen { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index b90495519..cefd778f9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -24,12 +24,20 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.breaking.BreakManager.BreakInfo import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.Communication.info import com.lambda.util.Communication.warn +import com.lambda.util.collections.LimitedDecayQueue import net.minecraft.util.Hand object PlaceManager : RequestHandler() { + private val pendingInteractions = LimitedDecayQueue( + TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L + ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + init { listen(Int.MIN_VALUE) { preEvent() @@ -47,6 +55,8 @@ object PlaceManager : RequestHandler() { postEvent() return@listen } + pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions) + pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L) placeBlock(request, Hand.MAIN_HAND) } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 2028c1b13..c6bdaf956 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -29,6 +29,7 @@ accessible field net/minecraft/entity/LivingEntity jumpingCooldown I accessible field net/minecraft/entity/Entity pos Lnet/minecraft/util/math/Vec3d; accessible field net/minecraft/client/network/ClientPlayerInteractionManager lastSelectedSlot I accessible method net/minecraft/entity/LivingEntity getHandSwingDuration ()I +accessible method net/minecraft/client/network/ClientPlayerInteractionManager syncSelectedSlot ()V # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V From 2900b09d703c2cdfa2e008f59ffa3a0e2105a6be Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 6 Mar 2025 21:03:32 +0000 Subject: [PATCH 036/364] moved build task logic from on rotate to on tick pre --- .../request/placing/PlaceRequest.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 162 +++++------------- 2 files changed, 47 insertions(+), 117 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index bce87c5a0..7cec84ce2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -33,7 +33,7 @@ data class PlaceRequest( val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, val interactionConfig: InteractionConfig, - val prio: Priority, + val prio: Priority = 0, val onPlace: () -> Unit ) : Request(prio) { override val done: Boolean diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 06a4d48d5..49a05c3f4 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -25,19 +25,14 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.EntityEvent -import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent -import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.PropagatingBlueprint import com.lambda.interaction.construction.blueprint.TickingBlueprint import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint -import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext -import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.result.* import com.lambda.interaction.construction.simulation.BuildGoal import com.lambda.interaction.construction.simulation.BuildSimulator.simulate @@ -46,7 +41,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig -import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -99,59 +94,7 @@ class BuildTask @Ta5kBuilder constructor( init { listen { - currentInteraction?.let { context -> -// TaskFlowModule.drawables = listOf(context) - if (context.shouldRotate(build) && !context.rotation.done) return@let - if (!hotbar.request(HotbarRequest(context.hotbarIndex)).done) return@let - when (context) { - is PlaceContext -> { - if (context.sneak && !player.isSneaking) return@let - placeBlock(Hand.MAIN_HAND, context) - } - } - } - - dropsToCollect.firstOrNull()?.let { itemDrop -> - if (!world.entities.contains(itemDrop)) { - dropsToCollect.remove(itemDrop) - BaritoneUtils.cancel() - return@listen - } - - val noInventorySpace = player.hotbarAndStorage.none { it.isEmpty } - if (noInventorySpace) { - val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { - it.stack.item.block in TaskFlowModule.inventory.disposables - } ?: run { - failure("No item in inventory to throw but inventory is full and cant pick up item drop") - return@listen - } - transfer(player.currentScreenHandler) { - throwStack(stackToThrow.id) - }.execute(this@BuildTask) - return@listen - } - - BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos)) - } - } - - listen { - (blueprint as? TickingBlueprint)?.tick() - - if (finishOnDone && blueprint.structure.isEmpty()) { - failure("Structure is empty") - return@listen - } - } - - onRotate { - if (collectDrops && dropsToCollect.isNotEmpty()) return@onRotate - -// val sim = blueprint.simulation(interact, rotation, inventory) -// BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> -// sim.simulate(pos.toFastVec()) -// } + if (collectDrops()) return@listen // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) @@ -168,31 +111,31 @@ class BuildTask @Ta5kBuilder constructor( instantResults.firstOrNull()?.let { build.breakSettings.request(BreakRequest(instantResults.map { it.context }, build, rotation, hotbar) { breaks++ }) - return@onRotate + return@listen } } - val resultsNotBlocked= results.filterNot { result -> + val resultsNotBlocked = results.filterNot { result -> result.blockPos in pendingInteractions.map { it.expectedPos } }.sorted() - val bestResult = resultsNotBlocked.firstOrNull() ?: return@onRotate + val bestResult = resultsNotBlocked.firstOrNull() ?: return@listen when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { - if (pendingInteractions.isNotEmpty()) return@onRotate + if (pendingInteractions.isNotEmpty()) return@listen if (blueprint is PropagatingBlueprint) { blueprint.next() - return@onRotate + return@listen } if (finishOnDone) success() } is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { - if (!build.pathing) return@onRotate + if (!build.pathing) return@listen val sim = blueprint.simulation(interact, rotation, inventory, build) val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) @@ -203,14 +146,17 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { - if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate - - currentInteraction = bestResult.context - if (bestResult !is BreakResult.Break) return@onRotate - - val contexts = resultsNotBlocked.filterIsInstance().take(2).map { it.context } - val request = BreakRequest(contexts, build, rotation, hotbar) { breaks++ } - build.breakSettings.request(request) + if (pendingInteractions.size >= build.maxPendingInteractions) return@listen + val breakContexts = resultsNotBlocked.filterIsInstance().map { it.context } + if (breakContexts.isNotEmpty()) { + val request = BreakRequest(breakContexts, build, rotation, hotbar) { breaks++ } + build.breakSettings.request(request) + return@listen + } + if (bestResult !is PlaceResult.Place) return@listen + build.placeSettings.request( + PlaceRequest(bestResult.context, build, rotation, hotbar, interact) { placements++ } + ) } is Resolvable -> { @@ -219,38 +165,14 @@ class BuildTask @Ta5kBuilder constructor( bestResult.resolve().execute(this@BuildTask) } } - - currentInteraction?.let { currentInteraction -> - if (currentInteraction is BreakContext) return@let - if (!currentInteraction.shouldRotate(build)) return@onRotate - val rotateTo = currentInteraction.rotation - rotation.request(rotateTo) - } - } - - listen { - val context = currentInteraction ?: return@listen - if (context !is PlaceContext) return@listen - if (context.sneak) it.input.sneaking = true } -// listen { event -> -// val context = currentInteraction ?: return@listen -// if (context.expectedPos != event.pos) return@listen -// currentInteraction = null -// pendingInteractions.add(context) -// } + listen { + (blueprint as? TickingBlueprint)?.tick() - listen(alwaysListen = true) { event -> - pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { ctx -> - pendingInteractions.remove(ctx) - if (!ctx.targetState.matches(event.newState, event.pos, world)) { - this@BuildTask.warn("Update at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${ctx.targetState}") - return@let - } - when (ctx) { - is PlaceContext -> placements++ - } + if (finishOnDone && blueprint.structure.isEmpty()) { + failure("Structure is empty") + return@listen } } @@ -268,23 +190,31 @@ class BuildTask @Ta5kBuilder constructor( } } - private fun SafeContext.placeBlock(hand: Hand, ctx: PlaceContext) { - val actionResult = interaction.interactBlock( - player, hand, ctx.result - ) - - if (actionResult.isAccepted) { - if (actionResult.shouldSwingHand() && interact.swingHand) { - player.swingHand(hand) + private fun SafeContext.collectDrops() = + dropsToCollect.firstOrNull()?.let { itemDrop -> + if (!world.entities.contains(itemDrop)) { + dropsToCollect.remove(itemDrop) + BaritoneUtils.cancel() + return true } - if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { - mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + val noInventorySpace = player.hotbarAndStorage.none { it.isEmpty } + if (noInventorySpace) { + val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { + it.stack.item.block in TaskFlowModule.inventory.disposables + } ?: run { + failure("No item in inventory to throw but inventory is full and cant pick up item drop") + return true + } + transfer(player.currentScreenHandler) { + throwStack(stackToThrow.id) + }.execute(this@BuildTask) + return true } - } else { - warn("Internal interaction failed with $actionResult") - } - } + + BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos)) + return true + } ?: false companion object { @Ta5kBuilder From 739e9ad61c2c5d98a12bed5e981fc241db94df8d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 6 Mar 2025 22:16:04 +0000 Subject: [PATCH 037/364] post hotbar manager pre and post events --- .../com/lambda/interaction/request/hotbar/HotbarManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 75b4df106..11b1343f9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -47,8 +47,10 @@ object HotbarManager : RequestHandler(), Loadable { } listen { + preEvent() updateRequest() interaction.syncSelectedSlot() + postEvent() } listen { From e98523b2e3bdb8569feca5f6ea14add38e1df169 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 6 Mar 2025 23:57:54 +0000 Subject: [PATCH 038/364] fixed place managers pending interactions type --- .../com/lambda/interaction/request/placing/PlaceManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index cefd778f9..0bc455682 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -23,8 +23,8 @@ import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.request.RequestHandler -import com.lambda.interaction.request.breaking.BreakManager.BreakInfo import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule @@ -34,9 +34,9 @@ import com.lambda.util.collections.LimitedDecayQueue import net.minecraft.util.Hand object PlaceManager : RequestHandler() { - private val pendingInteractions = LimitedDecayQueue( + private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } init { listen(Int.MIN_VALUE) { From d71fcdd9b65a83bae5ffd91063c0759a90270261 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 7 Mar 2025 02:00:04 +0000 Subject: [PATCH 039/364] refactors n stuff --- .../request/breaking/BreakManager.kt | 83 ++++++++++--------- .../request/breaking/BreakRequest.kt | 4 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 15 ++-- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index b52d00099..24cbbea5a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -40,12 +40,14 @@ import com.lambda.util.BlockUtils.fluidState import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.swingHandClient import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.ItemEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket @@ -89,13 +91,12 @@ object BreakManager : RequestHandler() { .sortedBy { it.instantBreak } .forEach { requestCtx -> if (!canAccept(requestCtx)) return@forEach - val breakType = handleRequestContext( - requestCtx, - request.onBreak, - request.buildConfig, - request.rotationConfig, - request.hotbarConfig - ) + val breakType = with(request) { + handleRequestContext(requestCtx, + buildConfig, rotationConfig, hotbarConfig, + onBreak, onItemDrop + ) + } if (breakType == BreakType.Null) return@request if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { breakingInfos.getOrNull(breakType.index)?.let { info -> @@ -154,6 +155,24 @@ object BreakManager : RequestHandler() { info.onBreak() } } + + //ToDo: drop callback stuff +// // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work +// listen { +// if (it.entity !is ItemEntity) return@listen +// pendingInteractions +// .firstOrNull { info -> matchesBlockItem(info, it.entity) } +// ?.onItemDrop?.invoke(it.entity) +// ?: breakingInfos +// .filterNotNull() +// .firstOrNull { info -> matchesBlockItem(info, it.entity) }?.onItemDrop?.invoke(it.entity) +// } + } + + private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { + val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) + val correctMaterial = info.context.checkedState.block == entity.stack.item.block + return inRange && correctMaterial } private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = @@ -165,52 +184,41 @@ object BreakManager : RequestHandler() { private fun handleRequestContext( requestCtx: BreakContext, - onBreak: () -> Unit, buildConfig: BuildConfig, rotationConfig: RotationConfig, - hotbarConfig: HotbarConfig + hotbarConfig: HotbarConfig, + onBreak: () -> Unit, + onItemDrop: (ItemEntity) -> Unit ): BreakType { + val breakInfo = BreakInfo(requestCtx, BreakType.Primary, + buildConfig.breakSettings, rotationConfig, hotbarConfig, + onBreak, onItemDrop + ) primaryBreakingInfo?.let { primaryInfo -> if (!primaryInfo.breakConfig.doubleBreak) return BreakType.Null if (primaryInfo.startedWithSecondary) return BreakType.Null if (!primaryInfo.breaking) { - secondaryBreakingInfo = BreakInfo( - requestCtx, - BreakType.Secondary, - onBreak, - buildConfig.breakSettings, - rotationConfig, - hotbarConfig - ) + secondaryBreakingInfo = breakInfo.apply { type = BreakType.Secondary } return BreakType.Secondary } else { primaryInfo.type = BreakType.Secondary secondaryBreakingInfo = primaryInfo - primaryBreakingInfo = BreakInfo( - requestCtx, - BreakType.Primary, - onBreak, - buildConfig.breakSettings, - rotationConfig, - hotbarConfig - ) + primaryBreakingInfo = breakInfo + setPendingInteractionsLimits(buildConfig) return BreakType.Primary } } ?: run { - primaryBreakingInfo = BreakInfo( - requestCtx, - BreakType.Primary, - onBreak, - buildConfig.breakSettings, - rotationConfig, - hotbarConfig - ) - pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) - pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) + primaryBreakingInfo = breakInfo + setPendingInteractionsLimits(buildConfig) return BreakType.Primary } } + private fun setPendingInteractionsLimits(buildConfig: BuildConfig) { + pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) + pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) + } + private fun SafeContext.canAccept(ctx: BreakContext) = pendingInteractions.none { it.context.expectedPos == ctx.expectedPos } && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } @@ -417,10 +425,11 @@ object BreakManager : RequestHandler() { data class BreakInfo( val context: BreakContext, var type: BreakType, - val onBreak: () -> Unit, val breakConfig: BreakConfig, val rotationConfig: RotationConfig, - val hotbarConfig: HotbarConfig + val hotbarConfig: HotbarConfig, + val onBreak: () -> Unit, + val onItemDrop: (ItemEntity) -> Unit ) { var breaking = false var breakingTicks = 0 diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 1896964fc..28d70a587 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -25,6 +25,7 @@ import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState +import net.minecraft.entity.ItemEntity data class BreakRequest( val contexts: List, @@ -32,7 +33,8 @@ data class BreakRequest( val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, val prio: Priority = 0, - val onBreak: () -> Unit + val onBreak: () -> Unit, + val onItemDrop: (ItemEntity) -> Unit, ) : Request(prio) { override val done: Boolean get() = runSafe { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 49a05c3f4..9aedc26fa 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -46,7 +46,6 @@ import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.info -import com.lambda.util.Communication.warn import com.lambda.util.Formatting.string import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure @@ -54,7 +53,6 @@ import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.SlotUtils.hotbarAndStorage import net.minecraft.entity.ItemEntity -import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos class BuildTask @Ta5kBuilder constructor( @@ -72,7 +70,6 @@ class BuildTask @Ta5kBuilder constructor( private val pendingInteractions = LimitedDecayQueue( build.maxPendingInteractions, build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } - private var currentInteraction: BuildContext? = null private var placements = 0 private var breaks = 0 @@ -110,7 +107,12 @@ class BuildTask @Ta5kBuilder constructor( .take(build.breakSettings.breaksPerTick) instantResults.firstOrNull()?.let { - build.breakSettings.request(BreakRequest(instantResults.map { it.context }, build, rotation, hotbar) { breaks++ }) + build.breakSettings.request( + BreakRequest( + instantResults.map { it.context }, build, rotation, hotbar, + onBreak = { breaks++ } + ) { item -> if (collectDrops) dropsToCollect.add(item) } + ) return@listen } } @@ -149,7 +151,10 @@ class BuildTask @Ta5kBuilder constructor( if (pendingInteractions.size >= build.maxPendingInteractions) return@listen val breakContexts = resultsNotBlocked.filterIsInstance().map { it.context } if (breakContexts.isNotEmpty()) { - val request = BreakRequest(breakContexts, build, rotation, hotbar) { breaks++ } + val request = BreakRequest( + breakContexts, build, rotation, hotbar, + onBreak = { breaks++ }, + ) { item -> if (collectDrops) dropsToCollect.add(item) } build.breakSettings.request(request) return@listen } From 6a61c5cdbcfd2cf03b148f1641c7b00168ec391b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 7 Mar 2025 02:02:20 +0000 Subject: [PATCH 040/364] call onPlace callback --- .../com/lambda/interaction/request/placing/PlaceManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 0bc455682..4c2e7872b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -91,6 +91,7 @@ object PlaceManager : RequestHandler() { } else { warn("Internal interaction failed with $actionResult") } + request.onPlace() } override fun preEvent() = UpdateManagerEvent.Place.Pre().post() From 21705b1d16599e09007724677049058b5c4d9ab2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 8 Mar 2025 03:48:10 +0000 Subject: [PATCH 041/364] - more placement customisation settings - account for max pending breaks in break manager - split pending interactions to breaks and placements --- .../com/lambda/config/groups/BreakSettings.kt | 5 +- .../com/lambda/config/groups/BuildConfig.kt | 6 + .../com/lambda/config/groups/PlaceSettings.kt | 4 + .../request/breaking/BreakConfig.kt | 10 +- .../request/breaking/BreakManager.kt | 20 +-- .../request/placing/PlaceConfig.kt | 5 + .../request/placing/PlaceManager.kt | 153 ++++++++++++++++-- .../com/lambda/util/player/PlayerUtils.kt | 9 ++ .../src/main/resources/lambda.accesswidener | 4 + 9 files changed, 184 insertions(+), 32 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 015d4861a..44bd6bae7 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -32,13 +32,14 @@ class BreakSettings( override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } - override val swingType by c.setting("Swing Type", SwingType.Vanilla, "The style of swing") { vis() } - override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() } + override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() } + override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() } override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } + override val maxPendingBreaks by c.setting("Max Pending Breaks", 2, 0..5, 1, "The maximum amount of pending breaks") { vis() } override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 54f5f9525..74d857371 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -30,4 +30,10 @@ interface BuildConfig { // Placing val placeSettings: PlaceSettings + + enum class SwingType { + Vanilla, + Server, + Client + } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index b7916c788..15a3282a9 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -28,5 +28,9 @@ class PlaceSettings( ) : PlaceConfig(priority) { override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val placeConfirmation by c.setting("Place Confirmation", PlaceConfirmation.PlaceThenAwait, "Wait for block placement confirmation") { vis() } + override val maxPendingPlacements by c.setting("Max Pending Placements", 2, 0..5, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } + override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() } + override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() } + override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 1f9ec0a37..e5f0a0e98 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request.breaking +import com.lambda.config.groups.BuildConfig import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig import net.minecraft.block.Block @@ -29,12 +30,13 @@ abstract class BreakConfig( abstract val doubleBreak: Boolean abstract val breakDelay: Int abstract val swing: SwingMode - abstract val swingType: SwingType + abstract val swingType: BuildConfig.SwingType abstract val sounds: Boolean abstract val particles: Boolean abstract val breakingTexture: Boolean abstract val rotateForBreak: Boolean abstract val breakConfirmation: BreakConfirmationMode + abstract val maxPendingBreaks: Int abstract val breaksPerTick: Int abstract val breakWeakBlocks: Boolean abstract val forceSilkTouch: Boolean @@ -58,12 +60,6 @@ abstract class BreakConfig( End } - enum class SwingType { - Vanilla, - Server, - Client - } - enum class BreakConfirmationMode { None, BreakThenAwait, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 24cbbea5a..c785a8694 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -41,7 +41,7 @@ import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.item.ItemUtils.block -import com.lambda.util.player.swingHandClient +import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance @@ -50,7 +50,6 @@ import net.minecraft.client.world.ClientWorld import net.minecraft.entity.ItemEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack -import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory import net.minecraft.util.math.BlockPos @@ -87,8 +86,11 @@ object BreakManager : RequestHandler() { if (updateRequest { true }) { currentRequest?.let request@ { request -> var instaBreaks = 0 + val config = breakingInfos.firstOrNull()?.breakConfig ?: request.buildConfig.breakSettings + val takeCount = config.maxPendingBreaks - (breakingInfos.count { it != null } + pendingInteractions.size) request.contexts .sortedBy { it.instantBreak } + .take(takeCount) .forEach { requestCtx -> if (!canAccept(requestCtx)) return@forEach val breakType = with(request) { @@ -242,7 +244,7 @@ object BreakManager : RequestHandler() { info.nullify() return false } - if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info) + if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info.breakConfig.swingType) return true } @@ -293,10 +295,10 @@ object BreakManager : RequestHandler() { onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } - if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info) + if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType) setBreakCooldown(info.breakConfig.breakDelay) } else { - if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info) + if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType) } return true @@ -409,14 +411,6 @@ object BreakManager : RequestHandler() { ) } - private fun SafeContext.swingHand(info: BreakInfo) { - when (info.breakConfig.swingType) { - BreakConfig.SwingType.Vanilla -> player.swingHand(player.activeHand) - BreakConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(player.activeHand)) - BreakConfig.SwingType.Client -> swingHandClient(player.activeHand) - } - } - private fun isOnBreakCooldown() = blockBreakingCooldown > 0 private fun setBreakCooldown(cooldown: Int) { blockBreakingCooldown = cooldown diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 544d10882..3d4f745d7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request.placing +import com.lambda.config.groups.BuildConfig import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -25,7 +26,11 @@ abstract class PlaceConfig( ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean abstract val placeConfirmation: PlaceConfirmation + abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int + abstract val swing: Boolean + abstract val swingType: BuildConfig.SwingType + abstract val sounds: Boolean override fun requestInternal(request: PlaceRequest) { PlaceManager.registerRequest(this, request) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 4c2e7872b..ed1a64df1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -31,13 +31,30 @@ import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +import com.lambda.util.player.swingHand +import net.minecraft.block.BlockState +import net.minecraft.block.pattern.CachedBlockPosition +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemStack +import net.minecraft.item.ItemUsageContext +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket +import net.minecraft.registry.RegistryKeys +import net.minecraft.sound.SoundCategory +import net.minecraft.stat.Stats +import net.minecraft.util.ActionResult import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.world.GameMode +import net.minecraft.world.event.GameEvent +import org.apache.commons.lang3.mutable.MutableObject object PlaceManager : RequestHandler() { private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } + //ToDo: Add server response check for pending interactions init { listen(Int.MIN_VALUE) { preEvent() @@ -47,7 +64,11 @@ object PlaceManager : RequestHandler() { return@listen } - currentRequest?.let { request -> + currentRequest?.let request@ { request -> + if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) { + return@request + } + if (request.placeContext.sneak && !player.isSneaking || (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done) || (!request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)).done) @@ -76,24 +97,136 @@ object PlaceManager : RequestHandler() { } private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) { - val actionResult = interaction.interactBlock( - player, hand, request.placeContext.result - ) + val stackInHand = player.getStackInHand(hand) + val stackCountPre = stackInHand.count + val actionResult = interactBlock(request.buildConfig.placeSettings, hand, request.placeContext.result) if (actionResult.isAccepted) { - if (actionResult.shouldSwingHand() && request.interactionConfig.swingHand) { - player.swingHand(hand) - } + if (request.buildConfig.placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None) + pendingInteractions.add(request.placeContext) - if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { + if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) + swingHand(request.buildConfig.placeSettings.swingType) + + if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) - } } else { - warn("Internal interaction failed with $actionResult") + warn("Placement interaction failed with $actionResult") } request.onPlace() } + private fun SafeContext.interactBlock(placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult): ActionResult { + interaction.syncSelectedSlot() + if (!world.worldBorder.contains(hitResult.blockPos)) { + return ActionResult.FAIL + } else { + val mutableObject = MutableObject() + interaction.sendSequencedPacket(world) { sequence: Int -> + mutableObject.value = interactBlockInternal(placeConfig, hand, hitResult) + PlayerInteractBlockC2SPacket(hand, hitResult, sequence) + } + return mutableObject.value + } + } + + private fun SafeContext.interactBlockInternal( + placeConfig: PlaceConfig, + hand: Hand, + hitResult: BlockHitResult + ): ActionResult { + val itemStack = player.getStackInHand(hand) + if (interaction.currentGameMode == GameMode.SPECTATOR) return ActionResult.SUCCESS + + val handNotEmpty = !player.getStackInHand(hand).isEmpty + val cantInteract = player.shouldCancelInteraction() && handNotEmpty + if (!cantInteract) return ActionResult.PASS + + if (!itemStack.isEmpty && !player.itemCooldownManager.isCoolingDown(itemStack.item)) { + val itemUsageContext = ItemUsageContext(player, hand, hitResult) + val itemUseResult: ActionResult + if (interaction.currentGameMode.isCreative) { + val i = itemStack.count + itemUseResult = useOnBlock(placeConfig, itemStack, itemUsageContext) + itemStack.count = i + } else + itemUseResult = useOnBlock(placeConfig, itemStack, itemUsageContext) + + return itemUseResult + } + return ActionResult.PASS + } + + private fun SafeContext.useOnBlock( + placeConfig: PlaceConfig, + itemStack: ItemStack, + context: ItemUsageContext + ): ActionResult { + val blockPos = context.blockPos + val cachedBlockPosition = CachedBlockPosition(context.world, blockPos, false) + if (!player.abilities.allowModifyWorld + && !itemStack.canPlaceOn(context.world.registryManager.get(RegistryKeys.BLOCK), cachedBlockPosition) + ) { + return ActionResult.PASS + } + val item: BlockItem = (itemStack.item as? BlockItem) ?: return ActionResult.PASS + val actionResult = useOnBlock(placeConfig, item, context) + if (actionResult.shouldIncrementStat()) player.incrementStat(Stats.USED.getOrCreateStat(item)) + + return actionResult + } + + private fun SafeContext.useOnBlock( + placeConfig: PlaceConfig, + item: BlockItem, + context: ItemUsageContext + ) = place(placeConfig, item, ItemPlacementContext(context)) + + private fun SafeContext.place( + placeConfig: PlaceConfig, + item: BlockItem, + context: ItemPlacementContext + ): ActionResult { + if (!item.block.isEnabled(world.enabledFeatures)) return ActionResult.FAIL + if (!context.canPlace()) return ActionResult.FAIL + + val itemPlacementContext: ItemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL + val blockState: BlockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL + + if (placeConfig.placeConfirmation == PlaceConfig.PlaceConfirmation.AwaitThenPlace) + return ActionResult.success(world.isClient) + + //ToDo: Add restriction checks, like world height, to avoid needlessly awaiting a server response which will never return + // if the user has the AwaitThenPlace confirmation setting enabled, as none of the state-setting methods which check these rules + // are called + if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL + + val blockPos = itemPlacementContext.blockPos + val itemStack = itemPlacementContext.stack + var hitState = world.getBlockState(blockPos) + if (hitState.isOf(blockState.block)) { + hitState = item.placeFromNbt(blockPos, world, itemStack, hitState) + item.postPlacement(blockPos, world, player, itemStack, hitState) + hitState.block.onPlaced(world, blockPos, hitState, player, itemStack) + } + + if (placeConfig.sounds) { + val blockSoundGroup = hitState.soundGroup + world.playSound( + player, + blockPos, + item.getPlaceSound(hitState), + SoundCategory.BLOCKS, + (blockSoundGroup.getVolume() + 1.0f) / 2.0f, + blockSoundGroup.getPitch() * 0.8f + ) + } + world.emitGameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Emitter.of(player, hitState)) + if (!player.abilities.creativeMode) itemStack.decrement(1) + + return ActionResult.success(world.isClient) + } + override fun preEvent() = UpdateManagerEvent.Place.Pre().post() override fun postEvent() = UpdateManagerEvent.Place.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index 58de52bc5..c972b5eb7 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -1,11 +1,13 @@ package com.lambda.util.player +import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.mojang.authlib.GameProfile import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry import net.minecraft.entity.player.PlayerEntity +import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket import net.minecraft.util.Hand fun SafeContext.copyPlayer(entity: ClientPlayerEntity) = @@ -41,6 +43,13 @@ fun SafeContext.spawnFakePlayer( return entity } +fun SafeContext.swingHand(swingType: BuildConfig.SwingType) = + when (swingType) { + BuildConfig.SwingType.Vanilla -> player.swingHand(player.activeHand) + BuildConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(player.activeHand)) + BuildConfig.SwingType.Client -> swingHandClient(player.activeHand) + } + fun SafeContext.swingHandClient(hand: Hand) { if (!player.handSwinging || player.handSwingTicks >= player.handSwingDuration / 2 || player.handSwingTicks < 0) { player.handSwingTicks = -1 diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index c6bdaf956..b2e6dbf3a 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -16,6 +16,10 @@ accessible field net/minecraft/client/world/ClientChunkManager$ClientChunkMap ch accessible field net/minecraft/client/network/ClientPlayerInteractionManager currentBreakingProgress F accessible field net/minecraft/client/network/ClientPlayerInteractionManager blockBreakingCooldown I accessible field net/minecraft/client/network/ClientPlayerInteractionManager currentBreakingPos Lnet/minecraft/util/math/BlockPos; +accessible method net/minecraft/item/BlockItem place (Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z +accessible method net/minecraft/item/BlockItem placeFromNbt (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState; +accessible method net/minecraft/item/BlockItem postPlacement (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Z +accessible method net/minecraft/item/BlockItem getPlaceSound (Lnet/minecraft/block/BlockState;)Lnet/minecraft/sound/SoundEvent; # Entity accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; From 216b0316b04178114436959dd5ea7c0a3dac7458 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 8 Mar 2025 17:07:00 +0000 Subject: [PATCH 042/364] place manager checks server block updates for pending placement confirmations --- .../com/lambda/config/groups/BuildSettings.kt | 2 +- .../com/lambda/config/groups/PlaceSettings.kt | 2 +- .../request/placing/PlaceConfig.kt | 4 +- .../request/placing/PlaceManager.kt | 121 +++++++++++------- 4 files changed, 81 insertions(+), 48 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 2453d1f92..53751b74b 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -43,5 +43,5 @@ class BuildSettings( // Placing override val placeSettings = PlaceSettings(c) { page == Page.Place && vis() } - override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) } + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeSettings.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 15a3282a9..bbd189a97 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -27,7 +27,7 @@ class PlaceSettings( vis: () -> Boolean = { true } ) : PlaceConfig(priority) { override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } - override val placeConfirmation by c.setting("Place Confirmation", PlaceConfirmation.PlaceThenAwait, "Wait for block placement confirmation") { vis() } + override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 2, 0..5, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 3d4f745d7..73bf6c305 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -25,7 +25,7 @@ abstract class PlaceConfig( priority: Priority ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean - abstract val placeConfirmation: PlaceConfirmation + abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int abstract val swing: Boolean @@ -36,7 +36,7 @@ abstract class PlaceConfig( PlaceManager.registerRequest(this, request) } - enum class PlaceConfirmation { + enum class PlaceConfirmationMode { None, PlaceThenAwait, AwaitThenPlace diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index ed1a64df1..c8f3dbf0a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -17,13 +17,16 @@ package com.lambda.interaction.request.placing +import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent +import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate @@ -41,20 +44,18 @@ import net.minecraft.item.ItemUsageContext import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket import net.minecraft.registry.RegistryKeys import net.minecraft.sound.SoundCategory -import net.minecraft.stat.Stats import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos import net.minecraft.world.GameMode -import net.minecraft.world.event.GameEvent import org.apache.commons.lang3.mutable.MutableObject object PlaceManager : RequestHandler() { - private val pendingInteractions = LimitedDecayQueue( + private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } + ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } - //ToDo: Add server response check for pending interactions init { listen(Int.MIN_VALUE) { preEvent() @@ -65,9 +66,8 @@ object PlaceManager : RequestHandler() { } currentRequest?.let request@ { request -> - if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) { + if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) return@request - } if (request.placeContext.sneak && !player.isSneaking || (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done) @@ -79,15 +79,28 @@ object PlaceManager : RequestHandler() { pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L) placeBlock(request, Hand.MAIN_HAND) - } + } postEvent() } + listen { event -> + pendingInteractions + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { pending -> + pendingInteractions.remove(pending) + if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) return@listen + if (pending.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) + placeSound(pending.item, pending.context.expectedState, pending.context.expectedPos) + pending.onPlace() + } + } + onRotate { currentRequest?.let { request -> - if (request.buildConfig.placeSettings.rotateForPlace) + if (request.buildConfig.placeSettings.rotateForPlace) { request.rotationConfig.request(request.placeContext.rotation) + } } } @@ -96,24 +109,43 @@ object PlaceManager : RequestHandler() { } } + private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = + if (targetState.matches(newState, pos, world)) true + else { + this@PlaceManager.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState") + false + } + private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) { val stackInHand = player.getStackInHand(hand) + val item = stackInHand.item as? BlockItem ?: return val stackCountPre = stackInHand.count val actionResult = interactBlock(request.buildConfig.placeSettings, hand, request.placeContext.result) if (actionResult.isAccepted) { - if (request.buildConfig.placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None) - pendingInteractions.add(request.placeContext) + if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) + request.onPlace() + else { + pendingInteractions.add( + PlaceInfo( + request.placeContext, + item, + request.buildConfig, + request.onPlace + ) + ) + } - if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) + if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) { swingHand(request.buildConfig.placeSettings.swingType) + } - if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) + if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + } } else { warn("Placement interaction failed with $actionResult") } - request.onPlace() } private fun SafeContext.interactBlock(placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult): ActionResult { @@ -121,12 +153,12 @@ object PlaceManager : RequestHandler() { if (!world.worldBorder.contains(hitResult.blockPos)) { return ActionResult.FAIL } else { - val mutableObject = MutableObject() + val mutableActionResult = MutableObject() interaction.sendSequencedPacket(world) { sequence: Int -> - mutableObject.value = interactBlockInternal(placeConfig, hand, hitResult) + mutableActionResult.value = interactBlockInternal(placeConfig, hand, hitResult) PlayerInteractBlockC2SPacket(hand, hitResult, sequence) } - return mutableObject.value + return mutableActionResult.value } } @@ -169,19 +201,12 @@ object PlaceManager : RequestHandler() { ) { return ActionResult.PASS } - val item: BlockItem = (itemStack.item as? BlockItem) ?: return ActionResult.PASS - val actionResult = useOnBlock(placeConfig, item, context) - if (actionResult.shouldIncrementStat()) player.incrementStat(Stats.USED.getOrCreateStat(item)) + val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS + val actionResult = place(placeConfig, item, ItemPlacementContext(context)) return actionResult } - private fun SafeContext.useOnBlock( - placeConfig: PlaceConfig, - item: BlockItem, - context: ItemUsageContext - ) = place(placeConfig, item, ItemPlacementContext(context)) - private fun SafeContext.place( placeConfig: PlaceConfig, item: BlockItem, @@ -190,15 +215,15 @@ object PlaceManager : RequestHandler() { if (!item.block.isEnabled(world.enabledFeatures)) return ActionResult.FAIL if (!context.canPlace()) return ActionResult.FAIL - val itemPlacementContext: ItemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL - val blockState: BlockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL + val itemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL + val blockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL - if (placeConfig.placeConfirmation == PlaceConfig.PlaceConfirmation.AwaitThenPlace) + if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) return ActionResult.success(world.isClient) - //ToDo: Add restriction checks, like world height, to avoid needlessly awaiting a server response which will never return - // if the user has the AwaitThenPlace confirmation setting enabled, as none of the state-setting methods which check these rules - // are called + // TODO: Implement restriction checks (e.g., world height) to prevent unnecessary server requests when the + // "AwaitThenPlace" confirmation setting is enabled, as the block state setting methods that validate these + // rules are not called. if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL val blockPos = itemPlacementContext.blockPos @@ -210,23 +235,31 @@ object PlaceManager : RequestHandler() { hitState.block.onPlaced(world, blockPos, hitState, player, itemStack) } - if (placeConfig.sounds) { - val blockSoundGroup = hitState.soundGroup - world.playSound( - player, - blockPos, - item.getPlaceSound(hitState), - SoundCategory.BLOCKS, - (blockSoundGroup.getVolume() + 1.0f) / 2.0f, - blockSoundGroup.getPitch() * 0.8f - ) - } - world.emitGameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Emitter.of(player, hitState)) + if (placeConfig.sounds) placeSound(item, hitState, blockPos) if (!player.abilities.creativeMode) itemStack.decrement(1) return ActionResult.success(world.isClient) } + private fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) { + val blockSoundGroup = state.soundGroup + world.playSound( + player, + pos, + item.getPlaceSound(state), + SoundCategory.BLOCKS, + (blockSoundGroup.getVolume() + 1.0f) / 2.0f, + blockSoundGroup.getPitch() * 0.8f + ) + } + + data class PlaceInfo( + val context: PlaceContext, + val item: BlockItem, + val buildConfig: BuildConfig, + val onPlace: () -> Unit + ) + override fun preEvent() = UpdateManagerEvent.Place.Pre().post() override fun postEvent() = UpdateManagerEvent.Place.Post().post() } \ No newline at end of file From bf39de5e55242ea4e66f5fba68ca8d70ba0e041a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 8 Mar 2025 18:55:14 +0000 Subject: [PATCH 043/364] activity checks --- .../interaction/request/RequestHandler.kt | 15 ++++++++ .../request/breaking/BreakManager.kt | 37 +++++++++++++------ .../request/hotbar/HotbarManager.kt | 4 +- .../request/placing/PlaceManager.kt | 10 ++++- .../request/rotation/RotationManager.kt | 2 + 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 5394e551e..7515a96bb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -18,6 +18,8 @@ package com.lambda.interaction.request import com.lambda.event.Event +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen import java.util.concurrent.ConcurrentHashMap /** @@ -29,11 +31,24 @@ abstract class RequestHandler { private val requestMap = ConcurrentHashMap, R>() + /** + * Represents if the handler performed any external actions within this tick + */ + protected var activeThisTick = false + /** * The currently active request. */ var currentRequest: R? = null; protected set + init { + listen(Int.MIN_VALUE) { + activeThisTick = false + } + } + + fun activeThisTick() = activeThisTick + /** * Registers a new request with the given configuration. * diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index c785a8694..7bc132939 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -31,6 +31,7 @@ import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule @@ -80,6 +81,12 @@ object BreakManager : RequestHandler() { return@listen } + if (PlaceManager.activeThisTick()) { + updateRequest(true) { true } + postEvent() + return@listen + } + var swapped = false //ToDo: improve instamine / non instamine integration @@ -106,25 +113,33 @@ object BreakManager : RequestHandler() { request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)) swapped = true } - updateBlockBreakingProgress(info, player.mainHandStack) - instaBreaks++ + if (updateBlockBreakingProgress(info, player.mainHandStack)) { + instaBreaks++ + activeThisTick = true + } } } } } } - breakingInfos.firstOrNull()?.let { info -> - if ((!swapped && !info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done) - || (info.breakConfig.rotateForBreak && !info.context.rotation.done)) { - postEvent() - return@listen + breakingInfos + .filterNotNull() + .firstOrNull()?.let { info -> + activeThisTick = true + if ((!swapped && !info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done) + || (info.breakConfig.rotateForBreak && !info.context.rotation.done)) { + postEvent() + return@listen + } } - } - breakingInfos.reversed().filterNotNull().forEach { info -> - updateBlockBreakingProgress(info, player.mainHandStack) - } + breakingInfos + .filterNotNull() + .reversed() + .forEach { info -> + updateBlockBreakingProgress(info, player.mainHandStack) + } postEvent() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 11b1343f9..d1c57be1f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -48,8 +48,8 @@ object HotbarManager : RequestHandler(), Loadable { listen { preEvent() - updateRequest() - interaction.syncSelectedSlot() + if (updateRequest()) interaction.syncSelectedSlot() + if (currentRequest != null) activeThisTick = true postEvent() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index c8f3dbf0a..760663a14 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -28,6 +28,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule @@ -65,10 +66,17 @@ object PlaceManager : RequestHandler() { return@listen } + if (BreakManager.activeThisTick()) { + postEvent() + return@listen + } + currentRequest?.let request@ { request -> if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) return@request + activeThisTick = true + if (request.placeContext.sneak && !player.isSneaking || (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done) || (!request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)).done) @@ -79,7 +87,7 @@ object PlaceManager : RequestHandler() { pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L) placeBlock(request, Hand.MAIN_HAND) - } + } postEvent() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index d838f3e00..ea6a420f3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -77,6 +77,8 @@ object RotationManager : RequestHandler(), Loadable { entry.value.target.targetRotation.value != null } + if (currentRequest != null) activeThisTick = true + if (!changed) { // rebuild the rotation if the same context gets used again currentRequest?.target?.targetRotation?.update() } From b4ccee846ed03b654db503cddef283f6e4a0afe7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 10 Mar 2025 13:09:32 +0000 Subject: [PATCH 044/364] fixed placements and removed interaction checks --- .../container/containers/MainHandContainer.kt | 2 +- .../interaction/request/placing/PlaceManager.kt | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt index ca0569ba3..da830cb09 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt @@ -43,7 +43,7 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { override val name: String get() = "Depositing [$selection] to ${hand.name.lowercase().replace("_", " ")}" override fun SafeContext.onStart() { - val moveStack = matchingStacks(selection).firstOrNull() ?: run { + val moveStack = InventoryContainer.matchingStacks(selection).firstOrNull() ?: run { failure("No matching stacks found in inventory") return } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 760663a14..cb647c5bb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -61,7 +61,9 @@ object PlaceManager : RequestHandler() { listen(Int.MIN_VALUE) { preEvent() - if (!updateRequest { true }) { + if (!updateRequest { request -> + pendingInteractions.none { pending -> pending.context.expectedPos == request.value.placeContext.expectedPos } + }) { postEvent() return@listen } @@ -178,9 +180,11 @@ object PlaceManager : RequestHandler() { val itemStack = player.getStackInHand(hand) if (interaction.currentGameMode == GameMode.SPECTATOR) return ActionResult.SUCCESS - val handNotEmpty = !player.getStackInHand(hand).isEmpty - val cantInteract = player.shouldCancelInteraction() && handNotEmpty - if (!cantInteract) return ActionResult.PASS + // checks if the player should be able to interact with the block for if its something + // like a furnace or chest where an action would happen +// val handNotEmpty = player.getStackInHand(hand).isEmpty.not() +// val cantInteract = player.shouldCancelInteraction() && handNotEmpty +// if (!cantInteract) return ActionResult.PASS if (!itemStack.isEmpty && !player.itemCooldownManager.isCoolingDown(itemStack.item)) { val itemUsageContext = ItemUsageContext(player, hand, hitResult) From c39f2c29643f5f42030f8b79cc45608261d33852 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 10 Mar 2025 15:47:28 +0000 Subject: [PATCH 045/364] slightly improved the hotbar swapping in the break manager --- .../interaction/request/breaking/BreakManager.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 7bc132939..a12c457b0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -87,8 +87,6 @@ object BreakManager : RequestHandler() { return@listen } - var swapped = false - //ToDo: improve instamine / non instamine integration if (updateRequest { true }) { currentRequest?.let request@ { request -> @@ -109,10 +107,8 @@ object BreakManager : RequestHandler() { if (breakType == BreakType.Null) return@request if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { breakingInfos.getOrNull(breakType.index)?.let { info -> - if (!swapped) { - request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)) - swapped = true - } + if (request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) + return@forEach if (updateBlockBreakingProgress(info, player.mainHandStack)) { instaBreaks++ activeThisTick = true @@ -127,8 +123,7 @@ object BreakManager : RequestHandler() { .filterNotNull() .firstOrNull()?.let { info -> activeThisTick = true - if ((!swapped && !info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done) - || (info.breakConfig.rotateForBreak && !info.context.rotation.done)) { + if (info.breakConfig.rotateForBreak && !info.context.rotation.done) { postEvent() return@listen } @@ -138,6 +133,7 @@ object BreakManager : RequestHandler() { .filterNotNull() .reversed() .forEach { info -> + if (info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) return@forEach updateBlockBreakingProgress(info, player.mainHandStack) } @@ -152,6 +148,7 @@ object BreakManager : RequestHandler() { } } + //ToDo: Clean this up listen { event -> pendingInteractions .firstOrNull { it.context.expectedPos == event.pos } From 8216d00eb83f2854946533d8bcf95780e1ca1663 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 11 Mar 2025 02:56:14 +0100 Subject: [PATCH 046/364] Units for HWT --- .../com/lambda/module/modules/player/HighwayTools.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 5979ca874..16d7de44d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -48,10 +48,10 @@ object HighwayTools : Module( ) { private val page by setting("Page", Page.Structure) - private val height by setting("Height", 4, 2..10, 1) { page == Page.Structure } - private val width by setting("Width", 6, 1..30, 1) { page == Page.Structure } + private val height by setting("Height", 4, 2..10, 1, "Height of the full tunnel tube including the pavement", " blocks") { page == Page.Structure } + private val width by setting("Width", 6, 1..30, 1, "Width of the full tunnel tube including the pavements rims", " blocks") { page == Page.Structure } private val pavement by setting("Pavement", Material.Block, "Material for the pavement") { page == Page.Structure } - private val rimHeight by setting("Pavement Rim Height", 1, 0..6, 1) { page == Page.Structure && pavement != Material.None } + private val rimHeight by setting("Pavement Rim Height", 1, 0..6, 1, "Height of the pavements rims where 0 is none", " blocks") { page == Page.Structure && pavement != Material.None } private val cornerBlock by setting("Corner", Corner.None, "Include corner blocks in the highway") { page == Page.Structure && pavement != Material.None } private val pavementMaterial by setting("Pavement Material", Blocks.OBSIDIAN, "Material to build the highway with") { page == Page.Structure && pavement == Material.Block } private val floor by setting("Floor", Material.None, "Material for the floor") { page == Page.Structure } @@ -60,8 +60,8 @@ object HighwayTools : Module( private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, "Material to build the walls with") { page == Page.Structure && walls == Material.Block } private val ceiling by setting("Ceiling", Material.None, "Material for the ceiling") { page == Page.Structure } private val ceilingMaterial by setting("Ceiling Material", Blocks.OBSIDIAN, "Material to build the ceiling with") { page == Page.Structure && ceiling == Material.Block } - private val distance by setting("Distance", -1, -1..1000000, 1, "Distance to build the highway/tunnel (negative for infinite)") { page == Page.Structure } - private val sliceSize by setting("Slice Size", 3, 1..5, 1, "Number of slices to build at once") { page == Page.Structure } + private val distance by setting("Distance", -1, -1..1000000, 1, "Distance to build the highway/tunnel (negative for infinite)", " blocks") { page == Page.Structure } + private val sliceSize by setting("Slice Size", 3, 1..5, 1, "Number of slices to build at once", " blocks") { page == Page.Structure } private val build = BuildSettings(this) { page == Page.Build } private val rotation = RotationSettings(this) { page == Page.Rotation } From 9ef88f02b2de319bf549da9b3cfb2184fa37f062 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 11 Mar 2025 03:55:11 +0100 Subject: [PATCH 047/364] Dont use support if rim is 0 --- .../kotlin/com/lambda/module/modules/player/HighwayTools.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 16d7de44d..62aca7172 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -167,7 +167,7 @@ object HighwayTools : Module( 1, ).associateWith { target(pavement, pavementMaterial) } - if (cornerBlock == Corner.None) { + if (cornerBlock == Corner.None && rimHeight > 0) { // Support for the left rim structure += generateDirectionalTube( orthogonal, From b3d4cd6fc25f7bcb16dbbf72082fd0fe237784d4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Mar 2025 00:03:28 +0000 Subject: [PATCH 048/364] fixed place and break delays with tick timing issues --- .../request/breaking/BreakManager.kt | 36 +++--- .../request/hotbar/HotbarManager.kt | 2 +- .../request/placing/PlaceManager.kt | 105 +++++++++--------- .../request/rotation/RotationManager.kt | 18 ++- 4 files changed, 90 insertions(+), 71 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index a12c457b0..fe9c1c452 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -20,7 +20,6 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post -import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -34,6 +33,8 @@ import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationManager.onRotatePost +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta @@ -70,21 +71,23 @@ object BreakManager : RequestHandler() { private var blockBreakingCooldown = 0 + private var rotation: RotationRequest? = null + init { - listen(Int.MIN_VALUE) { + onRotate(priority = Int.MIN_VALUE) { preEvent() if (isOnBreakCooldown()) { blockBreakingCooldown-- updateRequest(true) { true } postEvent() - return@listen + return@onRotate } if (PlaceManager.activeThisTick()) { updateRequest(true) { true } postEvent() - return@listen + return@onRotate } //ToDo: improve instamine / non instamine integration @@ -121,16 +124,25 @@ object BreakManager : RequestHandler() { breakingInfos .filterNotNull() + .firstOrNull { it.breakConfig.rotateForBreak } + ?.let { info -> + rotation = info.rotationConfig.request(info.context.rotation) + } + } + + onRotatePost { + val notNullInfos = breakingInfos.filterNotNull() + + notNullInfos .firstOrNull()?.let { info -> activeThisTick = true - if (info.breakConfig.rotateForBreak && !info.context.rotation.done) { + if (info.breakConfig.rotateForBreak && rotation?.done != true) { postEvent() - return@listen + return@onRotatePost } } - breakingInfos - .filterNotNull() + notNullInfos .reversed() .forEach { info -> if (info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) return@forEach @@ -140,14 +152,6 @@ object BreakManager : RequestHandler() { postEvent() } - onRotate { - breakingInfos - .filterNotNull() - .firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> - info.rotationConfig.request(info.context.rotation) - } - } - //ToDo: Clean this up listen { event -> pendingInteractions diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index d1c57be1f..48dd51fe5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -46,7 +46,7 @@ object HotbarManager : RequestHandler(), Loadable { it.slot = currentRequest?.slot ?: return@listen } - listen { + listen(priority = Int.MIN_VALUE) { preEvent() if (updateRequest()) interaction.syncSelectedSlot() if (currentRequest != null) activeThisTick = true diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index cb647c5bb..59f798685 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -21,7 +21,6 @@ import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.MovementEvent -import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -31,6 +30,8 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationManager.onRotatePost +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.Communication.info import com.lambda.util.Communication.warn @@ -57,41 +58,56 @@ object PlaceManager : RequestHandler() { TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + private var rotation: RotationRequest? = null + init { - listen(Int.MIN_VALUE) { + onRotate(priority = Int.MIN_VALUE) { preEvent() - if (!updateRequest { request -> - pendingInteractions.none { pending -> pending.context.expectedPos == request.value.placeContext.expectedPos } - }) { + if (!updateRequest { request -> canPlace(request.value.placeContext) }) { postEvent() - return@listen + return@onRotate } if (BreakManager.activeThisTick()) { postEvent() - return@listen + return@onRotate } currentRequest?.let request@ { request -> - if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) - return@request + if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) { + postEvent() + return@onRotate + } activeThisTick = true - if (request.placeContext.sneak && !player.isSneaking - || (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done) - || (!request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)).done) - ) { - postEvent() - return@listen + if (request.buildConfig.placeSettings.rotateForPlace) { + rotation = request.rotationConfig.request(request.placeContext.rotation) } + pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L) + } + } + + onRotatePost { + currentRequest?.let { request -> + val notSneaking = !player.isSneaking + val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)) + val invalidRotation = request.buildConfig.placeSettings.rotateForPlace && rotation?.done != true + if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || invalidRotation) { + postEvent() + return@onRotatePost + } + placeBlock(request, Hand.MAIN_HAND) + postEvent() } + } - postEvent() + listen { + if (currentRequest?.placeContext?.sneak == true) it.input.sneaking = true } listen { event -> @@ -105,20 +121,13 @@ object PlaceManager : RequestHandler() { pending.onPlace() } } + } - onRotate { - currentRequest?.let { request -> - if (request.buildConfig.placeSettings.rotateForPlace) { - request.rotationConfig.request(request.placeContext.rotation) - } - } + private fun canPlace(placeContext: PlaceContext) = + pendingInteractions.none { pending -> + pending.context.expectedPos == placeContext.expectedPos } - listen { - if (currentRequest?.placeContext?.sneak == true) it.input.sneaking = true - } - } - private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = if (targetState.matches(newState, pos, world)) true else { @@ -160,16 +169,14 @@ object PlaceManager : RequestHandler() { private fun SafeContext.interactBlock(placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult): ActionResult { interaction.syncSelectedSlot() - if (!world.worldBorder.contains(hitResult.blockPos)) { - return ActionResult.FAIL - } else { - val mutableActionResult = MutableObject() - interaction.sendSequencedPacket(world) { sequence: Int -> - mutableActionResult.value = interactBlockInternal(placeConfig, hand, hitResult) - PlayerInteractBlockC2SPacket(hand, hitResult, sequence) - } - return mutableActionResult.value + if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL + + val mutableActionResult = MutableObject() + interaction.sendSequencedPacket(world) { sequence: Int -> + mutableActionResult.value = interactBlockInternal(placeConfig, hand, hitResult) + PlayerInteractBlockC2SPacket(hand, hitResult, sequence) } + return mutableActionResult.value } private fun SafeContext.interactBlockInternal( @@ -188,15 +195,14 @@ object PlaceManager : RequestHandler() { if (!itemStack.isEmpty && !player.itemCooldownManager.isCoolingDown(itemStack.item)) { val itemUsageContext = ItemUsageContext(player, hand, hitResult) - val itemUseResult: ActionResult - if (interaction.currentGameMode.isCreative) { + return if (interaction.currentGameMode.isCreative) { val i = itemStack.count - itemUseResult = useOnBlock(placeConfig, itemStack, itemUsageContext) - itemStack.count = i + useOnBlock(placeConfig, itemStack, itemUsageContext) + .also { + itemStack.count = i + } } else - itemUseResult = useOnBlock(placeConfig, itemStack, itemUsageContext) - - return itemUseResult + useOnBlock(placeConfig, itemStack, itemUsageContext) } return ActionResult.PASS } @@ -206,13 +212,12 @@ object PlaceManager : RequestHandler() { itemStack: ItemStack, context: ItemUsageContext ): ActionResult { - val blockPos = context.blockPos - val cachedBlockPosition = CachedBlockPosition(context.world, blockPos, false) - if (!player.abilities.allowModifyWorld - && !itemStack.canPlaceOn(context.world.registryManager.get(RegistryKeys.BLOCK), cachedBlockPosition) - ) { - return ActionResult.PASS - } + val cachedBlockPosition = CachedBlockPosition(context.world, context.blockPos, false) + + val cantModifyWorld = !player.abilities.allowModifyWorld + val cantPlaceOn = !itemStack.canPlaceOn(context.world.registryManager.get(RegistryKeys.BLOCK), cachedBlockPosition) + if (cantModifyWorld && cantPlaceOn) return ActionResult.PASS + val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS val actionResult = place(placeConfig, item, ItemPlacementContext(context)) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index ea6a420f3..f6cb70d77 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -24,6 +24,7 @@ import com.lambda.event.EventFlow.post import com.lambda.event.events.* import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe +import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotation.Rotation.Companion.slerp import com.lambda.interaction.request.rotation.visibilty.lookAt @@ -59,8 +60,17 @@ object RotationManager : RequestHandler(), Loadable { */ fun Any.onRotate( alwaysListen: Boolean = false, - block: SafeContext.() -> Unit, - ) = this.listen(0, alwaysListen) { + priority: Priority = 0, + block: SafeContext.() -> Unit + ) = this.listen(priority, alwaysListen) { + block() + } + + fun Any.onRotatePost( + alwaysListen: Boolean = false, + priority: Priority = 0, + block: SafeContext.() -> Unit + ) = this.listen(priority, alwaysListen) { block() } @@ -265,6 +275,6 @@ object RotationManager : RequestHandler(), Loadable { } } - override fun preEvent() = UpdateManagerEvent.Rotation.Pre() - override fun postEvent() = UpdateManagerEvent.Rotation.Post() + override fun preEvent() = UpdateManagerEvent.Rotation.Pre().post() + override fun postEvent() = UpdateManagerEvent.Rotation.Post().post() } \ No newline at end of file From bea07d32aaf3e1a3819413c58d8d626092b60bc2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Mar 2025 00:30:16 +0000 Subject: [PATCH 049/364] public blocked positions in place and break managers --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 5 ++++- .../com/lambda/interaction/request/placing/PlaceManager.kt | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index fe9c1c452..e58ee917d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -69,6 +69,9 @@ object BreakManager : RequestHandler() { TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + val blockedPositions + get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingInteractions.map { it.context.expectedPos } + private var blockBreakingCooldown = 0 private var rotation: RotationRequest? = null @@ -175,7 +178,7 @@ object BreakManager : RequestHandler() { } //ToDo: drop callback stuff -// // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work + // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work // listen { // if (it.entity !is ItemEntity) return@listen // pendingInteractions diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 59f798685..029d87f65 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -60,6 +60,9 @@ object PlaceManager : RequestHandler() { private var rotation: RotationRequest? = null + val blockedPositions + get() = pendingInteractions.map { it.context.expectedPos } + init { onRotate(priority = Int.MIN_VALUE) { preEvent() From e214baed538b037929377b5cc1e0f50da8aa1091 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Mar 2025 01:32:20 +0000 Subject: [PATCH 050/364] split back to update on rotate and perform on tick --- .../request/breaking/BreakManager.kt | 31 ++++++++++--------- .../request/placing/PlaceManager.kt | 28 ++++++++++------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index e58ee917d..d0980fff2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post +import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -75,8 +76,21 @@ object BreakManager : RequestHandler() { private var blockBreakingCooldown = 0 private var rotation: RotationRequest? = null + private var validRotation = false init { + listen { + if (!validRotation) return@listen + + breakingInfos + .filterNotNull() + .reversed() + .forEach { info -> + if (info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) return@forEach + updateBlockBreakingProgress(info, player.mainHandStack) + } + } + onRotate(priority = Int.MIN_VALUE) { preEvent() @@ -134,22 +148,11 @@ object BreakManager : RequestHandler() { } onRotatePost { - val notNullInfos = breakingInfos.filterNotNull() - - notNullInfos + breakingInfos + .filterNotNull() .firstOrNull()?.let { info -> activeThisTick = true - if (info.breakConfig.rotateForBreak && rotation?.done != true) { - postEvent() - return@onRotatePost - } - } - - notNullInfos - .reversed() - .forEach { info -> - if (info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) return@forEach - updateBlockBreakingProgress(info, player.mainHandStack) + validRotation = info.breakConfig.rotateForBreak && rotation?.done == true } postEvent() diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 029d87f65..9d454a8ab 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -21,6 +21,7 @@ import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.MovementEvent +import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -59,11 +60,24 @@ object PlaceManager : RequestHandler() { ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } private var rotation: RotationRequest? = null + private var validRotation = false val blockedPositions get() = pendingInteractions.map { it.context.expectedPos } init { + listen { + currentRequest?.let { request -> + val notSneaking = !player.isSneaking + val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)) + val invalidRotation = request.buildConfig.placeSettings.rotateForPlace && !validRotation + if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || invalidRotation) + return@listen + + placeBlock(request, Hand.MAIN_HAND) + } + } + onRotate(priority = Int.MIN_VALUE) { preEvent() @@ -95,18 +109,8 @@ object PlaceManager : RequestHandler() { } onRotatePost { - currentRequest?.let { request -> - val notSneaking = !player.isSneaking - val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)) - val invalidRotation = request.buildConfig.placeSettings.rotateForPlace && rotation?.done != true - if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || invalidRotation) { - postEvent() - return@onRotatePost - } - - placeBlock(request, Hand.MAIN_HAND) - postEvent() - } + validRotation = rotation?.done == true + postEvent() } listen { From cc0493f76f00130f4df31280030694aefe9aaff4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 13 Mar 2025 02:49:07 +0000 Subject: [PATCH 051/364] break manager improvements and fixed something which im shocked didnt break everything earlier --- .../request/breaking/BreakManager.kt | 169 ++++++++++-------- 1 file changed, 97 insertions(+), 72 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d0980fff2..e437a3523 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -52,7 +52,6 @@ import net.minecraft.client.sound.SoundInstance import net.minecraft.client.world.ClientWorld import net.minecraft.entity.ItemEntity import net.minecraft.entity.player.PlayerEntity -import net.minecraft.item.ItemStack import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory import net.minecraft.util.math.BlockPos @@ -75,85 +74,97 @@ object BreakManager : RequestHandler() { private var blockBreakingCooldown = 0 + private var instantBreaks = listOf() + private var rotation: RotationRequest? = null private var validRotation = false init { - listen { + listen(Int.MIN_VALUE) { + if (isOnBreakCooldown()) { + blockBreakingCooldown-- + return@listen + } + if (PlaceManager.activeThisTick()) return@listen + + currentRequest?.let request@ { request -> + if (instantBreaks.isEmpty()) return@request + + instantBreaks.forEach { ctx -> + val breakInfo = with(request) { + handleRequestContext( + ctx, + buildConfig, rotationConfig, hotbarConfig, + onBreak, onItemDrop + ) + } ?: return@request + if (!breakInfo.requestHotbarSwap()) return@forEach + updateBlockBreakingProgress(breakInfo) + activeThisTick = true + } + instantBreaks = emptyList() + } + if (!validRotation) return@listen + // Reversed so that the breaking order feels natural to the user as the primary break has to + // be started after the secondary breakingInfos .filterNotNull() .reversed() .forEach { info -> - if (info.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) return@forEach - updateBlockBreakingProgress(info, player.mainHandStack) + if (!info.requestHotbarSwap()) return@forEach + updateBlockBreakingProgress(info) + activeThisTick = true } } onRotate(priority = Int.MIN_VALUE) { preEvent() - if (isOnBreakCooldown()) { - blockBreakingCooldown-- - updateRequest(true) { true } - postEvent() - return@onRotate - } - - if (PlaceManager.activeThisTick()) { - updateRequest(true) { true } - postEvent() + if (!updateRequest { true }) { + requestRotate() return@onRotate } - //ToDo: improve instamine / non instamine integration - if (updateRequest { true }) { - currentRequest?.let request@ { request -> - var instaBreaks = 0 - val config = breakingInfos.firstOrNull()?.breakConfig ?: request.buildConfig.breakSettings - val takeCount = config.maxPendingBreaks - (breakingInfos.count { it != null } + pendingInteractions.size) - request.contexts - .sortedBy { it.instantBreak } - .take(takeCount) - .forEach { requestCtx -> - if (!canAccept(requestCtx)) return@forEach - val breakType = with(request) { - handleRequestContext(requestCtx, - buildConfig, rotationConfig, hotbarConfig, - onBreak, onItemDrop - ) - } - if (breakType == BreakType.Null) return@request - if (requestCtx.instantBreak && instaBreaks < request.buildConfig.breakSettings.breaksPerTick) { - breakingInfos.getOrNull(breakType.index)?.let { info -> - if (request.hotbarConfig.request(HotbarRequest(info.context.hotbarIndex)).done.not()) - return@forEach - if (updateBlockBreakingProgress(info, player.mainHandStack)) { - instaBreaks++ - activeThisTick = true - } - } - } + currentRequest?.let request@ { request -> + val breakConfig = request.buildConfig.breakSettings + val takeCount = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingInteractions.size) + val validContexts = request.contexts + .filter { ctx -> canAccept(ctx) } + .sortedBy { it.instantBreak } + .take(takeCount) + + instantBreaks = validContexts + .take(breakConfig.breaksPerTick) + .filter { it.instantBreak } + + if (instantBreaks.isNotEmpty()) return@request + + validContexts + .filter { it.instantBreak.not() } + .forEach { ctx -> + val breakInfo = with(request) { + handleRequestContext( + ctx, + buildConfig, rotationConfig, hotbarConfig, + onBreak, onItemDrop + ) } - } + if (breakInfo == null) return@request + } } - breakingInfos - .filterNotNull() - .firstOrNull { it.breakConfig.rotateForBreak } - ?.let { info -> - rotation = info.rotationConfig.request(info.context.rotation) - } + requestRotate() } onRotatePost { - breakingInfos + validRotation = breakingInfos .filterNotNull() - .firstOrNull()?.let { info -> - activeThisTick = true - validRotation = info.breakConfig.rotateForBreak && rotation?.done == true - } + .firstOrNull() + ?.let { info -> + !info.breakConfig.rotateForBreak || rotation?.done == true + } ?: true postEvent() } @@ -206,6 +217,15 @@ object BreakManager : RequestHandler() { false } + private fun requestRotate() { + rotation = breakingInfos + .filterNotNull() + .firstOrNull { it.breakConfig.rotateForBreak } + ?.let { info -> + info.rotationConfig.request(info.context.rotation) + } + } + private fun handleRequestContext( requestCtx: BreakContext, buildConfig: BuildConfig, @@ -213,29 +233,34 @@ object BreakManager : RequestHandler() { hotbarConfig: HotbarConfig, onBreak: () -> Unit, onItemDrop: (ItemEntity) -> Unit - ): BreakType { + ): BreakInfo? { val breakInfo = BreakInfo(requestCtx, BreakType.Primary, buildConfig.breakSettings, rotationConfig, hotbarConfig, onBreak, onItemDrop ) primaryBreakingInfo?.let { primaryInfo -> - if (!primaryInfo.breakConfig.doubleBreak) return BreakType.Null - if (primaryInfo.startedWithSecondary) return BreakType.Null + if (!primaryInfo.breakConfig.doubleBreak + || primaryInfo.startedWithSecondary + || secondaryBreakingInfo != null) { + return null + } + if (!primaryInfo.breaking) { secondaryBreakingInfo = breakInfo.apply { type = BreakType.Secondary } - return BreakType.Secondary - } else { - primaryInfo.type = BreakType.Secondary - secondaryBreakingInfo = primaryInfo - primaryBreakingInfo = breakInfo - setPendingInteractionsLimits(buildConfig) - return BreakType.Primary + return secondaryBreakingInfo } - } ?: run { + + primaryInfo.type = BreakType.Secondary + secondaryBreakingInfo = primaryInfo primaryBreakingInfo = breakInfo + setPendingInteractionsLimits(buildConfig) - return BreakType.Primary + return primaryBreakingInfo } + + primaryBreakingInfo = breakInfo + setPendingInteractionsLimits(buildConfig) + return primaryBreakingInfo } private fun setPendingInteractionsLimits(buildConfig: BuildConfig) { @@ -248,7 +273,7 @@ object BreakManager : RequestHandler() { && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } && !blockState(ctx.expectedPos).isAir - private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo, item: ItemStack): Boolean { + private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo): Boolean { val ctx = info.context val hitResult = ctx.result @@ -281,7 +306,7 @@ object BreakManager : RequestHandler() { player, world, ctx.expectedPos, - item + player.mainHandStack ) * info.breakingTicks if (info.breakConfig.sounds) { @@ -452,6 +477,9 @@ object BreakManager : RequestHandler() { var soundsCooldown = 0.0f var startedWithSecondary = false + fun requestHotbarSwap() = + hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done + fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { val breakDelta = context.checkedState.calcItemBlockBreakingDelta( player, @@ -472,21 +500,18 @@ object BreakManager : RequestHandler() { enum class BreakType(val index: Int) { Primary(0), - Secondary(1), - Null(-1); + Secondary(1); fun getBreakThreshold(breakConfig: BreakConfig) = when (this) { Primary -> breakConfig.breakThreshold Secondary -> 1.0f - else -> -1.0f } fun nullify() = when (this) { Primary -> primaryBreakingInfo = null Secondary -> secondaryBreakingInfo = null - else -> {} } } From 70be96eb74f7e4470447c58470a941c7902093ad Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 13 Mar 2025 02:51:38 +0000 Subject: [PATCH 052/364] moved onBreak and onBreakPost functions above init in break manager --- .../request/breaking/BreakManager.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index e437a3523..5a0e64cb9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -79,6 +79,16 @@ object BreakManager : RequestHandler() { private var rotation: RotationRequest? = null private var validRotation = false + fun Any.onBreak( + alwaysListen: Boolean = false, + block: SafeContext.() -> Unit + ) = listen(0, alwaysListen) { block() } + + fun Any.onBreakPost( + alwaysListen: Boolean = false, + block: SafeContext.() -> Unit + ) = listen(0, alwaysListen) { block() } + init { listen(Int.MIN_VALUE) { if (isOnBreakCooldown()) { @@ -515,16 +525,6 @@ object BreakManager : RequestHandler() { } } - fun Any.onBreak( - alwaysListen: Boolean = false, - block: SafeContext.() -> Unit - ) = listen(0, alwaysListen) { block() } - - fun Any.onBreakPost( - alwaysListen: Boolean = false, - block: SafeContext.() -> Unit - ) = listen(0, alwaysListen) { block() } - override fun preEvent() = UpdateManagerEvent.Break.Pre().post() override fun postEvent() = UpdateManagerEvent.Break.Post().post() } \ No newline at end of file From 1ec300d63054d1519eb64c3ca6fa877595ecc57a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 13 Mar 2025 03:29:16 +0000 Subject: [PATCH 053/364] simplified rotation logic for place and break managers --- .../lambda/interaction/request/breaking/BreakManager.kt | 8 +------- .../lambda/interaction/request/placing/PlaceManager.kt | 8 ++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 5a0e64cb9..5b04312cf 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -169,13 +169,7 @@ object BreakManager : RequestHandler() { } onRotatePost { - validRotation = breakingInfos - .filterNotNull() - .firstOrNull() - ?.let { info -> - !info.breakConfig.rotateForBreak || rotation?.done == true - } ?: true - + validRotation = rotation?.done ?: true postEvent() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 9d454a8ab..dee2d9110 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -99,9 +99,9 @@ object PlaceManager : RequestHandler() { activeThisTick = true - if (request.buildConfig.placeSettings.rotateForPlace) { - rotation = request.rotationConfig.request(request.placeContext.rotation) - } + rotation = if (request.buildConfig.placeSettings.rotateForPlace) + request.rotationConfig.request(request.placeContext.rotation) + else null pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions) pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L) @@ -109,7 +109,7 @@ object PlaceManager : RequestHandler() { } onRotatePost { - validRotation = rotation?.done == true + validRotation = rotation?.done ?: true postEvent() } From fbe46621e4f64e3433736d4fb5c86bf726ca0fdb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 13 Mar 2025 15:56:47 +0000 Subject: [PATCH 054/364] player utils --- .../construction/simulation/BuildSimulator.kt | 7 ++++--- .../material/container/containers/CreativeContainer.kt | 6 +++--- .../interaction/request/breaking/BreakManager.kt | 9 +++++---- .../lambda/interaction/request/placing/PlaceManager.kt | 10 ++++++---- common/src/main/kotlin/com/lambda/util/BlockUtils.kt | 5 +++-- .../main/kotlin/com/lambda/util/player/PlayerUtils.kt | 8 +++++++- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index bc491ce6b..cd4735830 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -56,6 +56,7 @@ import com.lambda.util.item.ItemUtils.findBestToolsForBreaking import com.lambda.util.math.distSq import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.copyPlayer +import com.lambda.util.player.gamemode import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition @@ -121,7 +122,7 @@ object BuildSimulator { } /* the player is in the wrong game mode to alter the block state */ - if (player.isBlockBreakingRestricted(world, pos, interaction.currentGameMode)) { + if (player.isBlockBreakingRestricted(world, pos, gamemode)) { return BuildResult.Restricted(pos) } @@ -136,7 +137,7 @@ object BuildSimulator { } /* block is unbreakable, so it cant be broken or replaced */ - if (state.getHardness(world, pos) < 0 && !player.isCreative) { + if (state.getHardness(world, pos) < 0 && !gamemode.isCreative) { return BuildResult.Unbreakable(pos, state) } @@ -461,7 +462,7 @@ object BuildSimulator { eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant ) - if (player.isCreative) { + if (gamemode.isCreative) { acc.add(BreakResult.Break(pos, breakContext)) return acc } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt index 7f9af97f2..4bf3d515f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt @@ -22,9 +22,9 @@ import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer import com.lambda.interaction.material.transfer.TransactionExecutor -import com.lambda.interaction.material.transfer.TransactionExecutor.Companion import com.lambda.task.Task import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.player.gamemode import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack @@ -44,7 +44,7 @@ data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { override val name: String get() = "Removing $selection from creative inventory" override fun SafeContext.onStart() { - if (!player.isCreative) { + if (!gamemode.isCreative) { // ToDo: Maybe switch gamemode? throw NotInCreativeModeException() } @@ -70,7 +70,7 @@ data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { selection.optimalStack?.let { optimalStack -> if (player.mainHandStack.equal(optimalStack)) return - if (!player.isCreative) { + if (!gamemode.isCreative) { // ToDo: Maybe switch gamemode? throw NotInCreativeModeException() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 5b04312cf..1b9971283 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -44,6 +44,7 @@ import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.item.ItemUtils.block +import com.lambda.util.player.gamemode import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock @@ -281,7 +282,7 @@ object BreakManager : RequestHandler() { val ctx = info.context val hitResult = ctx.result - if (interaction.currentGameMode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { + if (gamemode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { setBreakCooldown(info.breakConfig.breakDelay) interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) @@ -358,10 +359,10 @@ object BreakManager : RequestHandler() { private fun SafeContext.attackBlock(info: BreakInfo): Boolean { val ctx = info.context - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false if (!world.worldBorder.contains(ctx.expectedPos)) return false - if (interaction.currentGameMode.isCreative) { + if (gamemode.isCreative) { interaction.sendSequencedPacket(world) { sequence: Int -> onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) @@ -433,7 +434,7 @@ object BreakManager : RequestHandler() { private fun SafeContext.destroyBlock(info: BreakInfo): Boolean { val ctx = info.context - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, interaction.currentGameMode)) return false + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) return false diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index dee2d9110..cd9c095db 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -37,6 +37,8 @@ import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +import com.lambda.util.player.gamemode +import com.lambda.util.player.isItemOnCooldown import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.pattern.CachedBlockPosition @@ -192,7 +194,7 @@ object PlaceManager : RequestHandler() { hitResult: BlockHitResult ): ActionResult { val itemStack = player.getStackInHand(hand) - if (interaction.currentGameMode == GameMode.SPECTATOR) return ActionResult.SUCCESS + if (gamemode == GameMode.SPECTATOR) return ActionResult.SUCCESS // checks if the player should be able to interact with the block for if its something // like a furnace or chest where an action would happen @@ -200,9 +202,9 @@ object PlaceManager : RequestHandler() { // val cantInteract = player.shouldCancelInteraction() && handNotEmpty // if (!cantInteract) return ActionResult.PASS - if (!itemStack.isEmpty && !player.itemCooldownManager.isCoolingDown(itemStack.item)) { + if (!itemStack.isEmpty && !isItemOnCooldown(itemStack.item)) { val itemUsageContext = ItemUsageContext(player, hand, hitResult) - return if (interaction.currentGameMode.isCreative) { + return if (gamemode.isCreative) { val i = itemStack.count useOnBlock(placeConfig, itemStack, itemUsageContext) .also { @@ -219,7 +221,7 @@ object PlaceManager : RequestHandler() { itemStack: ItemStack, context: ItemUsageContext ): ActionResult { - val cachedBlockPosition = CachedBlockPosition(context.world, context.blockPos, false) + val cachedBlockPosition = CachedBlockPosition(world, context.blockPos, false) val cantModifyWorld = !player.abilities.allowModifyWorld val cantPlaceOn = !itemStack.canPlaceOn(context.world.registryManager.get(RegistryKeys.BLOCK), cachedBlockPosition) diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index e508c9795..ea0292987 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -19,6 +19,7 @@ package com.lambda.util import com.lambda.context.SafeContext import com.lambda.util.math.MathUtils.floorToInt +import com.lambda.util.player.gamemode import net.minecraft.block.AbstractCauldronBlock import net.minecraft.block.AbstractFurnaceBlock import net.minecraft.block.AbstractSignBlock @@ -237,12 +238,12 @@ object BlockUtils { fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, breakThreshold: Float): Boolean { val ticksNeeded = 1 / (blockState.calcBlockBreakingDelta(player, world, blockPos) / breakThreshold) - return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative + return (ticksNeeded <= 1 && ticksNeeded != 0f) || gamemode.isCreative } fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, item: ItemStack, breakThreshold: Float): Boolean { val ticksNeeded = 1 / (blockState.calcItemBlockBreakingDelta(player, world, blockPos, item) / breakThreshold) - return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative + return (ticksNeeded <= 1 && ticksNeeded != 0f) || gamemode.isCreative } fun BlockState.calcItemBlockBreakingDelta( diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index c972b5eb7..f140850a2 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -7,9 +7,13 @@ import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket import net.minecraft.util.Hand +val SafeContext.gamemode + get() = interaction.currentGameMode + fun SafeContext.copyPlayer(entity: ClientPlayerEntity) = ClientPlayerEntity(mc, world, mc.networkHandler, null, null, entity.isSneaking, entity.isSprinting).apply { setPos(entity.x, entity.y, entity.z) @@ -56,4 +60,6 @@ fun SafeContext.swingHandClient(hand: Hand) { player.handSwinging = true player.preferredHand = hand } -} \ No newline at end of file +} + +fun SafeContext.isItemOnCooldown(item: Item) = player.itemCooldownManager.isCoolingDown(item) \ No newline at end of file From f7443660f872b98fd40255b314d8e0622850a53c Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 13 Mar 2025 16:55:43 +0000 Subject: [PATCH 055/364] moved build logic back to on rotate in build task --- .../request/breaking/BreakManager.kt | 4 ++-- .../request/placing/PlaceManager.kt | 4 ++-- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 23 ++++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 1b9971283..88cf94977 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -91,7 +91,7 @@ object BreakManager : RequestHandler() { ) = listen(0, alwaysListen) { block() } init { - listen(Int.MIN_VALUE) { + listen(priority = Int.MIN_VALUE) { if (isOnBreakCooldown()) { blockBreakingCooldown-- return@listen @@ -169,7 +169,7 @@ object BreakManager : RequestHandler() { requestRotate() } - onRotatePost { + onRotatePost(priority = Int.MIN_VALUE) { validRotation = rotation?.done ?: true postEvent() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index cd9c095db..18d80077b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -68,7 +68,7 @@ object PlaceManager : RequestHandler() { get() = pendingInteractions.map { it.context.expectedPos } init { - listen { + listen(priority = Int.MIN_VALUE) { currentRequest?.let { request -> val notSneaking = !player.isSneaking val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)) @@ -110,7 +110,7 @@ object PlaceManager : RequestHandler() { } } - onRotatePost { + onRotatePost(priority = Int.MIN_VALUE) { validRotation = rotation?.done ?: true postEvent() } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 9aedc26fa..0ce9b5273 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -42,6 +42,7 @@ import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.tr import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.placing.PlaceRequest +import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -90,15 +91,15 @@ class BuildTask @Ta5kBuilder constructor( } init { - listen { - if (collectDrops()) return@listen + onRotate { + if (collectDrops()) return@onRotate // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) TaskFlowModule.drawables = results.filterIsInstance() .plus(pendingInteractions.toList()) -// .plus(sim.goodPositions()) + // .plus(sim.goodPositions()) if (build.breakSettings.breaksPerTick > 1) { val instantResults = results.filterIsInstance() @@ -113,31 +114,31 @@ class BuildTask @Ta5kBuilder constructor( onBreak = { breaks++ } ) { item -> if (collectDrops) dropsToCollect.add(item) } ) - return@listen + return@onRotate } } val resultsNotBlocked = results.filterNot { result -> result.blockPos in pendingInteractions.map { it.expectedPos } }.sorted() - val bestResult = resultsNotBlocked.firstOrNull() ?: return@listen + val bestResult = resultsNotBlocked.firstOrNull() ?: return@onRotate when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { - if (pendingInteractions.isNotEmpty()) return@listen + if (pendingInteractions.isNotEmpty()) return@onRotate if (blueprint is PropagatingBlueprint) { blueprint.next() - return@listen + return@onRotate } if (finishOnDone) success() } is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { - if (!build.pathing) return@listen + if (!build.pathing) return@onRotate val sim = blueprint.simulation(interact, rotation, inventory, build) val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) @@ -148,7 +149,7 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { - if (pendingInteractions.size >= build.maxPendingInteractions) return@listen + if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate val breakContexts = resultsNotBlocked.filterIsInstance().map { it.context } if (breakContexts.isNotEmpty()) { val request = BreakRequest( @@ -156,9 +157,9 @@ class BuildTask @Ta5kBuilder constructor( onBreak = { breaks++ }, ) { item -> if (collectDrops) dropsToCollect.add(item) } build.breakSettings.request(request) - return@listen + return@onRotate } - if (bestResult !is PlaceResult.Place) return@listen + if (bestResult !is PlaceResult.Place) return@onRotate build.placeSettings.request( PlaceRequest(bestResult.context, build, rotation, hotbar, interact) { placements++ } ) From 6edb0a694d94eccaceba20bbe788d7741af6d20c Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 13 Mar 2025 18:58:03 +0000 Subject: [PATCH 056/364] improved build task stuff --- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 0ce9b5273..cd6dbacbe 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -39,8 +39,10 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer +import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule @@ -101,13 +103,21 @@ class BuildTask @Ta5kBuilder constructor( .plus(pendingInteractions.toList()) // .plus(sim.goodPositions()) + val resultsNotBlocked = results.filter { result -> + when(result) { + is BreakResult.Break -> { BreakManager.blockedPositions.none { blocked -> blocked == result.blockPos } } + is PlaceResult.Place -> { PlaceManager.blockedPositions.none { blocked -> blocked == result.blockPos } } + else -> true + } + } + .sorted() + if (build.breakSettings.breaksPerTick > 1) { - val instantResults = results.filterIsInstance() + val instantResults = resultsNotBlocked.filterIsInstance() .filter { it.context.instantBreak } - .sorted() .take(build.breakSettings.breaksPerTick) - instantResults.firstOrNull()?.let { + if (instantResults.isNotEmpty()) { build.breakSettings.request( BreakRequest( instantResults.map { it.context }, build, rotation, hotbar, @@ -118,9 +128,6 @@ class BuildTask @Ta5kBuilder constructor( } } - val resultsNotBlocked = results.filterNot { result -> - result.blockPos in pendingInteractions.map { it.expectedPos } - }.sorted() val bestResult = resultsNotBlocked.firstOrNull() ?: return@onRotate when (bestResult) { is BuildResult.Done, @@ -150,19 +157,25 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Contextual -> { if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate - val breakContexts = resultsNotBlocked.filterIsInstance().map { it.context } - if (breakContexts.isNotEmpty()) { - val request = BreakRequest( - breakContexts, build, rotation, hotbar, - onBreak = { breaks++ }, - ) { item -> if (collectDrops) dropsToCollect.add(item) } - build.breakSettings.request(request) - return@onRotate + when (bestResult) { + is BreakResult.Break -> { + val breakContexts = resultsNotBlocked + .filterIsInstance() + .map { it.context } + + val request = BreakRequest( + breakContexts, build, rotation, hotbar, + onBreak = { breaks++ } + ) { item -> if (collectDrops) dropsToCollect.add(item) } + build.breakSettings.request(request) + return@onRotate + } + is PlaceResult.Place -> { + build.placeSettings.request( + PlaceRequest(bestResult.context, build, rotation, hotbar, interact) { placements++ } + ) + } } - if (bestResult !is PlaceResult.Place) return@onRotate - build.placeSettings.request( - PlaceRequest(bestResult.context, build, rotation, hotbar, interact) { placements++ } - ) } is Resolvable -> { From 8c846ec9a0e924f9e5afe16856a1f7505bed11ac Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 15 Mar 2025 00:00:10 +0000 Subject: [PATCH 057/364] manager utils, abstracted blockedPositions, and build task now checks blocked positions before calling success --- .../interaction/request/ManagerUtils.kt | 35 +++++++++++++++++++ .../interaction/request/PositionBlocking.kt | 24 +++++++++++++ .../request/breaking/BreakManager.kt | 11 +++--- .../request/placing/PlaceManager.kt | 9 ++--- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 5 ++- 5 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt new file mode 100644 index 000000000..d5f5e44f9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request + +import com.lambda.interaction.request.breaking.BreakManager +import com.lambda.interaction.request.hotbar.HotbarManager +import com.lambda.interaction.request.placing.PlaceManager +import com.lambda.interaction.request.rotation.RotationManager + +object ManagerUtils { + val managers = listOf( + RotationManager, + HotbarManager, + BreakManager, + PlaceManager + ) + + val positionBlockingManagers = managers + .filterIsInstance() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt b/common/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt new file mode 100644 index 000000000..ef3cd6a77 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request + +import net.minecraft.util.math.BlockPos + +interface PositionBlocking { + val blockedPositions: List +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 88cf94977..6668f568e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -26,6 +26,7 @@ import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode @@ -57,7 +58,7 @@ import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory import net.minecraft.util.math.BlockPos -object BreakManager : RequestHandler() { +object BreakManager : RequestHandler(), PositionBlocking { private var primaryBreakingInfo: BreakInfo? get() = breakingInfos[0] set(value) { breakingInfos[0] = value } @@ -70,16 +71,16 @@ object BreakManager : RequestHandler() { TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } - val blockedPositions + override val blockedPositions get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingInteractions.map { it.context.expectedPos } + private var rotation: RotationRequest? = null + private var validRotation = false + private var blockBreakingCooldown = 0 private var instantBreaks = listOf() - private var rotation: RotationRequest? = null - private var validRotation = false - fun Any.onBreak( alwaysListen: Boolean = false, block: SafeContext.() -> Unit diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 18d80077b..67e070b3a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -27,6 +27,7 @@ import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarRequest @@ -56,17 +57,17 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.GameMode import org.apache.commons.lang3.mutable.MutableObject -object PlaceManager : RequestHandler() { +object PlaceManager : RequestHandler(), PositionBlocking { private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + override val blockedPositions + get() = pendingInteractions.map { it.context.expectedPos } + private var rotation: RotationRequest? = null private var validRotation = false - val blockedPositions - get() = pendingInteractions.map { it.context.expectedPos } - init { listen(priority = Int.MIN_VALUE) { currentRequest?.let { request -> diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index cd6dbacbe..325af4b92 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -39,6 +39,7 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer +import com.lambda.interaction.request.ManagerUtils.positionBlockingManagers import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig @@ -140,7 +141,9 @@ class BuildTask @Ta5kBuilder constructor( blueprint.next() return@onRotate } - if (finishOnDone) success() + + val managersStillRunning = positionBlockingManagers.any { it.blockedPositions.isNotEmpty() } + if (finishOnDone && !managersStillRunning) success() } is BuildResult.NotVisible, From 9ce0fda5745a80fbba8bf42975e19dc839046e6d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 15 Mar 2025 00:18:08 +0000 Subject: [PATCH 058/364] on methods for manager events --- .../request/breaking/BreakManager.kt | 11 +++++++++-- .../request/hotbar/HotbarManager.kt | 18 ++++++++++++++++++ .../request/placing/PlaceManager.kt | 17 +++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 6668f568e..1d498e1e8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -27,6 +27,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking +import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode @@ -83,13 +84,19 @@ object BreakManager : RequestHandler(), PositionBlocking { fun Any.onBreak( alwaysListen: Boolean = false, + priority: Priority = 0, block: SafeContext.() -> Unit - ) = listen(0, alwaysListen) { block() } + ) = this.listen(priority, alwaysListen) { + block() + } fun Any.onBreakPost( alwaysListen: Boolean = false, + priority: Priority = 0, block: SafeContext.() -> Unit - ) = listen(0, alwaysListen) { block() } + ) = this.listen(priority, alwaysListen) { + block() + } init { listen(priority = Int.MIN_VALUE) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 48dd51fe5..f1cda3553 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -17,12 +17,14 @@ package com.lambda.interaction.request.hotbar +import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.EventFlow.post import com.lambda.event.events.InventoryEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.threading.runSafe import com.lambda.mixin.entity.PlayerInventoryMixin @@ -41,6 +43,22 @@ object HotbarManager : RequestHandler(), Loadable { override fun load() = "Loaded Hotbar Manager" + fun Any.onHotbarUpdate( + alwaysListen: Boolean = false, + priority: Priority = 0, + block: SafeContext.() -> Unit + ) = this.listen(priority, alwaysListen) { + block() + } + + fun Any.onHotbarUpdatePost( + alwaysListen: Boolean = false, + priority: Priority = 0, + block: SafeContext.() -> Unit + ) = this.listen(priority, alwaysListen) { + block() + } + init { listen { it.slot = currentRequest?.slot ?: return@listen diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 67e070b3a..f181fe9b7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -28,6 +28,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking +import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarRequest @@ -68,6 +69,22 @@ object PlaceManager : RequestHandler(), PositionBlocking { private var rotation: RotationRequest? = null private var validRotation = false + fun Any.onPlace( + alwaysListen: Boolean = false, + priority: Priority = 0, + block: SafeContext.() -> Unit + ) = this.listen(priority, alwaysListen) { + block() + } + + fun Any.onPlacePost( + alwaysListen: Boolean = false, + priority: Priority = 0, + block: SafeContext.() -> Unit + ) = this.listen(priority, alwaysListen) { + block() + } + init { listen(priority = Int.MIN_VALUE) { currentRequest?.let { request -> From 524f33b299f9b3397a6e496ea477ac5c5e97bd2d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 17 Mar 2025 02:10:48 +0000 Subject: [PATCH 059/364] pending interaction improvements --- .../construction/context/BreakContext.kt | 2 +- .../construction/verify/TargetState.kt | 6 +- .../request/breaking/BreakManager.kt | 42 +++++-- .../request/placing/PlaceManager.kt | 108 ++++++++---------- .../main/kotlin/com/lambda/util/BlockUtils.kt | 5 + 5 files changed, 88 insertions(+), 75 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 4a98f9864..bc5546635 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -38,7 +38,7 @@ data class BreakContext( override val pov: Vec3d, override val result: BlockHitResult, override val rotation: RotationRequest, - override val checkedState: BlockState, + override var checkedState: BlockState, override val targetState: TargetState, override var hotbarIndex: Int, var instantBreak: Boolean, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index fa7a15edf..7581a3c4a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -19,7 +19,7 @@ package com.lambda.interaction.construction.verify import com.lambda.interaction.material.container.ContainerManager.findDisposable import com.lambda.module.modules.client.TaskFlowModule -import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.matches import com.lambda.util.StringUtils.capitalize import com.lambda.util.item.ItemUtils.block import net.minecraft.block.BlockState @@ -80,9 +80,7 @@ sealed class TargetState(val type: Type) : StateMatcher { override fun toString() = "State of $blockState" override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = - state.block == blockState.block && state.properties.all { - /*it in TaskFlowModule.defaultIgnoreTags ||*/ state[it] == blockState[it] - } + state.matches(blockState) override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = blockState.block.getPickStack(world, pos, blockState) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 1d498e1e8..0a815365b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request.breaking +import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post @@ -42,6 +43,7 @@ import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.BlockUtils.fluidState +import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue @@ -70,7 +72,10 @@ object BreakManager : RequestHandler(), PositionBlocking { private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + ) { + info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") + mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + } override val blockedPositions get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingInteractions.map { it.context.expectedPos } @@ -187,21 +192,36 @@ object BreakManager : RequestHandler(), PositionBlocking { pendingInteractions .firstOrNull { it.context.expectedPos == event.pos } ?.let { pending -> + // return if the state hasn't changed + if (event.newState.matches(pending.context.checkedState)) + return@listen + pendingInteractions.remove(pending) - if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) return@listen - if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) + // return if the block's not broken + if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) + return@listen + + if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { destroyBlock(pending) + } pending.onBreak() + return@listen } - ?: breakingInfos - .filterNotNull() - .firstOrNull { it.context.expectedPos == event.pos } - ?.let { info -> - if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) return@listen - info.nullify() - destroyBlock(info) - info.onBreak() + + breakingInfos + .filterNotNull() + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { info -> + // if not broken + if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) { + // update the checked state + info.context.checkedState = event.newState + return@listen } + destroyBlock(info) + info.onBreak() + info.nullify() + } } //ToDo: drop callback stuff diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index f181fe9b7..81f3ae3bc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -17,7 +17,7 @@ package com.lambda.interaction.request.placing -import com.lambda.config.groups.BuildConfig +import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.MovementEvent @@ -36,6 +36,8 @@ import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.request.rotation.RotationManager.onRotatePost import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.item +import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue @@ -56,15 +58,17 @@ import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.world.GameMode -import org.apache.commons.lang3.mutable.MutableObject object PlaceManager : RequestHandler(), PositionBlocking { - private val pendingInteractions = LimitedDecayQueue( + private val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") } + ) { + info("${it::class.simpleName} at ${it.placeContext.expectedPos.toShortString()} timed out") + mc.world?.setBlockState(it.placeContext.expectedPos, it.placeContext.checkedState) + } override val blockedPositions - get() = pendingInteractions.map { it.context.expectedPos } + get() = pendingInteractions.map { it.placeContext.expectedPos } private var rotation: RotationRequest? = null private var validRotation = false @@ -95,6 +99,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { return@listen placeBlock(request, Hand.MAIN_HAND) + activeThisTick = true } } @@ -117,8 +122,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { return@onRotate } - activeThisTick = true - rotation = if (request.buildConfig.placeSettings.rotateForPlace) request.rotationConfig.request(request.placeContext.rotation) else null @@ -139,20 +142,27 @@ object PlaceManager : RequestHandler(), PositionBlocking { listen { event -> pendingInteractions - .firstOrNull { it.context.expectedPos == event.pos } - ?.let { pending -> - pendingInteractions.remove(pending) - if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) return@listen - if (pending.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) - placeSound(pending.item, pending.context.expectedState, pending.context.expectedPos) - pending.onPlace() + .firstOrNull { it.placeContext.expectedPos == event.pos } + ?.let { request -> + pendingInteractions.remove(request) + + // return if the block wasn't placed + if (!matchesTargetState(event.pos, request.placeContext.targetState, event.newState)) + return@listen + + if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) + with (request.placeContext) { + placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos) + } + request.onPlace() + return@listen } } } private fun canPlace(placeContext: PlaceContext) = pendingInteractions.none { pending -> - pending.context.expectedPos == placeContext.expectedPos + pending.placeContext.expectedPos == placeContext.expectedPos } private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = @@ -162,48 +172,35 @@ object PlaceManager : RequestHandler(), PositionBlocking { false } - private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) { - val stackInHand = player.getStackInHand(hand) - val item = stackInHand.item as? BlockItem ?: return - val stackCountPre = stackInHand.count - val actionResult = interactBlock(request.buildConfig.placeSettings, hand, request.placeContext.result) - - if (actionResult.isAccepted) { - if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) - request.onPlace() - else { - pendingInteractions.add( - PlaceInfo( - request.placeContext, - item, - request.buildConfig, - request.onPlace - ) - ) - } - - if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) { - swingHand(request.buildConfig.placeSettings.swingType) - } + private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) = + interactBlock(request, request.buildConfig.placeSettings, hand, request.placeContext.result) - if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { - mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) - } - } else { - warn("Placement interaction failed with $actionResult") - } - } - - private fun SafeContext.interactBlock(placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult): ActionResult { + private fun SafeContext.interactBlock(request: PlaceRequest, placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult) { interaction.syncSelectedSlot() - if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL + if (!world.worldBorder.contains(hitResult.blockPos)) return - val mutableActionResult = MutableObject() interaction.sendSequencedPacket(world) { sequence: Int -> - mutableActionResult.value = interactBlockInternal(placeConfig, hand, hitResult) + val stackInHand = player.getStackInHand(hand) + val stackCountPre = stackInHand.count + val actionResult = interactBlockInternal(placeConfig, hand, hitResult) + if (actionResult.isAccepted) { + if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) + request.onPlace() + else + pendingInteractions.add(request) + + if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) { + swingHand(request.buildConfig.placeSettings.swingType) + } + + if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + } + } else { + warn("Placement interaction failed with $actionResult") + } PlayerInteractBlockC2SPacket(hand, hitResult, sequence) } - return mutableActionResult.value } private fun SafeContext.interactBlockInternal( @@ -212,7 +209,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { hitResult: BlockHitResult ): ActionResult { val itemStack = player.getStackInHand(hand) - if (gamemode == GameMode.SPECTATOR) return ActionResult.SUCCESS + if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS // checks if the player should be able to interact with the block for if its something // like a furnace or chest where an action would happen @@ -297,13 +294,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { ) } - data class PlaceInfo( - val context: PlaceContext, - val item: BlockItem, - val buildConfig: BuildConfig, - val onPlace: () -> Unit - ) - override fun preEvent() = UpdateManagerEvent.Place.Pre().post() override fun postEvent() = UpdateManagerEvent.Place.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index ea0292987..96b6d5556 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -236,6 +236,11 @@ object BlockUtils { fun SafeContext.fluidState(pos: BlockPos): FluidState = world.getFluidState(pos) fun SafeContext.blockEntity(pos: BlockPos) = world.getBlockEntity(pos) + fun BlockState.matches(state: BlockState) = + this.block == state.block && this.properties.all { + /*it in TaskFlowModule.defaultIgnoreTags ||*/ this[it] == state[it] + } + fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, breakThreshold: Float): Boolean { val ticksNeeded = 1 / (blockState.calcBlockBreakingDelta(player, world, blockPos) / breakThreshold) return (ticksNeeded <= 1 && ticksNeeded != 0f) || gamemode.isCreative From a4f8d6022a1c2c190d0197b842c9f04f78c5bebd Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 17 Mar 2025 03:18:01 +0000 Subject: [PATCH 060/364] import --- .../com/lambda/interaction/request/placing/PlaceManager.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 81f3ae3bc..0c9e9b09c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -37,7 +37,6 @@ import com.lambda.interaction.request.rotation.RotationManager.onRotatePost import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.item -import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue From 8a89f7cd91e924bde3ff94092e59f4d5997f0037 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 18 Mar 2025 18:55:07 +0000 Subject: [PATCH 061/364] onItemBreak variable --- .../interaction/request/breaking/BreakManager.kt | 5 ++--- .../interaction/request/breaking/BreakRequest.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 15 +++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 0a815365b..c0064b074 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -187,7 +187,6 @@ object BreakManager : RequestHandler(), PositionBlocking { postEvent() } - //ToDo: Clean this up listen { event -> pendingInteractions .firstOrNull { it.context.expectedPos == event.pos } @@ -265,7 +264,7 @@ object BreakManager : RequestHandler(), PositionBlocking { rotationConfig: RotationConfig, hotbarConfig: HotbarConfig, onBreak: () -> Unit, - onItemDrop: (ItemEntity) -> Unit + onItemDrop: ((ItemEntity) -> Unit)? ): BreakInfo? { val breakInfo = BreakInfo(requestCtx, BreakType.Primary, buildConfig.breakSettings, rotationConfig, hotbarConfig, @@ -503,7 +502,7 @@ object BreakManager : RequestHandler(), PositionBlocking { val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, val onBreak: () -> Unit, - val onItemDrop: (ItemEntity) -> Unit + val onItemDrop: ((ItemEntity) -> Unit)? ) { var breaking = false var breakingTicks = 0 diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 28d70a587..c3e87f73a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -34,7 +34,7 @@ data class BreakRequest( val hotbarConfig: HotbarConfig, val prio: Priority = 0, val onBreak: () -> Unit, - val onItemDrop: (ItemEntity) -> Unit, + val onItemDrop: ((ItemEntity) -> Unit)?, ) : Request(prio) { override val done: Boolean get() = runSafe { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 325af4b92..9b7e59936 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -80,6 +80,11 @@ class BuildTask @Ta5kBuilder constructor( private val dropsToCollect = mutableSetOf() // private var goodPositions = setOf() + private val onItemDrop: ((item: ItemEntity) -> Unit)? + get() = if (collectDrops) { + item -> dropsToCollect.add(item) + } else null + override fun SafeContext.onStart() { (blueprint as? PropagatingBlueprint)?.next() } @@ -122,8 +127,9 @@ class BuildTask @Ta5kBuilder constructor( build.breakSettings.request( BreakRequest( instantResults.map { it.context }, build, rotation, hotbar, - onBreak = { breaks++ } - ) { item -> if (collectDrops) dropsToCollect.add(item) } + onBreak = { breaks++ }, + onItemDrop = onItemDrop + ) ) return@onRotate } @@ -168,8 +174,9 @@ class BuildTask @Ta5kBuilder constructor( val request = BreakRequest( breakContexts, build, rotation, hotbar, - onBreak = { breaks++ } - ) { item -> if (collectDrops) dropsToCollect.add(item) } + onBreak = { breaks++ }, + onItemDrop = onItemDrop + ) build.breakSettings.request(request) return@onRotate } From 53bb4defefe48b3789020dc17e1c4e9d0c325e59 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 19 Mar 2025 01:59:27 +0000 Subject: [PATCH 062/364] renamed pending interaction queues to match the managers --- .../request/breaking/BreakManager.kt | 22 +++++++++---------- .../request/placing/PlaceManager.kt | 18 +++++++-------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index c0064b074..2116c388b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -70,7 +70,7 @@ object BreakManager : RequestHandler(), PositionBlocking { set(value) { breakingInfos[1] = value } private val breakingInfos = arrayOfNulls(2) - private val pendingInteractions = LimitedDecayQueue( + private val pendingBreaks = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") @@ -78,7 +78,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } override val blockedPositions - get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingInteractions.map { it.context.expectedPos } + get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } private var rotation: RotationRequest? = null private var validRotation = false @@ -153,7 +153,7 @@ object BreakManager : RequestHandler(), PositionBlocking { currentRequest?.let request@ { request -> val breakConfig = request.buildConfig.breakSettings - val takeCount = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingInteractions.size) + val takeCount = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) val validContexts = request.contexts .filter { ctx -> canAccept(ctx) } .sortedBy { it.instantBreak } @@ -188,14 +188,14 @@ object BreakManager : RequestHandler(), PositionBlocking { } listen { event -> - pendingInteractions + pendingBreaks .firstOrNull { it.context.expectedPos == event.pos } ?.let { pending -> // return if the state hasn't changed if (event.newState.matches(pending.context.checkedState)) return@listen - pendingInteractions.remove(pending) + pendingBreaks.remove(pending) // return if the block's not broken if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) return@listen @@ -227,7 +227,7 @@ object BreakManager : RequestHandler(), PositionBlocking { // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work // listen { // if (it.entity !is ItemEntity) return@listen -// pendingInteractions +// pendingBreaks // .firstOrNull { info -> matchesBlockItem(info, it.entity) } // ?.onItemDrop?.invoke(it.entity) // ?: breakingInfos @@ -296,12 +296,12 @@ object BreakManager : RequestHandler(), PositionBlocking { } private fun setPendingInteractionsLimits(buildConfig: BuildConfig) { - pendingInteractions.setMaxSize(buildConfig.maxPendingInteractions) - pendingInteractions.setDecayTime(buildConfig.interactionTimeout * 50L) + pendingBreaks.setMaxSize(buildConfig.maxPendingInteractions) + pendingBreaks.setDecayTime(buildConfig.interactionTimeout * 50L) } private fun SafeContext.canAccept(ctx: BreakContext) = - pendingInteractions.none { it.context.expectedPos == ctx.expectedPos } + pendingBreaks.none { it.context.expectedPos == ctx.expectedPos } && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } && !blockState(ctx.expectedPos).isAir @@ -449,10 +449,10 @@ object BreakManager : RequestHandler(), PositionBlocking { } BreakConfirmationMode.BreakThenAwait -> { destroyBlock(info) - pendingInteractions.add(info) + pendingBreaks.add(info) } BreakConfirmationMode.AwaitThenBreak -> { - pendingInteractions.add(info) + pendingBreaks.add(info) } } info.nullify() diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 0c9e9b09c..0d8db619d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -59,7 +59,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.GameMode object PlaceManager : RequestHandler(), PositionBlocking { - private val pendingInteractions = LimitedDecayQueue( + private val pendingPlacements = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.placeContext.expectedPos.toShortString()} timed out") @@ -67,7 +67,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { } override val blockedPositions - get() = pendingInteractions.map { it.placeContext.expectedPos } + get() = pendingPlacements.map { it.placeContext.expectedPos } private var rotation: RotationRequest? = null private var validRotation = false @@ -116,7 +116,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { } currentRequest?.let request@ { request -> - if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) { + if (pendingPlacements.size >= request.buildConfig.placeSettings.maxPendingPlacements) { postEvent() return@onRotate } @@ -125,8 +125,8 @@ object PlaceManager : RequestHandler(), PositionBlocking { request.rotationConfig.request(request.placeContext.rotation) else null - pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions) - pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L) + pendingPlacements.setMaxSize(request.buildConfig.maxPendingInteractions) + pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) } } @@ -140,10 +140,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { } listen { event -> - pendingInteractions + pendingPlacements .firstOrNull { it.placeContext.expectedPos == event.pos } ?.let { request -> - pendingInteractions.remove(request) + pendingPlacements.remove(request) // return if the block wasn't placed if (!matchesTargetState(event.pos, request.placeContext.targetState, event.newState)) @@ -160,7 +160,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { } private fun canPlace(placeContext: PlaceContext) = - pendingInteractions.none { pending -> + pendingPlacements.none { pending -> pending.placeContext.expectedPos == placeContext.expectedPos } @@ -186,7 +186,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) request.onPlace() else - pendingInteractions.add(request) + pendingPlacements.add(request) if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) { swingHand(request.buildConfig.placeSettings.swingType) From 7edb2110a40047a6830065207067c76b2692f216 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 19 Mar 2025 20:26:00 +0000 Subject: [PATCH 063/364] item drop callbacks --- .../request/breaking/BreakManager.kt | 71 +++++++++++++++---- .../module/modules/player/HighwayTools.kt | 1 + .../kotlin/com/lambda/task/tasks/BuildTask.kt | 58 +++++++-------- 3 files changed, 82 insertions(+), 48 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 2116c388b..1a9fdc64d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -21,6 +21,7 @@ import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post +import com.lambda.event.events.EntityEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent @@ -195,15 +196,19 @@ object BreakManager : RequestHandler(), PositionBlocking { if (event.newState.matches(pending.context.checkedState)) return@listen - pendingBreaks.remove(pending) // return if the block's not broken - if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) + if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) { + pendingBreaks.remove(pending) return@listen + } if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { destroyBlock(pending) } - pending.onBreak() + pending.internalOnBreak() + if (pending.callbacksCompleted) { + pendingBreaks.remove(pending) + } return@listen } @@ -218,22 +223,34 @@ object BreakManager : RequestHandler(), PositionBlocking { return@listen } destroyBlock(info) - info.onBreak() + info.internalOnBreak() info.nullify() + if (!info.callbacksCompleted) { + pendingBreaks.add(info) + } } } - //ToDo: drop callback stuff // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work -// listen { -// if (it.entity !is ItemEntity) return@listen -// pendingBreaks -// .firstOrNull { info -> matchesBlockItem(info, it.entity) } -// ?.onItemDrop?.invoke(it.entity) -// ?: breakingInfos -// .filterNotNull() -// .firstOrNull { info -> matchesBlockItem(info, it.entity) }?.onItemDrop?.invoke(it.entity) -// } + listen { + if (it.entity !is ItemEntity) return@listen + pendingBreaks + .firstOrNull { info -> matchesBlockItem(info, it.entity) } + ?.let { pending -> + pending.internalOnItemDrop(it.entity) + if (pending.callbacksCompleted) { + pendingBreaks.remove(pending) + } + return@listen + } + + breakingInfos + .filterNotNull() + .firstOrNull { info -> matchesBlockItem(info, it.entity) } + ?.let { info -> + info.internalOnItemDrop(it.entity) + } + } } private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { @@ -509,6 +526,32 @@ object BreakManager : RequestHandler(), PositionBlocking { var soundsCooldown = 0.0f var startedWithSecondary = false + @Volatile + private var broken = false + private var item: ItemEntity? = null + + val callbacksCompleted + @Synchronized get() = broken && (onItemDrop == null || item != null) + + fun internalOnBreak() { + synchronized(this) { + broken = true + onBreak() + item?.let { item -> + onItemDrop?.invoke(item) + } + } + } + + fun internalOnItemDrop(item: ItemEntity) { + synchronized(this) { + this.item = item + if (broken) { + onItemDrop?.invoke(item) + } + } + } + fun requestHotbarSwap() = hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 62aca7172..86b7a9aa2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -119,6 +119,7 @@ object HighwayTools : Module( emptyStructure() } }.build( + collectDrops = build.collectDrops, build = build, rotation = rotation, interact = interact, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 9b7e59936..a1b7c0cf7 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -24,7 +24,6 @@ import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext -import com.lambda.event.events.EntityEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.blueprint.Blueprint @@ -204,46 +203,37 @@ class BuildTask @Ta5kBuilder constructor( return@listen } } - - // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work - listen { - if (!collectDrops) return@listen - if (it.entity !is ItemEntity) return@listen - pendingInteractions.find { context -> - val inRange = context.expectedPos.toCenterPos().isInRange(it.entity.pos, 0.5) - val correctMaterial = context.checkedState.block == it.entity.stack.item.block - inRange && correctMaterial - }?.let { _ -> - dropsToCollect.add(it.entity) - } - } } private fun SafeContext.collectDrops() = - dropsToCollect.firstOrNull()?.let { itemDrop -> - if (!world.entities.contains(itemDrop)) { - dropsToCollect.remove(itemDrop) - BaritoneUtils.cancel() - return true - } + dropsToCollect + .firstOrNull() + ?.let { itemDrop -> + if (positionBlockingManagers.any { it.blockedPositions.isNotEmpty() }) return true + + if (!world.entities.contains(itemDrop)) { + dropsToCollect.remove(itemDrop) + BaritoneUtils.cancel() + return true + } - val noInventorySpace = player.hotbarAndStorage.none { it.isEmpty } - if (noInventorySpace) { - val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { - it.stack.item.block in TaskFlowModule.inventory.disposables - } ?: run { - failure("No item in inventory to throw but inventory is full and cant pick up item drop") + val noInventorySpace = player.hotbarAndStorage.none { it.isEmpty } + if (noInventorySpace) { + val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { + it.stack.item.block in TaskFlowModule.inventory.disposables + } ?: run { + failure("No item in inventory to throw but inventory is full and cant pick up item drop") + return true + } + transfer(player.currentScreenHandler) { + throwStack(stackToThrow.id) + }.execute(this@BuildTask) return true } - transfer(player.currentScreenHandler) { - throwStack(stackToThrow.id) - }.execute(this@BuildTask) - return true - } - BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos)) - return true - } ?: false + BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos)) + return true + } ?: false companion object { @Ta5kBuilder From ad4e8c2e48020eea8d3f69fc6c3823a5328c86bc Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 19 Mar 2025 21:08:46 +0000 Subject: [PATCH 064/364] small cleanup --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 1a9fdc64d..5d554dbb4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -247,9 +247,7 @@ object BreakManager : RequestHandler(), PositionBlocking { breakingInfos .filterNotNull() .firstOrNull { info -> matchesBlockItem(info, it.entity) } - ?.let { info -> - info.internalOnItemDrop(it.entity) - } + ?.internalOnItemDrop(it.entity) } } From 1a112fe71bc53618f299b53ab47bf9367c8959c0 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 20 Mar 2025 03:09:30 +0000 Subject: [PATCH 065/364] inlined post placement interactions --- .../request/placing/PlaceManager.kt | 92 ++++++++++++------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 0d8db619d..8ed552751 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -36,6 +36,7 @@ import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.request.rotation.RotationManager.onRotatePost import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.item import com.lambda.util.Communication.info import com.lambda.util.Communication.warn @@ -97,7 +98,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || invalidRotation) return@listen - placeBlock(request, Hand.MAIN_HAND) + val actionResult = placeBlock(request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } activeThisTick = true } } @@ -174,35 +178,19 @@ object PlaceManager : RequestHandler(), PositionBlocking { private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) = interactBlock(request, request.buildConfig.placeSettings, hand, request.placeContext.result) - private fun SafeContext.interactBlock(request: PlaceRequest, placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult) { + private fun SafeContext.interactBlock( + request: PlaceRequest, + placeConfig: PlaceConfig, + hand: Hand, + hitResult: BlockHitResult + ): ActionResult { interaction.syncSelectedSlot() - if (!world.worldBorder.contains(hitResult.blockPos)) return - - interaction.sendSequencedPacket(world) { sequence: Int -> - val stackInHand = player.getStackInHand(hand) - val stackCountPre = stackInHand.count - val actionResult = interactBlockInternal(placeConfig, hand, hitResult) - if (actionResult.isAccepted) { - if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) - request.onPlace() - else - pendingPlacements.add(request) - - if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) { - swingHand(request.buildConfig.placeSettings.swingType) - } - - if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { - mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) - } - } else { - warn("Placement interaction failed with $actionResult") - } - PlayerInteractBlockC2SPacket(hand, hitResult, sequence) - } + if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL + return interactBlockInternal(request, placeConfig, hand, hitResult) } private fun SafeContext.interactBlockInternal( + request: PlaceRequest, placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult @@ -210,27 +198,40 @@ object PlaceManager : RequestHandler(), PositionBlocking { val itemStack = player.getStackInHand(hand) if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS - // checks if the player should be able to interact with the block for if its something - // like a furnace or chest where an action would happen -// val handNotEmpty = player.getStackInHand(hand).isEmpty.not() -// val cantInteract = player.shouldCancelInteraction() && handNotEmpty -// if (!cantInteract) return ActionResult.PASS + val handNotEmpty = player.getStackInHand(hand).isEmpty.not() + val cantInteract = player.shouldCancelInteraction() && handNotEmpty + if (!cantInteract) { + val blockState = blockState(hitResult.blockPos) + if (!connection.hasFeature(blockState.block.requiredFeatures)) { + return ActionResult.FAIL + } + + // checks if the player should be able to interact with the block for if its something + // like a furnace or chest where an action would happen +// val actionResult = blockState.onUse(world, player, hand, hitResult) +// if (actionResult.isAccepted) { +// return actionResult +// } + } if (!itemStack.isEmpty && !isItemOnCooldown(itemStack.item)) { val itemUsageContext = ItemUsageContext(player, hand, hitResult) return if (gamemode.isCreative) { val i = itemStack.count - useOnBlock(placeConfig, itemStack, itemUsageContext) + useOnBlock(request, hand, hitResult, placeConfig, itemStack, itemUsageContext) .also { itemStack.count = i } } else - useOnBlock(placeConfig, itemStack, itemUsageContext) + useOnBlock(request, hand, hitResult, placeConfig, itemStack, itemUsageContext) } return ActionResult.PASS } private fun SafeContext.useOnBlock( + request: PlaceRequest, + hand: Hand, + hitResult: BlockHitResult, placeConfig: PlaceConfig, itemStack: ItemStack, context: ItemUsageContext @@ -242,12 +243,14 @@ object PlaceManager : RequestHandler(), PositionBlocking { if (cantModifyWorld && cantPlaceOn) return ActionResult.PASS val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS - val actionResult = place(placeConfig, item, ItemPlacementContext(context)) - return actionResult + return place(request, hand, hitResult, placeConfig, item, ItemPlacementContext(context)) } private fun SafeContext.place( + request: PlaceRequest, + hand: Hand, + hitResult: BlockHitResult, placeConfig: PlaceConfig, item: BlockItem, context: ItemPlacementContext @@ -258,6 +261,25 @@ object PlaceManager : RequestHandler(), PositionBlocking { val itemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL val blockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL + val stackInHand = player.getStackInHand(hand) + val stackCountPre = stackInHand.count + if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) + request.onPlace() + else + pendingPlacements.add(request) + + interaction.sendSequencedPacket(world) { sequence: Int -> + PlayerInteractBlockC2SPacket(hand, hitResult, sequence) + } + + if (request.buildConfig.placeSettings.swing) { + swingHand(request.buildConfig.placeSettings.swingType) + } + + if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + } + if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) return ActionResult.success(world.isClient) From 5b78774340d8fdb0d7cfb3584bc8aeabb0a4acee Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 20 Mar 2025 17:33:18 +0000 Subject: [PATCH 066/364] place manager cleanup --- .../request/placing/PlaceManager.kt | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 8ed552751..6d530a06e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -175,18 +175,12 @@ object PlaceManager : RequestHandler(), PositionBlocking { false } - private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) = - interactBlock(request, request.buildConfig.placeSettings, hand, request.placeContext.result) - - private fun SafeContext.interactBlock( - request: PlaceRequest, - placeConfig: PlaceConfig, - hand: Hand, - hitResult: BlockHitResult - ): ActionResult { + private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() + val hitResult = request.placeContext.result if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL - return interactBlockInternal(request, placeConfig, hand, hitResult) + if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS + return interactBlockInternal(request, request.buildConfig.placeSettings, hand, hitResult) } private fun SafeContext.interactBlockInternal( @@ -195,9 +189,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { hand: Hand, hitResult: BlockHitResult ): ActionResult { - val itemStack = player.getStackInHand(hand) - if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS - val handNotEmpty = player.getStackInHand(hand).isEmpty.not() val cantInteract = player.shouldCancelInteraction() && handNotEmpty if (!cantInteract) { @@ -214,6 +205,8 @@ object PlaceManager : RequestHandler(), PositionBlocking { // } } + val itemStack = player.getStackInHand(hand) + if (!itemStack.isEmpty && !isItemOnCooldown(itemStack.item)) { val itemUsageContext = ItemUsageContext(player, hand, hitResult) return if (gamemode.isCreative) { From 1770ca4ea7a5ea4bdeec0d96d03aa405dff2bee6 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 20 Mar 2025 18:22:14 +0000 Subject: [PATCH 067/364] air place option --- .../com/lambda/config/groups/PlaceSettings.kt | 1 + .../request/breaking/BreakManager.kt | 7 ++-- .../request/placing/PlaceConfig.kt | 7 ++++ .../request/placing/PlaceManager.kt | 37 ++++++++++++++----- .../com/lambda/util/player/PlayerUtils.kt | 8 ++-- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index bbd189a97..dd7d6dff7 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -27,6 +27,7 @@ class PlaceSettings( vis: () -> Boolean = { true } ) : PlaceConfig(priority) { override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } + override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 2, 0..5, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 5d554dbb4..8fd91e684 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -60,6 +60,7 @@ import net.minecraft.entity.ItemEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.sound.SoundCategory +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos object BreakManager : RequestHandler(), PositionBlocking { @@ -338,7 +339,7 @@ object BreakManager : RequestHandler(), PositionBlocking { info.nullify() return false } - if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info.breakConfig.swingType) + if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) return true } @@ -389,10 +390,10 @@ object BreakManager : RequestHandler(), PositionBlocking { onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } - if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType) + if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) setBreakCooldown(info.breakConfig.breakDelay) } else { - if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType) + if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) } return true diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 73bf6c305..2f0b7a30d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -25,6 +25,7 @@ abstract class PlaceConfig( priority: Priority ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean + abstract val airPlace: AirPlaceMode abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int @@ -36,6 +37,12 @@ abstract class PlaceConfig( PlaceManager.registerRequest(this, request) } + enum class AirPlaceMode { + None, + Standard, + Grim + } + enum class PlaceConfirmationMode { None, PlaceThenAwait, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 6d530a06e..49186f289 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -50,6 +50,7 @@ import net.minecraft.item.BlockItem import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket import net.minecraft.registry.RegistryKeys import net.minecraft.sound.SoundCategory @@ -57,6 +58,7 @@ import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction import net.minecraft.world.GameMode object PlaceManager : RequestHandler(), PositionBlocking { @@ -197,12 +199,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { return ActionResult.FAIL } - // checks if the player should be able to interact with the block for if its something - // like a furnace or chest where an action would happen -// val actionResult = blockState.onUse(world, player, hand, hitResult) -// if (actionResult.isAccepted) { -// return actionResult -// } + val actionResult = blockState.onUse(world, player, hand, hitResult) + if (actionResult.isAccepted) { + return actionResult + } } val itemStack = player.getStackInHand(hand) @@ -261,12 +261,16 @@ object PlaceManager : RequestHandler(), PositionBlocking { else pendingPlacements.add(request) - interaction.sendSequencedPacket(world) { sequence: Int -> - PlayerInteractBlockC2SPacket(hand, hitResult, sequence) + if (request.buildConfig.placeSettings.airPlace == PlaceConfig.AirPlaceMode.Grim) { + airPlaceOffhandSwap() + sendPlacePacket(hand, hitResult) + airPlaceOffhandSwap() + } else { + sendPlacePacket(hand, hitResult) } if (request.buildConfig.placeSettings.swing) { - swingHand(request.buildConfig.placeSettings.swingType) + swingHand(request.buildConfig.placeSettings.swingType, hand) } if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { @@ -296,6 +300,11 @@ object PlaceManager : RequestHandler(), PositionBlocking { return ActionResult.success(world.isClient) } + private fun SafeContext.sendPlacePacket(hand: Hand, hitResult: BlockHitResult) = + interaction.sendSequencedPacket(world) { sequence: Int -> + PlayerInteractBlockC2SPacket(hand, hitResult, sequence) + } + private fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) { val blockSoundGroup = state.soundGroup world.playSound( @@ -308,6 +317,16 @@ object PlaceManager : RequestHandler(), PositionBlocking { ) } + private fun SafeContext.airPlaceOffhandSwap() { + connection.sendPacket( + PlayerActionC2SPacket( + PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, + BlockPos.ORIGIN, + Direction.DOWN + ) + ) + } + override fun preEvent() = UpdateManagerEvent.Place.Pre().post() override fun postEvent() = UpdateManagerEvent.Place.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index f140850a2..5c83018f0 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -47,11 +47,11 @@ fun SafeContext.spawnFakePlayer( return entity } -fun SafeContext.swingHand(swingType: BuildConfig.SwingType) = +fun SafeContext.swingHand(swingType: BuildConfig.SwingType, hand: Hand) = when (swingType) { - BuildConfig.SwingType.Vanilla -> player.swingHand(player.activeHand) - BuildConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(player.activeHand)) - BuildConfig.SwingType.Client -> swingHandClient(player.activeHand) + BuildConfig.SwingType.Vanilla -> player.swingHand(hand) + BuildConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(hand)) + BuildConfig.SwingType.Client -> swingHandClient(hand) } fun SafeContext.swingHandClient(hand: Hand) { From 458e4a3069a1195ff9951cdf027bb3f3259e79da Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 01:36:55 +0000 Subject: [PATCH 068/364] pending interactions fixes / improvements --- .../com/lambda/config/groups/BreakSettings.kt | 4 +- .../com/lambda/config/groups/PlaceSettings.kt | 4 +- .../request/breaking/BreakConfig.kt | 5 +- .../request/breaking/BreakManager.kt | 53 ++++++++---- .../request/breaking/BreakRequest.kt | 2 + .../request/placing/PlaceManager.kt | 26 ++++-- .../request/placing/PlaceRequest.kt | 2 + .../kotlin/com/lambda/task/tasks/BuildTask.kt | 82 ++++++++----------- 8 files changed, 103 insertions(+), 75 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 44bd6bae7..e6471567f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -32,14 +32,14 @@ class BreakSettings( override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } - override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() } + override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() } override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } - override val maxPendingBreaks by c.setting("Max Pending Breaks", 2, 0..5, 1, "The maximum amount of pending breaks") { vis() } + override val maxPendingBreaks by c.setting("Max Pending Breaks", 5, 1..30, 1, "The maximum amount of pending breaks") { vis() } override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index dd7d6dff7..a4e0db560 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -29,9 +29,9 @@ class PlaceSettings( override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } - override val maxPendingPlacements by c.setting("Max Pending Placements", 2, 0..5, 1, "The maximum amount of pending placements") { vis() } + override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() } - override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() } + override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing } override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index e5f0a0e98..9f22413ed 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -57,7 +57,10 @@ abstract class BreakConfig( Constant, StartAndEnd, Start, - End + End, + None; + + fun isEnabled() = this != None } enum class BreakConfirmationMode { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 8fd91e684..7539a3fb1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -27,6 +27,7 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority @@ -77,6 +78,7 @@ object BreakManager : RequestHandler(), PositionBlocking { ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + it.pendingInteractionsList.remove(it.context) } override val blockedPositions @@ -121,7 +123,7 @@ object BreakManager : RequestHandler(), PositionBlocking { handleRequestContext( ctx, buildConfig, rotationConfig, hotbarConfig, - onBreak, onItemDrop + pendingInteractionsList, onBreak, onItemDrop ) } ?: return@request if (!breakInfo.requestHotbarSwap()) return@forEach @@ -156,6 +158,7 @@ object BreakManager : RequestHandler(), PositionBlocking { currentRequest?.let request@ { request -> val breakConfig = request.buildConfig.breakSettings val takeCount = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) + if (takeCount <= 0) return@request val validContexts = request.contexts .filter { ctx -> canAccept(ctx) } .sortedBy { it.instantBreak } @@ -174,7 +177,7 @@ object BreakManager : RequestHandler(), PositionBlocking { handleRequestContext( ctx, buildConfig, rotationConfig, hotbarConfig, - onBreak, onItemDrop + pendingInteractionsList, onBreak, onItemDrop ) } if (breakInfo == null) return@request @@ -199,7 +202,7 @@ object BreakManager : RequestHandler(), PositionBlocking { // return if the block's not broken if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) { - pendingBreaks.remove(pending) + removePendingBreak(pending) return@listen } @@ -208,7 +211,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } pending.internalOnBreak() if (pending.callbacksCompleted) { - pendingBreaks.remove(pending) + removePendingBreak(pending) } return@listen } @@ -225,10 +228,10 @@ object BreakManager : RequestHandler(), PositionBlocking { } destroyBlock(info) info.internalOnBreak() - info.nullify() if (!info.callbacksCompleted) { - pendingBreaks.add(info) + addPendingBreak(info) } + info.nullify() } } @@ -240,7 +243,7 @@ object BreakManager : RequestHandler(), PositionBlocking { ?.let { pending -> pending.internalOnItemDrop(it.entity) if (pending.callbacksCompleted) { - pendingBreaks.remove(pending) + removePendingBreak(pending) } return@listen } @@ -279,12 +282,13 @@ object BreakManager : RequestHandler(), PositionBlocking { buildConfig: BuildConfig, rotationConfig: RotationConfig, hotbarConfig: HotbarConfig, + pendingInteractionsList: MutableCollection, onBreak: () -> Unit, onItemDrop: ((ItemEntity) -> Unit)? ): BreakInfo? { val breakInfo = BreakInfo(requestCtx, BreakType.Primary, buildConfig.breakSettings, rotationConfig, hotbarConfig, - onBreak, onItemDrop + pendingInteractionsList, onBreak, onItemDrop ) primaryBreakingInfo?.let { primaryInfo -> if (!primaryInfo.breakConfig.doubleBreak @@ -312,7 +316,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } private fun setPendingInteractionsLimits(buildConfig: BuildConfig) { - pendingBreaks.setMaxSize(buildConfig.maxPendingInteractions) + pendingBreaks.setMaxSize(buildConfig.breakSettings.maxPendingBreaks) pendingBreaks.setDecayTime(buildConfig.interactionTimeout * 50L) } @@ -339,7 +343,10 @@ object BreakManager : RequestHandler(), PositionBlocking { info.nullify() return false } - if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + val swing = info.breakConfig.swing + if (swing.isEnabled() && swing != BreakConfig.SwingMode.End) { + swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + } return true } @@ -385,15 +392,17 @@ object BreakManager : RequestHandler(), PositionBlocking { setBreakingTextureStage(info) } + val swing = info.breakConfig.swing + if (progress >= info.getBreakThreshold()) { interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } - if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) setBreakCooldown(info.breakConfig.breakDelay) } else { - if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + if (swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) } return true @@ -461,19 +470,32 @@ object BreakManager : RequestHandler(), PositionBlocking { when (info.breakConfig.breakConfirmation) { BreakConfirmationMode.None -> { destroyBlock(info) - info.onBreak() + info.internalOnBreak() + if (!info.callbacksCompleted) { + addPendingBreak(info) + } } BreakConfirmationMode.BreakThenAwait -> { destroyBlock(info) - pendingBreaks.add(info) + addPendingBreak(info) } BreakConfirmationMode.AwaitThenBreak -> { - pendingBreaks.add(info) + addPendingBreak(info) } } info.nullify() } + private fun addPendingBreak(info: BreakInfo) { + pendingBreaks.add(info) + info.pendingInteractionsList.add(info.context) + } + + private fun removePendingBreak(info: BreakInfo) { + pendingBreaks.remove(info) + info.pendingInteractionsList.remove(info.context) + } + private fun SafeContext.destroyBlock(info: BreakInfo): Boolean { val ctx = info.context @@ -517,6 +539,7 @@ object BreakManager : RequestHandler(), PositionBlocking { val breakConfig: BreakConfig, val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, + val pendingInteractionsList: MutableCollection, val onBreak: () -> Unit, val onItemDrop: ((ItemEntity) -> Unit)? ) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index c3e87f73a..4f892a96a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig @@ -33,6 +34,7 @@ data class BreakRequest( val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, val prio: Priority = 0, + val pendingInteractionsList: MutableCollection, val onBreak: () -> Unit, val onItemDrop: ((ItemEntity) -> Unit)?, ) : Request(prio) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 49186f289..33ec1d324 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -67,6 +67,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { ) { info("${it::class.simpleName} at ${it.placeContext.expectedPos.toShortString()} timed out") mc.world?.setBlockState(it.placeContext.expectedPos, it.placeContext.checkedState) + it.pendingInteractionsList.remove(it.placeContext) } override val blockedPositions @@ -131,7 +132,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { request.rotationConfig.request(request.placeContext.rotation) else null - pendingPlacements.setMaxSize(request.buildConfig.maxPendingInteractions) + pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements) pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) } } @@ -149,7 +150,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { pendingPlacements .firstOrNull { it.placeContext.expectedPos == event.pos } ?.let { request -> - pendingPlacements.remove(request) + removePendingPlace(request) // return if the block wasn't placed if (!matchesTargetState(event.pos, request.placeContext.targetState, event.newState)) @@ -256,10 +257,9 @@ object PlaceManager : RequestHandler(), PositionBlocking { val stackInHand = player.getStackInHand(hand) val stackCountPre = stackInHand.count - if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) - request.onPlace() - else - pendingPlacements.add(request) + if (placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) { + addPendingPlace(request) + } if (request.buildConfig.placeSettings.airPlace == PlaceConfig.AirPlaceMode.Grim) { airPlaceOffhandSwap() @@ -297,6 +297,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { if (placeConfig.sounds) placeSound(item, hitState, blockPos) if (!player.abilities.creativeMode) itemStack.decrement(1) + if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) { + request.onPlace() + } + return ActionResult.success(world.isClient) } @@ -327,6 +331,16 @@ object PlaceManager : RequestHandler(), PositionBlocking { ) } + private fun addPendingPlace(request: PlaceRequest) { + pendingPlacements.add(request) + request.pendingInteractionsList.add(request.placeContext) + } + + private fun removePendingPlace(request: PlaceRequest) { + pendingPlacements.remove(request) + request.pendingInteractionsList.remove(request.placeContext) + } + override fun preEvent() = UpdateManagerEvent.Place.Pre().post() override fun postEvent() = UpdateManagerEvent.Place.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index 7cec84ce2..cb138d1bc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig +import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request @@ -33,6 +34,7 @@ data class PlaceRequest( val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, val interactionConfig: InteractionConfig, + val pendingInteractionsList: MutableCollection, val prio: Priority = 0, val onPlace: () -> Unit ) : Request(prio) { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index a1b7c0cf7..982a221af 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -38,25 +38,21 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer -import com.lambda.interaction.request.ManagerUtils.positionBlockingManagers -import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig -import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils -import com.lambda.util.Communication.info import com.lambda.util.Formatting.string -import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.SlotUtils.hotbarAndStorage import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos +import java.util.concurrent.ConcurrentLinkedQueue class BuildTask @Ta5kBuilder constructor( private val blueprint: Blueprint, @@ -70,9 +66,7 @@ class BuildTask @Ta5kBuilder constructor( ) : Task() { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" - private val pendingInteractions = LimitedDecayQueue( - build.maxPendingInteractions, build.interactionTimeout * 50L - ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } + private val pendingInteractions = ConcurrentLinkedQueue() private var placements = 0 private var breaks = 0 @@ -88,15 +82,6 @@ class BuildTask @Ta5kBuilder constructor( (blueprint as? PropagatingBlueprint)?.next() } - override fun SafeContext.onCancel() { -// currentInteraction?.let { ctx -> -// if (ctx !is BreakContext) return -// if (ctx.buildConfig.breakSettings.breakingTexture) { -// setBreakingTextureStage(ctx, -1) -// } -// } - } - init { onRotate { if (collectDrops()) return@onRotate @@ -104,36 +89,15 @@ class BuildTask @Ta5kBuilder constructor( // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) - TaskFlowModule.drawables = results.filterIsInstance() + TaskFlowModule.drawables = results + .filterIsInstance() .plus(pendingInteractions.toList()) // .plus(sim.goodPositions()) - val resultsNotBlocked = results.filter { result -> - when(result) { - is BreakResult.Break -> { BreakManager.blockedPositions.none { blocked -> blocked == result.blockPos } } - is PlaceResult.Place -> { PlaceManager.blockedPositions.none { blocked -> blocked == result.blockPos } } - else -> true - } - } + val resultsNotBlocked = results + .filter { result -> pendingInteractions.none { it.expectedPos == result.blockPos } } .sorted() - if (build.breakSettings.breaksPerTick > 1) { - val instantResults = resultsNotBlocked.filterIsInstance() - .filter { it.context.instantBreak } - .take(build.breakSettings.breaksPerTick) - - if (instantResults.isNotEmpty()) { - build.breakSettings.request( - BreakRequest( - instantResults.map { it.context }, build, rotation, hotbar, - onBreak = { breaks++ }, - onItemDrop = onItemDrop - ) - ) - return@onRotate - } - } - val bestResult = resultsNotBlocked.firstOrNull() ?: return@onRotate when (bestResult) { is BuildResult.Done, @@ -147,8 +111,7 @@ class BuildTask @Ta5kBuilder constructor( return@onRotate } - val managersStillRunning = positionBlockingManagers.any { it.blockedPositions.isNotEmpty() } - if (finishOnDone && !managersStillRunning) success() + if (finishOnDone) success() } is BuildResult.NotVisible, @@ -167,12 +130,33 @@ class BuildTask @Ta5kBuilder constructor( if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate when (bestResult) { is BreakResult.Break -> { - val breakContexts = resultsNotBlocked + val breakResults = resultsNotBlocked .filterIsInstance() - .map { it.context } + + if (build.breakSettings.breaksPerTick > 1) { + val takeCount = build.breakSettings + .breaksPerTick + .coerceAtMost((build.maxPendingInteractions - pendingInteractions.size)) + val instantResults = breakResults + .filter { it.context.instantBreak } + .take(takeCount.coerceAtLeast(0)) + + if (instantResults.isNotEmpty()) { + build.breakSettings.request( + BreakRequest( + instantResults.map { it.context }, build, rotation, hotbar, + pendingInteractionsList = pendingInteractions, + onBreak = { breaks++ }, + onItemDrop = onItemDrop + ) + ) + return@onRotate + } + } val request = BreakRequest( - breakContexts, build, rotation, hotbar, + breakResults.map { it.context }, build, rotation, hotbar, + pendingInteractionsList = pendingInteractions, onBreak = { breaks++ }, onItemDrop = onItemDrop ) @@ -181,7 +165,7 @@ class BuildTask @Ta5kBuilder constructor( } is PlaceResult.Place -> { build.placeSettings.request( - PlaceRequest(bestResult.context, build, rotation, hotbar, interact) { placements++ } + PlaceRequest(bestResult.context, build, rotation, hotbar, interact, pendingInteractions) { placements++ } ) } } @@ -209,7 +193,7 @@ class BuildTask @Ta5kBuilder constructor( dropsToCollect .firstOrNull() ?.let { itemDrop -> - if (positionBlockingManagers.any { it.blockedPositions.isNotEmpty() }) return true + if (pendingInteractions.isNotEmpty()) return true if (!world.entities.contains(itemDrop)) { dropsToCollect.remove(itemDrop) From 67365d39c45b3dfe2f19d8ea77f81be417837eb7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 02:29:51 +0000 Subject: [PATCH 069/364] cleanup --- .../src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 982a221af..29c29078c 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -67,6 +67,10 @@ class BuildTask @Ta5kBuilder constructor( override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" private val pendingInteractions = ConcurrentLinkedQueue() + private val emptyPendingInteractionSlots + get() = (build.maxPendingInteractions - pendingInteractions.size).coerceAtLeast(0) + private val atMaxPendingInteractions + get() = pendingInteractions.size >= build.maxPendingInteractions private var placements = 0 private var breaks = 0 @@ -127,7 +131,7 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { - if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate + if (atMaxPendingInteractions) return@onRotate when (bestResult) { is BreakResult.Break -> { val breakResults = resultsNotBlocked @@ -136,10 +140,10 @@ class BuildTask @Ta5kBuilder constructor( if (build.breakSettings.breaksPerTick > 1) { val takeCount = build.breakSettings .breaksPerTick - .coerceAtMost((build.maxPendingInteractions - pendingInteractions.size)) + .coerceAtMost(emptyPendingInteractionSlots) val instantResults = breakResults .filter { it.context.instantBreak } - .take(takeCount.coerceAtLeast(0)) + .take(takeCount) if (instantResults.isNotEmpty()) { build.breakSettings.request( From 884ac41033873f2b26d5c990c4263bd5eb1c5f2a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 02:54:03 +0000 Subject: [PATCH 070/364] stepped BreakManager priority 1 over PlaceManagers --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 7539a3fb1..fc79ba30a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -108,7 +108,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } init { - listen(priority = Int.MIN_VALUE) { + listen(priority = Int.MIN_VALUE + 1) { if (isOnBreakCooldown()) { blockBreakingCooldown-- return@listen From 98c3a3c391986ea8d524cdae5245870177eafed2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 15:47:59 +0000 Subject: [PATCH 071/364] improved sending break packets --- .../construction/context/BreakContext.kt | 21 ++++++++++--------- .../request/breaking/BreakManager.kt | 16 +++++--------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index bc5546635..daa5bc128 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -25,7 +25,8 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState -import net.minecraft.client.network.ClientPlayNetworkHandler +import net.minecraft.client.network.ClientPlayerInteractionManager +import net.minecraft.client.world.ClientWorld import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.util.hit.BlockHitResult @@ -77,22 +78,22 @@ data class BreakContext( withState(checkedState, expectedPos, sideColor, result.side) } - fun startBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = - breakPacket(Action.START_DESTROY_BLOCK, sequence, connection) + fun startBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.START_DESTROY_BLOCK, world, interaction) - fun stopBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = - breakPacket(Action.STOP_DESTROY_BLOCK, sequence, connection) + fun stopBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.STOP_DESTROY_BLOCK, world, interaction) - fun abortBreakPacket(sequence: Int, connection: ClientPlayNetworkHandler) = - breakPacket(Action.ABORT_DESTROY_BLOCK, sequence, connection) + fun abortBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.ABORT_DESTROY_BLOCK, world, interaction) - private fun breakPacket(action: Action, sequence: Int, connection: ClientPlayNetworkHandler) = - connection.sendPacket( + private fun breakPacket(action: Action, world: ClientWorld, interaction: ClientPlayerInteractionManager) = + interaction.sendSequencedPacket(world) { sequence: Int -> PlayerActionC2SPacket( action, expectedPos, result.side, sequence ) - ) + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index fc79ba30a..112565be5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -425,8 +425,6 @@ object BreakManager : RequestHandler(), PositionBlocking { if (info.breaking) return false val blockState = blockState(ctx.expectedPos) - val pendingUpdateManager = world.pendingUpdateManager.incrementSequence() - val sequence = pendingUpdateManager.sequence val notAir = !blockState.isAir if (notAir && info.breakingTicks == 0) { blockState.onBlockBreakStart(world, ctx.expectedPos, player) @@ -449,17 +447,13 @@ object BreakManager : RequestHandler(), PositionBlocking { } if (info.breakConfig.breakMode == BreakMode.Packet) { - ctx.stopBreakPacket(sequence, connection) - ctx.startBreakPacket(sequence + 1, connection) - ctx.stopBreakPacket(sequence + 1, connection) - repeat(2) { - pendingUpdateManager.incrementSequence() - } + ctx.stopBreakPacket(world, interaction) + ctx.startBreakPacket(world, interaction) + ctx.stopBreakPacket(world, interaction) } else { - ctx.startBreakPacket(sequence, connection) + ctx.startBreakPacket(world, interaction) if (breakingDelta < 1 && (breakingDelta >= 0.7 || info.breakConfig.doubleBreak)) { - ctx.stopBreakPacket(sequence + 1, connection) - pendingUpdateManager.incrementSequence() + ctx.stopBreakPacket(world, interaction) } } From 638a86c69be33ffa342f48759662c8aa4790010b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 16:23:36 +0000 Subject: [PATCH 072/364] use correct block position when setting checked state in build sim --- .../interaction/construction/simulation/BuildSimulator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index cd4735830..3b6c965e8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -299,7 +299,7 @@ object BuildSimulator { RotationRequest(lookAt(checkedHit.targetRotation, 0.001), rotation), eye.distanceTo(blockHit.pos), resultState, - blockState(blockHit.blockPos), + blockState(blockHit.blockPos.offset(blockHit.side)), player.inventory.selectedSlot, context.blockPos, target, From 56ea1bd423d81c2b5626aa82093768861db29314 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 18:07:48 +0000 Subject: [PATCH 073/364] only setblock to checked state on timeout if the block was not broken client side --- .../lambda/interaction/request/breaking/BreakManager.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 112565be5..8e785945a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -77,7 +77,9 @@ object BreakManager : RequestHandler(), PositionBlocking { TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") - mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + if (!it.broken && it.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) { + mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + } it.pendingInteractionsList.remove(it.context) } @@ -543,7 +545,8 @@ object BreakManager : RequestHandler(), PositionBlocking { var startedWithSecondary = false @Volatile - private var broken = false + var broken = false + private set private var item: ItemEntity? = null val callbacksCompleted From de28d7b783fde29c72f5268d1f61ab3b74bab51a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 22 Mar 2025 18:08:30 +0000 Subject: [PATCH 074/364] fixed air place oversight --- .../com/lambda/interaction/request/placing/PlaceManager.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 33ec1d324..2ff39453d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -262,8 +262,9 @@ object PlaceManager : RequestHandler(), PositionBlocking { } if (request.buildConfig.placeSettings.airPlace == PlaceConfig.AirPlaceMode.Grim) { + val placeHand = if (hand == Hand.MAIN_HAND) Hand.OFF_HAND else Hand.MAIN_HAND airPlaceOffhandSwap() - sendPlacePacket(hand, hitResult) + sendPlacePacket(placeHand, hitResult) airPlaceOffhandSwap() } else { sendPlacePacket(hand, hitResult) From edfa84d07b1adaa182eedf629a39477abb16a932 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 23 Mar 2025 01:48:56 +0000 Subject: [PATCH 075/364] moved resetEquipProgress into swing check in place manager --- .../com/lambda/interaction/request/placing/PlaceManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 2ff39453d..e152ed9bc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -272,10 +272,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { if (request.buildConfig.placeSettings.swing) { swingHand(request.buildConfig.placeSettings.swingType, hand) - } - if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { - mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) + } } if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) From a78b69b1cf4c9d2f1c38e4a5d20d0a0656f2bd4a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 27 Mar 2025 03:33:50 +0000 Subject: [PATCH 076/364] basic air place logic in the build sim for a start --- .../construction/simulation/BuildSimulator.kt | 25 +++++++++++++------ .../request/placing/PlaceConfig.kt | 4 ++- .../rotation/visibilty/PointSelection.kt | 8 +++--- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 3b6c965e8..d1c912750 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -34,6 +34,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.request.placing.PlaceConfig import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig @@ -72,6 +73,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d +import net.minecraft.util.shape.VoxelShapes import kotlin.jvm.optionals.getOrNull import kotlin.math.pow @@ -87,11 +89,11 @@ object BuildSimulator { checkRequirements(pos, target, build)?.let { return@flatMap setOf(it) } - checkPlaceResults(pos, target, eye, interact, rotation, inventory).let { + checkPlaceResults(pos, target, eye, build.placeSettings, interact, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } - checkBreakResults(pos, eye, interact, rotation, inventory, build).let { + checkBreakResults(pos, eye, build.placeSettings, interact, rotation, inventory, build).let { if (it.isEmpty()) return@let return@flatMap it } @@ -148,6 +150,7 @@ object BuildSimulator { pos: BlockPos, target: TargetState, eye: Vec3d, + place: PlaceConfig, interact: InteractionConfig, rotation: RotationConfig, inventory: InventoryConfig @@ -160,10 +163,15 @@ object BuildSimulator { val preprocessing = target.findProcessorForState() preprocessing.sides.forEach { neighbor -> - val hitPos = if (targetPosState.isAir || targetPosState.isLiquid) pos.offset(neighbor) else pos + val hitPos = if (!place.airPlace.isEnabled() && targetPosState.isAir || targetPosState.isLiquid) + pos.offset(neighbor) + else pos val hitSide = neighbor.opposite - val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos) + val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> + if (!outlineShape.isEmpty || !place.airPlace.isEnabled()) outlineShape + else VoxelShapes.fullCube() + } if (voxelShape.isEmpty) return@forEach val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } @@ -220,7 +228,7 @@ object BuildSimulator { // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { setPos(eye.x, eye.y - standingEyeHeight, eye.z) - this.rotation = checkedHit.targetRotation + if (place.rotateForPlace) this.rotation = checkedHit.targetRotation } val checkedResult = checkedHit.hit @@ -329,6 +337,7 @@ object BuildSimulator { private fun SafeContext.checkBreakResults( pos: BlockPos, eye: Vec3d, + place: PlaceConfig, interact: InteractionConfig, rotation: RotationConfig, inventory: InventoryConfig, @@ -356,7 +365,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye, interact, rotation, inventory) + val submerge = checkPlaceResults(pos, TargetState.Solid, eye, place, interact, rotation, inventory) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -371,9 +380,9 @@ object BuildSimulator { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { - checkPlaceResults(liquidPos, TargetState.Solid, eye, interact, rotation, inventory) + checkPlaceResults(liquidPos, TargetState.Solid, eye, place, interact, rotation, inventory) } else { - checkBreakResults(liquidPos, eye, interact, rotation, inventory, build) + checkBreakResults(liquidPos, eye, place, interact, rotation, inventory, build) } acc.addAll(submerge) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 2f0b7a30d..55514a15d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -40,7 +40,9 @@ abstract class PlaceConfig( enum class AirPlaceMode { None, Standard, - Grim + Grim; + + fun isEnabled() = this != None } enum class PlaceConfirmationMode { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt index 39caf5c0c..761d7fda4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt @@ -28,9 +28,11 @@ enum class PointSelection(val select: (MutableList } }), Optimum( optimum@ { hits -> - val optimum = hits.map { it.hit.pos }.reduceOrNull { acc, vec3d -> - acc.add(vec3d) - }?.multiply(1.0 / hits.size.toDouble()) ?: return@optimum null + val optimum = hits + .map { it.hit.pos }.reduceOrNull { acc, vec3d -> + acc?.let { it.add(vec3d) } ?: acc + } + ?.multiply(1.0 / hits.size.toDouble()) ?: return@optimum null hits.minByOrNull { it.hit.pos distSq optimum From b406de4a61482d08dca39d93c3c456d4fe838f17 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 27 Mar 2025 03:42:32 +0000 Subject: [PATCH 077/364] removed PacketMine and slightly cleaned up FastBreak --- .../lambda/module/modules/player/FastBreak.kt | 41 +- .../module/modules/player/PacketMine.kt | 1480 ----------------- 2 files changed, 10 insertions(+), 1511 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt index 04fd3e50a..9b821ddfc 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt @@ -152,13 +152,8 @@ object FastBreak : Module( val dynamicAABB = DynamicAABB() dynamicAABB.update(renderBox) - if (renderSetting != RenderSetting.Outline) { - renderer.buildFilled(dynamicAABB, fillColour) - } - - if (renderSetting != RenderSetting.Fill) { - renderer.buildOutline(dynamicAABB, outlineColour) - } + if (renderSetting != RenderSetting.Outline) renderer.buildFilled(dynamicAABB, fillColour) + if (renderSetting != RenderSetting.Fill) renderer.buildOutline(dynamicAABB, outlineColour) } renderer.upload() } @@ -166,34 +161,18 @@ object FastBreak : Module( private fun getLerpBox(box: Box, factor: Float): Box { val boxCenter = Box(box.center, box.center) - when (renderMode) { - RenderMode.Out -> { - return lerp(factor.toDouble(), boxCenter, box) - } - - RenderMode.In -> { - return lerp(factor.toDouble(), box, boxCenter) - } - + return when (renderMode) { + RenderMode.Out -> lerp(factor.toDouble(), boxCenter, box) + RenderMode.In -> lerp(factor.toDouble(), box, boxCenter) RenderMode.InOut -> { - return if (factor >= 0.5f) { - lerp((factor.toDouble() - 0.5) * 2, boxCenter, box) - } else { - lerp(factor.toDouble() * 2, box, boxCenter) - } + if (factor >= 0.5f) lerp((factor.toDouble() - 0.5) * 2, boxCenter, box) + else lerp(factor.toDouble() * 2, box, boxCenter) } - RenderMode.OutIn -> { - return if (factor >= 0.5f) { - lerp((factor.toDouble() - 0.5) * 2, box, boxCenter) - } else { - lerp(factor.toDouble() * 2, boxCenter, box) - } - } - - else -> { - return box + if (factor >= 0.5f) lerp((factor.toDouble() - 0.5) * 2, box, boxCenter) + else lerp(factor.toDouble() * 2, boxCenter, box) } + else -> box } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt deleted file mode 100644 index c903ad52e..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ /dev/null @@ -1,1480 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.module.modules.player - -import com.lambda.Lambda.mc -import com.lambda.context.SafeContext -import com.lambda.event.events.* -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.DynamicAABB -import com.lambda.graphics.renderer.esp.builders.buildFilled -import com.lambda.graphics.renderer.esp.builders.buildOutline -import com.lambda.graphics.renderer.esp.global.DynamicESP -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.lookAtBlock -import com.lambda.module.Module -import com.lambda.module.modules.client.TaskFlowModule -import com.lambda.module.tag.ModuleTag -import com.lambda.util.BlockUtils.blockState -import com.lambda.util.math.lerp -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap -import net.minecraft.block.BlockState -import net.minecraft.enchantment.EnchantmentHelper -import net.minecraft.enchantment.Enchantments -import net.minecraft.entity.effect.StatusEffectUtil -import net.minecraft.entity.effect.StatusEffects -import net.minecraft.fluid.WaterFluid -import net.minecraft.item.ItemStack -import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket -import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action -import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket -import net.minecraft.registry.tag.FluidTags -import net.minecraft.screen.slot.SlotActionType -import net.minecraft.state.property.Properties -import net.minecraft.util.Hand -import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.hit.HitResult -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Box -import net.minecraft.util.math.Direction -import net.minecraft.util.math.Vec3d -import java.awt.Color -import java.util.function.Supplier - -object PacketMine : Module( - name = "Packet Mine", - description = "Mines blocks semi-automatically at a faster rate", - defaultTags = setOf(ModuleTag.PLAYER) -) { - private val page by setting("Page", Page.General) - - private val breakThreshold by setting("Break Threshold", 0.70f, 0.00f..1.00f, 0.01f, "Breaks the selected block once the block breaking progress passes this value, 1 being 100%", visibility = { page == Page.General}) - private val breakMode by setting("Break Mode", BreakMode.Total, "Changes the way break amount is added up. Total will choose the best tool and act as if its been using it the whole time while additive will add progress throughout the break", visibility = { page == Page.General }) - private val strict by setting("Strict", false, "Resets the breaking progress at various stages to bypass generally stricter anti-cheats", visibility = { page == Page.General }) - private val range by setting("Range", 6.0f, 3.0f..6.0f, 0.1f, "The maximum distance between the players eye position and the center of the block", " blocks", visibility = { page == Page.General }) - private val doubleBreak by setting("Double Break", false, "This exploit only works on non strict servers or servers that run grim. It breaks two blocks at once", visibility = { page == Page.General }) - private val pauseWhileUsingItems by setting("Pause While Using Items", true, "Will prevent breaking while using items like eating or aiming a bow", visibility = { page == Page.General }) - private val validateBreak by setting("Validate Break", true, "Breaks blocks client side rather than waiting for a response from the server", visibility = { page == Page.General }) - private val timeoutDelay by setting("Timeout Delay", 0.20f, 0.00f..1.00f, 0.1f, "Will wait this amount of time (seconds) after the time to break for the block is complete before moving on", " seconds", visibility = { page == Page.General && validateBreak }) - private val swingMode by setting("Swing Mode", ModeOptions.None, "Swings the players hand to simulate vanilla breaking, usually used on stricter anti-cheats", visibility = { page == Page.General }) - private val swingOnManual by setting("Manual Swing", true, "Swings when the player attacks a block", visibility = { page == Page.General }) - private val rotate by setting("Rotation Mode", ModeOptions.None, "Changes the method used to make the player look at the current mining block", visibility = { page == Page.General }) - private val rayCast by setting("Raycast", false, "Checks if the player is directly looking at the block rather than allowing through walls", visibility = { page == Page.General && rotate.isEnabled() }) - private val rotateReleaseDelay by setting("Rotation Release Delay", 1, 0..5, 1, "The number of ticks to wait before releasing the rotation", " ticks", visibility = { page == Page.General && rotate.isEnabled() }) - private val swapMethod by setting("Swap Method", SwapMethod.StandardSilent, "Changes the swap method used. For example, silent swaps once at the beginning, and once at the end without updating client side, and constant swaps for the whole break", visibility = { page == Page.General}) - private val swapMode by setting("Swap Mode", SwapMode.StartAndEnd, "The different times to swap to the best tool", visibility = { page == Page.General && swapMethod.isEnabled()}) - private val packets by setting("Packet Mode", PacketMode.Vanilla, "Chooses different packets to send for each mode", visibility = { page == Page.General }) - - private val reBreak by setting("Re-Break", ReBreakMode.Standard, "The different modes for re-breaking the current block", visibility = { page == Page.ReBreak}) - private val reBreakDelay by setting("Re-Break Delay", 0, 0..10, 1, "The delay between attempting to re-breaking the block", "ticks", visibility = { page == Page.ReBreak && (reBreak.isAutomatic() || reBreak.isFastAutomatic()) }) - private val emptyReBreakDelay by setting("Empty Re-Break Delay", 0, 0..10, 1, "The delay between attempting to re-break the block if the block is currently empty", " ticks", visibility = { page == Page.ReBreak && reBreak.isFastAutomatic()}) - private val renderIfEmpty by setting("Render If Empty", false, "Draws the renders even if the re-break position is empty in the world", visibility = { page == Page.ReBreak && reBreak.isEnabled() }) - - private val queueBlocks by setting("Queue Blocks", false, "Queues any blocks you click for breaking", visibility = { page == Page.Queue }) - .onValueSet { _, to -> if (!to) blockQueue.clear() } - - private val reverseQueueOrder by setting("Reverse Queue Order", false, "Breaks the latest addition to the queue first", visibility = { page == Page.Queue && queueBlocks}) - private val queueBreakDelay by setting("Break Delay", 0, 0..5, 1, "The delay after breaking a block to break the next queue block", " ticks", visibility = { page == Page.Queue && queueBlocks }) - - - private val breakingAnimation by setting("Breaking Animation", false, "Renders the block breaking animation like vanilla would to show progress", visibility = { page == Page.BlockRender }) - private val renderMode by setting("BlockRender Mode", RenderMode.Out, "The animation style of the renders", visibility = { page == Page.BlockRender }) - private val renderSetting by setting("BlockRender Setting", RenderSetting.Both, "The different ways to draw the renders", visibility = { page == Page.BlockRender && renderMode.isEnabled() }) - - private val fillColourMode by setting("Fill Mode", ColourMode.Dynamic, visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Outline }) - private val staticFillColour by setting("Static Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static fill of the box faces", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Static }) - private val startFillColour by setting("Start Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start fill of the box faces", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic }) - private val endFillColour by setting("End Fill Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end fill of the box faces", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic }) - - private val outlineColourMode by setting("Outline Mode", ColourMode.Dynamic, visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Fill }) - private val staticOutlineColour by setting("Static Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the outline of the box", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Static }) - private val startOutlineColour by setting("Start Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start outline of the box", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }) - private val endOutlineColour by setting("End Outline Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end outline of the box", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }) - private val outlineWidth by setting("Outline Width", 1f, 0f..3f, 0.1f, "the thickness of the outline", visibility = { page == Page.BlockRender && renderMode.isEnabled() && renderSetting != RenderSetting.Fill }) - - private val renderQueueMode by setting("Render mode", RenderQueueMode.Cube, "The render type for queue blocks", visibility = { page == Page.QueueRender }) - private val renderQueueSetting by setting("Render Setting", RenderSetting.Both, "The style to render queue blocks", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() }) - private val renderQueueSize by setting("Render Size", 0.3f, 0f..1f, 0.01f, "The scale of the queue render blocks", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() }) - - private val queueFillColourMode by setting("Queue Fill Mode", ColourMode.Dynamic, visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Outline }) - private val queueStaticFillColour by setting("Queue Static Fill Colour", Color(1f, 0f, 0f, 0.2f), "The colour used to render the faces for queue blocks", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Outline && queueFillColourMode == ColourMode.Static }) - private val queueStartFillColour by setting("Queue Start Fill Colour", Color(1f, 0f, 0f, 0.2f), "The colour to render the faces for queue blocks closer to being broken next", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Outline && queueFillColourMode == ColourMode.Dynamic }) - private val queueEndFillColour by setting("Queue End Fill Colour", Color(1f, 1f, 0f, 0.2f), "the colour to render the faces for queue blocks closer to the end of the queue", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Outline && queueFillColourMode == ColourMode.Dynamic }) - - private val queueOutlineColourMode by setting("Queue Outline Mode", ColourMode.Dynamic, visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Fill }) - private val queueStaticOutlineColour by setting("Queue Static Outline Colour", Color(1f, 0f, 0f), "The colour used to render the outline for queue blocks", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Fill && queueOutlineColourMode == ColourMode.Static }) - private val queueStartOutlineColour by setting("Queue Start Outline Colour", Color(1f, 0f, 0f), "The colour to render the outline for queue blocks closer to being broken next", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Fill && queueOutlineColourMode == ColourMode.Dynamic }) - private val queueEndOutlineColour by setting("Queue End Outline Colour", Color(1f, 1f, 0f), "the colour to render the outline for queue blocks closer to the end of the queue", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderQueueSetting != RenderSetting.Fill && queueOutlineColourMode == ColourMode.Dynamic }) - private val queueOutlineWidth by setting("Queue Outline Width", 1f, 0f..3f, 0.1f, "The thickness of the outline used on queue blocks", visibility = { page == Page.QueueRender && renderQueueMode.isEnabled() && renderSetting != RenderSetting.Fill } ) - - private enum class Page { - General, ReBreak, Queue, BlockRender, QueueRender - } - - @Suppress("UNUSED") - private enum class BreakMode { - Total, Additive; - - fun isTotal() = - this == Total - } - - private enum class PacketMode { - Vanilla, Grim, NCP - } - - @Suppress("UNUSED") - private enum class SwapMethod { - None, StandardSilent, NCPSilent, Vanilla; - - fun isEnabled() = - this != None - - fun isSilent() = - this == StandardSilent || this == NCPSilent - - fun isStandardSilent() = - this == StandardSilent - - fun isNCPSilent() = - this == NCPSilent - } - - private enum class SwapMode { - StartAndEnd, Start, End, Constant; - - fun isConstant() = - this == Constant - - fun isStart() = - this == Start - - fun isEnd() = - this == End - - fun isStartAndEnd() = - this == StartAndEnd - } - - private enum class ReBreakMode { - None, Standard, Automatic, FastAutomatic; - - fun isEnabled() = - this != None - - fun isStandard() = - this == Standard - - fun isAutomatic() = - this == Automatic - - fun isFastAutomatic() = - this == FastAutomatic - } - - private enum class ModeOptions { - None, StartAndEnd, Start, End, Constant; - - fun isEnabled() = - this != None - - fun isStartAndEnd() = - this == StartAndEnd - - fun isStart() = - this == Start - - fun isEnd() = - this == End - - fun isConstant() = - this == Constant - } - - private enum class ProgressStage { - StartPre, StartPost, PreTick, During, EndPre, EndPost, PacketReceiveBreak, TimedOut - } - - private enum class RenderMode { - None, Out, In, InOut, OutIn, Static; - - fun isEnabled() = - this != None - } - - @Suppress("UNUSED") - private enum class RenderQueueMode { - None, Cube, Shape; - - fun isEnabled() = - this != None - } - - private enum class RenderSetting { - Both, Fill, Outline - } - - private enum class ColourMode { - Static, Dynamic - } - - private enum class BreakState { - Breaking, ReBreaking, AwaitingResponse - } - - private enum class BreakType { - Primary, Double; - - fun isPrimary() = - this == Primary - } - - val renderer = DynamicESP - private var currentMiningBlock = Array(2) { null } - private var lastNonEmptyState: BlockState? = null - private val blockQueue = ArrayDeque() - private var queueBreakStartCounter = 0 - private var awaitingQueueBreak = false - private var returnSlot = -1 - private var swappedSlot = -1 - private var swapped = false - private var previousSelectedSlot = -1 - private var expectedRotation: RotationRequest? = null - private var rotationPosition: BlockPos? = null - private var pausedForRotation = false - private var releaseRotateDelayCounter = 0 - private var reBreakDelayCounter = 0 - private var emptyReBreakDelayCounter = 0 - private var rotated = false - private var onRotationComplete: Runnable? = null - private var waitingToReleaseRotation = false - private var cancelNextSwing = false - private var breaksPerTickCounter = 0 - private var swingingNextAttack = true - private var doubleBreakSwapped = false - private var doubleBreakSwappedCounter = 0 - private var doubleBreakReturnSlot = 0 - - init { - listen { - swingingNextAttack = false - } - - listen { - it.cancel() - if (swingOnManual) swingMainHand() - - if (swingingNextAttack) { - cancelNextSwing = true - } else { - swingingNextAttack = true - } - - if (queueBlocks) { - if (shouldBePlacedInBlockQueue(it.pos)) { - if (reverseQueueOrder) { - blockQueue.addFirst(it.pos) - } else { - blockQueue.add(it.pos) - } - return@listen - } - - if (blockQueue.contains(it.pos)) return@listen - } - - currentMiningBlock.forEach { ctx -> - ctx?.apply { - if (it.pos != pos) return@forEach - - val primary = breakType.isPrimary() - - if (!primary || (breakState == BreakState.ReBreaking && !reBreak.isStandard())) return@listen - - if (miningProgress < breakThreshold) return@listen - - runBetweenHandlers(ProgressStage.EndPre, ProgressStage.EndPost, pos, { lastValidBestTool }) { - packetStopBreak(pos) - - onBlockBreak() - } - - return@listen - } - } - - currentMiningBlock[0]?.apply { - - currentMiningBlock[1]?.run { return@apply } - - if (doubleBreak && breakState != BreakState.ReBreaking) { - switchCurrentMiningBlockToDouble() - } - } - - startBreaking(it.pos) - } - - listen { - if (!cancelNextSwing) return@listen - - cancelNextSwing = false - it.cancel() - } - - listen(1) { - updateCounters() - - if (shouldWaitForQueuePause()) return@listen - - currentMiningBlock.forEach { ctx -> - ctx?.apply { - - mineTicks++ - - val activeState = blockState(pos) - state = activeState - - val empty = isStateEmpty(activeState) - - val bestTool = if (breakType.isPrimary()) { - lastNonEmptyState?.run { - getBestTool(this, pos) - } ?: player.inventory.selectedSlot - } else { - getBestTool(state, pos) - } - - if (breakType.isPrimary()) { - if (!empty) { - if (activeState != lastNonEmptyState?.block) { - lastNonEmptyState?.apply { - transformProgress(block.hardness / activeState.block.hardness) - } - } - - lastNonEmptyState = activeState - } - - lastNonEmptyState?.let { - lastValidBestTool = bestTool - } - - runHandlers(ProgressStage.PreTick, pos, lastValidBestTool, empty) - - if (strict - && !swapped - && player.inventory.selectedSlot != previousSelectedSlot - ) { - resetProgress() - currentMiningBlock[1]?.apply { - resetProgress() - } - } - previousSelectedSlot = player.inventory.selectedSlot - } - - if ((pauseWhileUsingItems && player.isUsingItem) || pausedForRotation) { - return@forEach - } - - currentBreakDelta = if (swapMethod.isEnabled()) { - if (breakType.isPrimary()) { - calcBreakDelta(state, pos, lastValidBestTool) - } else { - calcBreakDelta(state, pos, bestTool) - } - } else { - calcBreakDelta(state, pos, player.inventory.selectedSlot) - } - - updateBreakDeltas(currentBreakDelta) - - if (renderMode.isEnabled()) updateRenders() - - if (breakingAnimation - && breakType.isPrimary() - ) - world.setBlockBreakingInfo( - player.id, - pos, - (miningProgress * (2 - breakThreshold) * 10).toInt().coerceAtMost(9) - ) - - when (breakState) { - BreakState.Breaking -> { - val threshold = if (breakType.isPrimary()) breakThreshold else 1f - - if (miningProgress < threshold) return@forEach - - timeCompleted = System.currentTimeMillis() - - if (breakType.isPrimary()) { - runBetweenHandlers( - ProgressStage.EndPre, - ProgressStage.EndPost, - pos, - { lastValidBestTool }) { - packetStopBreak(pos) - - onBlockBreak(doubleBreakBlock = false) - } - return@forEach - } - - doubleBreakSwapTo(bestTool) - onBlockBreak(doubleBreakBlock = true) - } - - BreakState.ReBreaking -> { - if (miningProgress < breakThreshold) { - runHandlers(ProgressStage.During, pos, lastValidBestTool, empty = empty) - return@forEach - } - - if (isOutOfRange(pos.toCenterPos()) || !reBreak.isEnabled()) { - nullifyCurrentBreakingBlock(false) - return@forEach - } - - if (breakNextQueueBlock() || reBreak.isStandard()) return@forEach - - if (empty) { - if (emptyReBreakDelayCounter > 0) return@forEach - emptyReBreakDelayCounter = emptyReBreakDelay - } else { - if (reBreakDelayCounter > 0) return@forEach - reBreakDelayCounter = reBreakDelay - } - - if (!reBreak.isFastAutomatic() && empty) { - return@forEach - } - - runBetweenHandlers( - ProgressStage.EndPre, - ProgressStage.EndPost, - pos, - { lastValidBestTool }, - empty = empty - ) { - packetStopBreak(pos) - - onBlockBreak() - } - } - - BreakState.AwaitingResponse -> { - if (!validateBreak) { - if (breakType.isPrimary()) { - runHandlers(ProgressStage.EndPost, pos, lastValidBestTool) - } - onBlockBreak(doubleBreakBlock = !breakType.isPrimary()) - return@forEach - } - - if (System.currentTimeMillis() - timeCompleted < timeoutDelay * 1000) return@forEach - - val primary = breakType.isPrimary() - - if (!primary) nullifyCurrentBreakingBlock(true) - if (breakNextQueueBlock()) return@forEach - - if (primary) { - runHandlers(ProgressStage.TimedOut, pos, lastValidBestTool) - } - - nullifyCurrentBreakingBlock(!breakType.isPrimary()) - } - } - } - } - } - - listen { - if (doubleBreakSwapped && doubleBreakSwappedCounter >= 1) { - returnToOriginalDoubleBreakSlot() - } - } - - listen { - currentMiningBlock.forEach { ctx -> - ctx?.apply { - if (it.pos != pos || !isStateBroken(blockState(pos), it.newState)) return@forEach - - if (breakType.isPrimary()) { - runHandlers(ProgressStage.PacketReceiveBreak, pos, lastValidBestTool) - } else { - if (doubleBreakSwapped) { - returnToOriginalDoubleBreakSlot() - } - } - - onBlockBreak(packetReceiveBreak = true, doubleBreakBlock = !breakType.isPrimary()) - } - } - } - - onRotate { - if (!rotate.isEnabled()) return@onRotate - - rotationPosition?.let { pos -> - lastNonEmptyState?.let { state -> - expectedRotation = lookAtBlock(pos).requestBy(TaskFlowModule.rotation) - } - } ?: run { - expectedRotation = null - } - - if (!rotated || !waitingToReleaseRotation) return@onRotate - - releaseRotateDelayCounter-- - - if (releaseRotateDelayCounter <= 0) { - waitingToReleaseRotation = false - rotationPosition = null - rotated = false - } - } - - listen { - if (!rotate.isEnabled()) return@listen - - expectedRotation?.let { expectedRot -> - rotationPosition?.let { pos -> - if (it.request != expectedRot) { - pausedForRotation = true - return@listen - } - - val boxList = lastNonEmptyState?.getOutlineShape(world, pos)?.boundingBoxes?.map { it.offset(pos) } - if (verifyRotation( - boxList, - RotationManager.currentRotation.vector, - RotationManager.currentRequest?.target?.hit?.hitIfValid() - ) - ) { - onRotationComplete?.run() - onRotationComplete = null - pausedForRotation = false - } else { - pausedForRotation = true - } - - return@listen - } - } - - onRotationComplete = null - pausedForRotation = false - } - - listen { - renderer.clear() - - currentMiningBlock.forEach { ctx -> - ctx?.run { buildRenders() } - } - - if (renderQueueMode.isEnabled()) { - blockQueue.forEach { pos -> - var boxes = if (renderQueueMode == RenderQueueMode.Shape) { - blockState(pos).getOutlineShape(world, pos).boundingBoxes - } else { - listOf(Box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)) - } - boxes = boxes.map { - val reSized = lerp(renderQueueSize.toDouble(), Box(it.center, it.center), it); reSized.offset( - pos - ) - } - - val indexFactor = blockQueue.indexOf(pos).toDouble() / blockQueue.size.toDouble() - - val fillColour = if (queueFillColourMode == ColourMode.Static) { - queueStaticFillColour - } else { - lerp(indexFactor, queueStartFillColour, queueEndFillColour) - } - - val outlineColour = if (queueOutlineColourMode == ColourMode.Static) { - queueStaticOutlineColour - } else { - lerp(indexFactor, queueStartOutlineColour, queueEndOutlineColour) - } - - boxes.forEach { box -> - val dynamicAABB = DynamicAABB() - dynamicAABB.update(box) - - if (renderQueueSetting != RenderSetting.Outline) { - renderer.buildFilled(dynamicAABB, fillColour) - } - - if (renderQueueSetting != RenderSetting.Fill) { - renderer.buildOutline(dynamicAABB, outlineColour) - } - } - } - } - - renderer.upload() - } - - onDisable { - currentMiningBlock[0] = null - currentMiningBlock[1] = null - lastNonEmptyState = null - blockQueue.clear() - queueBreakStartCounter = 0 - awaitingQueueBreak = false - returnSlot = -1 - swappedSlot = -1 - swapped = false - previousSelectedSlot = -1 - expectedRotation = null - rotationPosition = null - pausedForRotation = false - releaseRotateDelayCounter = 0 - reBreakDelayCounter = 0 - emptyReBreakDelayCounter = 0 - rotated = false - onRotationComplete = null - waitingToReleaseRotation = false - cancelNextSwing = false - breaksPerTickCounter = 0 - swingingNextAttack = true - doubleBreakSwapped = false - doubleBreakSwappedCounter = 0 - doubleBreakReturnSlot = 0 - } - } - - private fun SafeContext.startBreaking(pos: BlockPos) { - val state = blockState(pos) - val bestTool = getBestTool(state, pos) - if (!isStateEmpty(state)) lastNonEmptyState = state - - val breakDelta = if (swapMethod.isEnabled()) { - calcBreakDelta(state, pos, bestTool) - } else { - calcBreakDelta(state, pos, player.inventory.selectedSlot) - } - val instaBreak = breakDelta >= breakThreshold - - previousSelectedSlot = player.inventory.selectedSlot - - runBetweenHandlers( - ProgressStage.StartPre, - ProgressStage.StartPost, - pos, - { bestTool }, - instaBroken = instaBreak - ) { - packetStartBreak(pos) - - currentMiningBlock[0] = BreakingContext(pos, state, BreakState.Breaking, breakDelta, bestTool) - - if (!instaBreak) return@runBetweenHandlers - - packetStopBreak(pos) - - onBlockBreak() - } - } - - private fun SafeContext.runBetweenHandlers( - preStage: ProgressStage, - postStage: ProgressStage, - pos: BlockPos, - bestTool: Supplier, - empty: Boolean = false, - instaBroken: Boolean = false, - task: Runnable, - ) { - handleRotations(preStage, pos, empty = empty, instaBroken = instaBroken) - - val postRotationTask = Runnable { - handleAutoSwap(preStage, bestTool.get(), empty = empty, instaBroken = instaBroken) - handleSwing(preStage, empty = empty, instaBroken = instaBroken) - task.run() - runHandlers(postStage, pos, bestTool.get(), empty = empty, instaBroken = instaBroken) - } - - if (pausedForRotation) { - onRotationComplete = postRotationTask - } else { - postRotationTask.run() - } - } - - private fun SafeContext.runHandlers( - progressStage: ProgressStage, - pos: BlockPos, - bestTool: Int, - empty: Boolean = false, - instaBroken: Boolean = false, - ) { - handleRotations(progressStage, pos, empty = empty, instaBroken = instaBroken) - handleAutoSwap(progressStage, bestTool, empty = empty, instaBroken = instaBroken) - handleSwing(progressStage, empty = empty, instaBroken = instaBroken) - } - - private fun SafeContext.handleRotations( - progressStage: ProgressStage, - pos: BlockPos, - empty: Boolean = false, - instaBroken: Boolean = false, - ) { - when (progressStage) { - ProgressStage.PreTick -> { - if (rotate.isConstant() - && !rotated - && !empty - && (currentMiningBlock[0]?.breakState != BreakState.ReBreaking - || !reBreak.isStandard() - ) - ) { - rotateTo(pos) - } - } - - ProgressStage.StartPre -> if (rotate.isEnabled() && (!rotate.isEnd() || instaBroken)) rotateTo(pos) - - ProgressStage.EndPre -> if (rotate.isEnabled() && !rotate.isStart()) rotateTo(pos) - - ProgressStage.During -> if (rotate.isConstant() && !empty && !reBreak.isStandard()) rotateTo(pos) - - ProgressStage.StartPost -> if ((instaBroken && !validateBreak) || (!instaBroken && (rotate.isStart() || rotate.isStartAndEnd()))) checkReleaseRotation() - - ProgressStage.EndPost -> if (!validateBreak || empty) checkReleaseRotation() - - ProgressStage.PacketReceiveBreak, - ProgressStage.TimedOut, - -> checkReleaseRotation() - } - } - - private fun SafeContext.handleAutoSwap( - progressStage: ProgressStage, - bestTool: Int, - empty: Boolean = false, - instaBroken: Boolean = false, - ) { - if (!swapMethod.isEnabled()) return - - when (progressStage) { - ProgressStage.PreTick -> { - if (!swapped) return - - when { - player.inventory.selectedSlot != swappedSlot -> resetSwap() - - swapMode.isConstant() -> { - if (swappedSlot != bestTool) swapTo(bestTool) - } - } - - if (swapMode.isStartAndEnd()) returnToOriginalSlot() - } - - ProgressStage.StartPre -> if (!swapped && (!swapMode.isEnd() || instaBroken)) swapTo(bestTool) - - ProgressStage.EndPre -> if (!swapped && !swapMode.isStart()) swapTo(bestTool) - - ProgressStage.During -> if (swapped && swapMode.isConstant() && swappedSlot != bestTool) swapTo(bestTool) - - ProgressStage.StartPost -> { - if (!swapped) return - - if ((instaBroken && !validateBreak) - || (swapMethod.isSilent() && !swapMode.isConstant()) - ) { - returnToOriginalSlot() - } - } - - ProgressStage.EndPost -> { - if (!swapped) return - - if ((!validateBreak || empty) - || (swapMethod.isSilent() && !swapMode.isConstant()) - ) { - returnToOriginalSlot() - } - } - - ProgressStage.PacketReceiveBreak, - ProgressStage.TimedOut, - -> returnToOriginalSlot() - } - } - - private fun SafeContext.handleSwing( - progressStage: ProgressStage, - empty: Boolean = false, - instaBroken: Boolean = false, - ) { - if (!swingMode.isEnabled()) return - - when (progressStage) { - ProgressStage.PreTick -> { - currentMiningBlock[0]?.apply { - if (swingMode.isConstant() && breakState == BreakState.Breaking) { - swingMainHand() - } - } - } - - ProgressStage.During -> if (swingMode.isConstant() && (!empty || reBreak.isFastAutomatic())) swingMainHand() - - ProgressStage.StartPre -> if (!swingMode.isEnd() || instaBroken) swingMainHand() - - ProgressStage.EndPre -> if (!swingMode.isStart()) swingMainHand() - - ProgressStage.EndPost, - ProgressStage.StartPost, - ProgressStage.PacketReceiveBreak, - ProgressStage.TimedOut, - -> { - } - } - } - - private fun SafeContext.swingMainHand() { - player.swingHand(Hand.MAIN_HAND, false) - connection.sendPacket(HandSwingC2SPacket(Hand.MAIN_HAND)) - } - - private fun updateCounters() { - breaksPerTickCounter = 0 - - if (reBreakDelayCounter > 0) { - reBreakDelayCounter-- - } - if (emptyReBreakDelayCounter > 0) { - emptyReBreakDelayCounter-- - } - if (queueBreakStartCounter > 0) { - queueBreakStartCounter-- - } - if (doubleBreakSwapped) { - doubleBreakSwappedCounter++ - } - } - - private fun switchCurrentMiningBlockToDouble() { - currentMiningBlock[1] = currentMiningBlock[0] - currentMiningBlock[0] = null - currentMiningBlock[1]?.breakType = BreakType.Double - } - - private fun SafeContext.rotateTo(pos: BlockPos) { - waitingToReleaseRotation = false - releaseRotateDelayCounter = rotateReleaseDelay - rotationPosition = pos - rotated = true - if (!verifyRotation( - lastNonEmptyState?.getOutlineShape(world, pos)?.boundingBoxes?.map { it.offset(pos) }, - RotationManager.currentRotation.vector, - RotationManager.currentRequest?.target?.hit?.hitIfValid() - ) - ) { - pausedForRotation = true - } - } - - private fun SafeContext.verifyRotation(boxes: Collection?, lookVec: Vec3d, hitResult: HitResult?): Boolean { - if (rayCast && (hitResult?.type == HitResult.Type.ENTITY || (hitResult as BlockHitResult?)?.blockPos != rotationPosition)) { - return false - } - - boxes?.let { - return it.any { box -> - lookIntersectsBox(player.eyePos, lookVec, box) - } - } ?: return false - } - - private fun lookIntersectsBox(start: Vec3d, direction: Vec3d, box: Box): Boolean { - val invDirX = 1.0 / direction.x - val invDirY = 1.0 / direction.y - val invDirZ = 1.0 / direction.z - - val tMinX = if (invDirX >= 0) (box.minX - start.x) * invDirX else (box.maxX - start.x) * invDirX - val tMaxX = if (invDirX >= 0) (box.maxX - start.x) * invDirX else (box.minX - start.x) * invDirX - - val tMinY = if (invDirY >= 0) (box.minY - start.y) * invDirY else (box.maxY - start.y) * invDirY - val tMaxY = if (invDirY >= 0) (box.maxY - start.y) * invDirY else (box.minY - start.y) * invDirY - - val tMinZ = if (invDirZ >= 0) (box.minZ - start.z) * invDirZ else (box.maxZ - start.z) * invDirZ - val tMaxZ = if (invDirZ >= 0) (box.maxZ - start.z) * invDirZ else (box.minZ - start.z) * invDirZ - - val tMin = maxOf(tMinX, tMinY, tMinZ) - val tMax = minOf(tMaxX, tMaxY, tMaxZ) - - return tMax >= tMin && tMax > 0 - } - - private fun checkReleaseRotation() { - if (!rotated || waitingToReleaseRotation) return - - if (releaseRotateDelayCounter <= 0) { - rotationPosition = null - rotated = false - } else { - waitingToReleaseRotation = true - } - } - - private fun SafeContext.swapTo(slot: Int) { - if (swapped && swapMethod.isNCPSilent()) returnToOriginalSlot() - - if (returnSlot == -1) { - returnSlot = if (!doubleBreakSwapped) { - player.inventory.selectedSlot - } else { - doubleBreakReturnSlot - } - } - - if (swapMethod.isSilent()) { - silentSwapTo(slot, false) - } else { - player.inventory.selectedSlot = slot - connection.sendPacket(UpdateSelectedSlotC2SPacket(slot)) - } - - swappedSlot = slot - swapped = true - } - - private fun SafeContext.silentSwapTo(slot: Int, returningToOriginalSlot: Boolean) { - if (swapMethod.isStandardSilent()) { - connection.sendPacket(UpdateSelectedSlotC2SPacket(slot)) - return - } - - val screenHandler = player.playerScreenHandler - var itemStack = player.mainHandStack - var newSlot = slot - var fromSlot = player.inventory.selectedSlot - - if (returningToOriginalSlot) { - newSlot = swappedSlot - itemStack = player.inventory.getStack(returnSlot) - fromSlot = returnSlot - } - - connection.sendPacket( - ClickSlotC2SPacket( - screenHandler.syncId, - screenHandler.revision, - newSlot + 36, - fromSlot, - SlotActionType.SWAP, - itemStack, - Int2ObjectArrayMap() - ) - ) - } - - private fun SafeContext.returnToOriginalSlot() { - if (!swapped || returnSlot == -1) return - - if (swapMethod.isSilent()) { - silentSwapTo(returnSlot, true) - } else { - player.inventory.selectedSlot = returnSlot - connection.sendPacket(UpdateSelectedSlotC2SPacket(returnSlot)) - } - - if (doubleBreakSwapped && (!swapMethod.isSilent() || swapMode.isConstant())) { - doubleBreakSwapped = false - doubleBreakReturnSlot = 0 - } - - resetSwap() - - return - } - - private fun SafeContext.doubleBreakSwapTo(slot: Int) { - val currentSelectedSlot = player.inventory.selectedSlot - - if (!doubleBreakSwapped) { - doubleBreakReturnSlot = currentSelectedSlot - } - - if (slot != currentSelectedSlot) { - player.inventory.selectedSlot = slot - connection.sendPacket(UpdateSelectedSlotC2SPacket(slot)) - } - - doubleBreakSwappedCounter = 0 - doubleBreakSwapped = true - } - - private fun SafeContext.returnToOriginalDoubleBreakSlot() { - if (!doubleBreakSwapped) return - - if (doubleBreakReturnSlot != player.inventory.selectedSlot) { - player.inventory.selectedSlot = doubleBreakReturnSlot - connection.sendPacket(UpdateSelectedSlotC2SPacket(doubleBreakReturnSlot)) - } - - doubleBreakSwapped = false - } - - private fun resetSwap() { - returnSlot = -1 - swappedSlot = -1 - swapped = false - } - - private fun SafeContext.isOutOfRange(vec: Vec3d) = - player.eyePos.distanceTo(vec) > range - - private fun SafeContext.onBlockBreak(packetReceiveBreak: Boolean = false, doubleBreakBlock: Boolean = false) { - val block = if (doubleBreakBlock) 1 else 0 - - currentMiningBlock[block]?.apply { - if (!isOutOfRange(pos.toCenterPos()) || packetReceiveBreak) { - checkClientSideBreak(packetReceiveBreak, pos, doubleBreakBlock = doubleBreakBlock) - } - - if (timeCompleted == -1L) { - timeCompleted = System.currentTimeMillis() - } - - if (validateBreak && breakState == BreakState.Breaking) { - breakState = BreakState.AwaitingResponse - return - } - - if (!breakType.isPrimary() && (breakState == BreakState.AwaitingResponse || !validateBreak)) { - nullifyCurrentBreakingBlock(true) - } - - breaksPerTickCounter++ - - queueBreakStartCounter = queueBreakDelay - if (breakNextQueueBlock(doubleBreakBlock = doubleBreakBlock)) return - - if (reBreak.isEnabled() && !isOutOfRange(pos.toCenterPos()) && breakType.isPrimary()) { - if (breakState != BreakState.ReBreaking) { - breakState = BreakState.ReBreaking - } - - if (strict) resetProgress() - return - } - - nullifyCurrentBreakingBlock(doubleBreakBlock) - } - } - - private fun SafeContext.nullifyCurrentBreakingBlock(doubleBreakBlock: Boolean) { - val block = if (doubleBreakBlock) 1 else 0 - - if (!doubleBreakBlock) { - currentMiningBlock[block]?.apply { - if (breakingAnimation) world.setBlockBreakingInfo(player.id, pos, -1) - runHandlers(ProgressStage.TimedOut, pos, lastValidBestTool) - } - } - - currentMiningBlock[block] = null - } - - private fun isStateBroken(previousState: BlockState?, activeState: BlockState): Boolean { - previousState?.let { previous -> - if (isStateEmpty(previous)) return false - - return activeState.isAir || ( - activeState.fluidState.fluid is WaterFluid - && !activeState.properties.contains(Properties.WATERLOGGED) - && previous.properties.contains(Properties.WATERLOGGED) - && previous.get(Properties.WATERLOGGED) - ) - } ?: return false - } - - private fun isStateEmpty(state: BlockState) = - state.isAir || ( - (!state.properties.contains(Properties.WATERLOGGED) - || !state.get(Properties.WATERLOGGED)) - && !state.fluidState.isEmpty - ) - - private fun SafeContext.checkClientSideBreak( - packetReceiveBreak: Boolean, - pos: BlockPos, - doubleBreakBlock: Boolean = false, - ) { - if (packetReceiveBreak || (!validateBreak && !doubleBreakBlock)) { - interaction.breakBlock(pos) - } - } - - private fun shouldBePlacedInBlockQueue(pos: BlockPos): Boolean = - (((currentMiningBlock[0] != null && !doubleBreak) || currentMiningBlock[1] != null) || !blockQueue.isEmpty()) - && (currentMiningBlock[0]?.breakState != BreakState.ReBreaking || !blockQueue.isEmpty()) - && currentMiningBlock[0]?.pos != pos - && currentMiningBlock[1]?.pos != pos - && !blockQueue.contains(pos) - - private fun SafeContext.breakNextQueueBlock(doubleBreakBlock: Boolean = false): Boolean { - if (!queueBlocks || currentMiningBlock.any { it?.timeCompleted == -1L }) return false - - var startedBreakingAtLeastOne = false - - while (true) { - filterBlockQueueUntilNextPossible()?.let { block -> - currentMiningBlock[1]?.run { - return startedBreakingAtLeastOne - } - - val requiresAnotherTickDelay = doubleBreakBlock && !validateBreak && queueBreakDelay <= 0 && doubleBreak - - if (queueBreakStartCounter > 0 || breaksPerTickCounter >= 1 || requiresAnotherTickDelay) { - if (requiresAnotherTickDelay) queueBreakStartCounter++ - awaitingQueueBreak = true - return true - } - - currentMiningBlock[0]?.apply { - if (timeCompleted != -1L || breakState == BreakState.ReBreaking) return@apply - - if (!doubleBreak) return startedBreakingAtLeastOne - - switchCurrentMiningBlockToDouble() - } - - blockQueue.remove(block) - startBreaking(block) - - if (!doubleBreak || startedBreakingAtLeastOne) { - return true - } - - startedBreakingAtLeastOne = true - } ?: return startedBreakingAtLeastOne - } - } - - private fun SafeContext.filterBlockQueueUntilNextPossible(): BlockPos? { - while (true) { - val block = blockQueue.firstOrNull() ?: return null - - if (isOutOfRange(block.toCenterPos())) { - blockQueue.remove(block) - continue - } - - return block - } - } - - private fun SafeContext.shouldWaitForQueuePause(): Boolean { - if (awaitingQueueBreak) { - if (queueBreakStartCounter > 0) return true - - awaitingQueueBreak = false - if (breakNextQueueBlock()) return true - } - - return false - } - - private class BreakingContext( - val pos: BlockPos, - var state: BlockState, - var breakState: BreakState, - var currentBreakDelta: Float, - var lastValidBestTool: Int, - ) { - var mineTicks = 0 - var breakType = BreakType.Primary - var additiveBreakDelta = currentBreakDelta - var timeCompleted: Long = -1 - var previousBreakDelta = 0f - var lastLerpedBoxes: HashSet = hashSetOf() - var lastLerpFillColour: Color? = null - var lastLerpOutlineColour: Color? = null - - var boxList = if (renderMode.isEnabled()) { - state.getOutlineShape(mc.world, pos).boundingBoxes.toSet() - } else { - null - } - - val miningProgress: Float - get() = if (breakMode.isTotal()) { - mineTicks * currentBreakDelta - } else { - additiveBreakDelta - } - - val previousMiningProgress: Float - get() = if (breakMode.isTotal()) { - (mineTicks - 1) * previousBreakDelta - } else { - additiveBreakDelta - currentBreakDelta - } - - fun resetProgress() { - mineTicks = 0 - additiveBreakDelta = 0f - } - - fun transformProgress(percentage: Float) { - additiveBreakDelta *= percentage - } - - fun updateBreakDeltas(newBreakDelta: Float) { - previousBreakDelta = currentBreakDelta - currentBreakDelta = newBreakDelta - additiveBreakDelta += newBreakDelta - } - - fun updateRenders() { - boxList = if (breakType.isPrimary()) { - lastNonEmptyState?.getOutlineShape(mc.world, pos)?.boundingBoxes?.toSet() - } else { - state.getOutlineShape(mc.world, pos)?.boundingBoxes?.toSet() - } - } - - fun SafeContext.buildRenders() { - if (!renderIfEmpty && isStateEmpty(state)) return - - val threshold = if (breakType.isPrimary()) { - 2f - breakThreshold - } else { - 1f - } - val previousFactor = previousMiningProgress * threshold - val nextFactor = miningProgress * threshold - val currentFactor = lerp(mc.tickDelta, previousFactor, nextFactor) - - val paused = (pauseWhileUsingItems && player.isUsingItem) || pausedForRotation || awaitingQueueBreak - - val fillColour = if (fillColourMode == ColourMode.Dynamic) { - val lerpColour = lerp(currentFactor.toDouble(), startFillColour, endFillColour) - if (!paused) { - lastLerpFillColour = lerpColour - lerpColour - } else { - lastLerpFillColour ?: startFillColour - } - } else { - staticFillColour - } - - val outlineColour = if (outlineColourMode == ColourMode.Dynamic) { - val lerpColour = lerp(currentFactor.toDouble(), startOutlineColour, endOutlineColour) - if (!paused) { - lastLerpOutlineColour = lerpColour - lerpColour - } else { - lastLerpOutlineColour ?: startOutlineColour - } - } else { - staticOutlineColour - } - - if (paused) { - lastLerpedBoxes.forEach { box -> - val dynamicAABB = DynamicAABB() - dynamicAABB.update(box) - - if (renderSetting != RenderSetting.Outline) { - renderer.buildFilled(dynamicAABB, fillColour) - } - - if (renderSetting != RenderSetting.Fill) { - renderer.buildOutline(dynamicAABB, outlineColour) - } - } - - return - } - - lastLerpedBoxes.clear() - - boxList?.forEach { box -> - val positionedBox = box.offset(pos) - - val renderBox = if (renderMode == RenderMode.Static) { - positionedBox - } else { - getLerpBox(positionedBox, currentFactor) - } - - lastLerpedBoxes.add(renderBox) - - val dynamicAABB = DynamicAABB() - dynamicAABB.update(renderBox) - - if (renderSetting != RenderSetting.Outline) { - renderer.buildFilled(dynamicAABB, fillColour) - } - - if (renderSetting != RenderSetting.Fill) { - renderer.buildOutline(dynamicAABB, outlineColour) - } - } - } - - private fun getLerpBox(box: Box, factor: Float): Box { - val boxCenter = Box(box.center, box.center) - when (renderMode) { - RenderMode.Out -> { - return lerp(factor.toDouble(), boxCenter, box) - } - - RenderMode.In -> { - return lerp(factor.toDouble(), box, boxCenter) - } - - RenderMode.InOut -> { - return if (factor >= 0.5f) { - lerp((factor.toDouble() - 0.5) * 2, boxCenter, box) - } else { - lerp(factor.toDouble() * 2, box, boxCenter) - } - } - - RenderMode.OutIn -> { - return if (factor >= 0.5f) { - lerp((factor.toDouble() - 0.5) * 2, box, boxCenter) - } else { - lerp(factor.toDouble() * 2, boxCenter, box) - } - } - - else -> { - return box - } - } - } - } - - private fun SafeContext.packetStartBreak(pos: BlockPos) { - if (packets == PacketMode.Grim) { - abortBreak(pos) - stopBreak(pos) - } - startBreak(pos) - if (packets == PacketMode.NCP) abortBreak(pos) - if (packets == PacketMode.Grim || doubleBreak) { - stopBreak(pos) - } - } - - private fun SafeContext.packetStopBreak(pos: BlockPos) { - stopBreak(pos) - if (packets == PacketMode.NCP) { - abortBreak(pos) - startBreak(pos) - stopBreak(pos) - } - } - - private fun SafeContext.startBreak(pos: BlockPos) { - connection.sendPacket(PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, pos, Direction.UP)) - } - - private fun SafeContext.stopBreak(pos: BlockPos) { - connection.sendPacket(PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, pos, Direction.UP)) - } - - private fun SafeContext.abortBreak(pos: BlockPos) { - connection.sendPacket(PlayerActionC2SPacket(Action.ABORT_DESTROY_BLOCK, pos, Direction.UP)) - } - - //Todo: Replace with task system - - private fun SafeContext.getBestTool(state: BlockState, pos: BlockPos): Int { - val selectedSlot = player.inventory.selectedSlot - var bestTool = selectedSlot - var bestTimeToMine = calcBreakDelta(state, pos, selectedSlot) - for (i in 0..8) { - if (i == selectedSlot) continue - val currentToolsTimeToMine = calcBreakDelta(state, pos, i) - if (currentToolsTimeToMine > bestTimeToMine) { - bestTimeToMine = currentToolsTimeToMine - bestTool = i - } - } - return bestTool - } - - private fun SafeContext.calcBreakDelta(state: BlockState, pos: BlockPos, toolSlot: Int): Float { - val f: Float = state.getHardness(world, pos) - if (f == -1.0f) { - return 0.0f - } else { - val i = if (!state.isToolRequired || player.inventory.getStack(toolSlot).isSuitableFor(state)) 30 else 100 - return getBlockBreakingSpeed(state, toolSlot) / f / i.toFloat() - } - } - - private fun SafeContext.getBlockBreakingSpeed(state: BlockState, toolSlot: Int): Float { - var f: Float = player.inventory.getStack(toolSlot).getMiningSpeedMultiplier(state) - if (f > 1.0f) { - val itemStack: ItemStack = player.inventory.getStack(toolSlot) - val i = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, itemStack) - if (i > 0 && !itemStack.isEmpty) { - f += (i * i + 1).toFloat() - } - } - - if (StatusEffectUtil.hasHaste(player)) { - f *= 1.0f + (StatusEffectUtil.getHasteAmplifier(player) + 1).toFloat() * 0.2f - } - - if (player.hasStatusEffect(StatusEffects.MINING_FATIGUE)) { - val g = when (player.getStatusEffect(StatusEffects.MINING_FATIGUE)?.amplifier) { - 0 -> 0.3f - 1 -> 0.09f - 2 -> 0.0027f - 3 -> 8.1E-4f - else -> 8.1E-4f - } - f *= g - } - - if (player.isSubmergedIn(FluidTags.WATER) - && !EnchantmentHelper.hasAquaAffinity(player) - ) { - f /= 5.0f - } - - if (!player.isOnGround) { - f /= 5.0f - } - - return f - } -} From 5e9b892f219ba2c4b2abcec7315f4c82d9a1df1f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 27 Mar 2025 16:39:04 +0000 Subject: [PATCH 078/364] cleaned up break manager a bit and raised max pending interactions cap / default value --- .../com/lambda/config/groups/BuildSettings.kt | 2 +- .../request/breaking/BreakManager.kt | 74 +++++-------------- 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 53751b74b..b9210e391 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -35,7 +35,7 @@ class BuildSettings( override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.General } override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.General && pathing } override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.General } - override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } + override val maxPendingInteractions by c.setting("Max Pending Interactions", 10, 1..30, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking override val breakSettings = BreakSettings(c) { page == Page.Break && vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 8e785945a..b96f9c5dd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -27,17 +27,14 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode -import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceManager -import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.request.rotation.RotationManager.onRotatePost import com.lambda.interaction.request.rotation.RotationRequest @@ -121,13 +118,7 @@ object BreakManager : RequestHandler(), PositionBlocking { if (instantBreaks.isEmpty()) return@request instantBreaks.forEach { ctx -> - val breakInfo = with(request) { - handleRequestContext( - ctx, - buildConfig, rotationConfig, hotbarConfig, - pendingInteractionsList, onBreak, onItemDrop - ) - } ?: return@request + val breakInfo = handleRequestContext(ctx, request) ?: return@request if (!breakInfo.requestHotbarSwap()) return@forEach updateBlockBreakingProgress(breakInfo) activeThisTick = true @@ -152,7 +143,7 @@ object BreakManager : RequestHandler(), PositionBlocking { onRotate(priority = Int.MIN_VALUE) { preEvent() - if (!updateRequest { true }) { + if (!updateRequest()) { requestRotate() return@onRotate } @@ -175,14 +166,7 @@ object BreakManager : RequestHandler(), PositionBlocking { validContexts .filter { it.instantBreak.not() } .forEach { ctx -> - val breakInfo = with(request) { - handleRequestContext( - ctx, - buildConfig, rotationConfig, hotbarConfig, - pendingInteractionsList, onBreak, onItemDrop - ) - } - if (breakInfo == null) return@request + if (handleRequestContext(ctx, request) == null) return@request } } @@ -281,17 +265,9 @@ object BreakManager : RequestHandler(), PositionBlocking { private fun handleRequestContext( requestCtx: BreakContext, - buildConfig: BuildConfig, - rotationConfig: RotationConfig, - hotbarConfig: HotbarConfig, - pendingInteractionsList: MutableCollection, - onBreak: () -> Unit, - onItemDrop: ((ItemEntity) -> Unit)? + request: BreakRequest ): BreakInfo? { - val breakInfo = BreakInfo(requestCtx, BreakType.Primary, - buildConfig.breakSettings, rotationConfig, hotbarConfig, - pendingInteractionsList, onBreak, onItemDrop - ) + val breakInfo = BreakInfo(requestCtx, BreakType.Primary, request) primaryBreakingInfo?.let { primaryInfo -> if (!primaryInfo.breakConfig.doubleBreak || primaryInfo.startedWithSecondary @@ -308,12 +284,12 @@ object BreakManager : RequestHandler(), PositionBlocking { secondaryBreakingInfo = primaryInfo primaryBreakingInfo = breakInfo - setPendingInteractionsLimits(buildConfig) + setPendingInteractionsLimits(request.buildConfig) return primaryBreakingInfo } primaryBreakingInfo = breakInfo - setPendingInteractionsLimits(buildConfig) + setPendingInteractionsLimits(request.buildConfig) return primaryBreakingInfo } @@ -384,10 +360,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } if (info.breakConfig.particles) { - mc.particleManager.addBlockBreakingParticles( - ctx.expectedPos, - hitResult.side - ) + mc.particleManager.addBlockBreakingParticles(ctx.expectedPos, hitResult.side) } if (info.breakConfig.breakingTexture) { @@ -517,11 +490,7 @@ object BreakManager : RequestHandler(), PositionBlocking { info: BreakInfo, stage: Int = info.getBreakTextureProgress(player, world) ) { - world.setBlockBreakingInfo( - player.id, - info.context.expectedPos, - stage - ) + world.setBlockBreakingInfo(player.id, info.context.expectedPos, stage) } private fun isOnBreakCooldown() = blockBreakingCooldown > 0 @@ -532,13 +501,15 @@ object BreakManager : RequestHandler(), PositionBlocking { data class BreakInfo( val context: BreakContext, var type: BreakType, - val breakConfig: BreakConfig, - val rotationConfig: RotationConfig, - val hotbarConfig: HotbarConfig, - val pendingInteractionsList: MutableCollection, - val onBreak: () -> Unit, - val onItemDrop: ((ItemEntity) -> Unit)? + val request: BreakRequest ) { + val breakConfig = request.buildConfig.breakSettings + val rotationConfig = request.rotationConfig + private val hotbarConfig = request.hotbarConfig + val pendingInteractionsList = request.pendingInteractionsList + private val onBreak = request.onBreak + private val onItemDrop = request.onItemDrop + var breaking = false var breakingTicks = 0 var soundsCooldown = 0.0f @@ -575,21 +546,14 @@ object BreakManager : RequestHandler(), PositionBlocking { hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { - val breakDelta = context.checkedState.calcItemBlockBreakingDelta( - player, - world, - context.expectedPos, - player.mainHandStack - ) - + val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) val progress = (breakDelta * breakingTicks) / breakConfig.breakThreshold return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } fun nullify() = type.nullify() - fun getBreakThreshold() = - type.getBreakThreshold(breakConfig) + fun getBreakThreshold() = type.getBreakThreshold(breakConfig) } enum class BreakType(val index: Int) { From 19512071af35218930819c694cc1945982aed7f6 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 28 Mar 2025 02:08:35 +0000 Subject: [PATCH 079/364] more air place build sim checks --- .../construction/simulation/BuildSimulator.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index d1c912750..f86a954ec 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -191,7 +191,8 @@ object BuildSimulator { } scanSurfaces(box, sides, interact.resolution, preprocessing.surfaceScan) { _, vec -> - if (eye distSq vec > reachSq) { + val distSquared = eye distSq vec + if (distSquared > reachSq) { misses.add(vec) return@scanSurfaces } @@ -199,7 +200,18 @@ object BuildSimulator { val newRotation = eye.rotationTo(vec) val hit = if (interact.strictRayCast) { - newRotation.rayCast(interact.interactReach, eye)?.blockResult + val rayCast = newRotation.rayCast(interact.interactReach, eye) + when { + rayCast != null && (!place.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> + rayCast.blockResult + + place.airPlace.isEnabled() -> { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, hitSide, hitPos, false) + } + + else -> null + } } else { val hitVec = newRotation.castBox(box, interact.interactReach, eye) BlockHitResult(hitVec, hitSide, hitPos, false) From 58aef678e04d056ada4cb27897ae932cb5df4d2e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 03:28:51 +0000 Subject: [PATCH 080/364] axis rotations for air placing rotational blocks --- .../com/lambda/config/groups/PlaceSettings.kt | 3 ++ .../construction/simulation/BuildSimulator.kt | 49 ++++++++++++++++--- .../request/placing/PlaceConfig.kt | 2 + .../com/lambda/util/player/PlayerUtils.kt | 16 +++++- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index a4e0db560..56df18afc 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -28,6 +28,9 @@ class PlaceSettings( ) : PlaceConfig(priority) { override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } + override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } + override val axisRotate + get() = airPlace.isEnabled() && axisRotateSetting override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index f86a954ec..f8e006dc3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -58,6 +58,7 @@ import com.lambda.util.math.distSq import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.copyPlayer import com.lambda.util.player.gamemode +import com.lambda.util.player.placementRotations import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition @@ -292,18 +293,52 @@ object BuildSimulator { context = checked } - val resultState = blockItem.getPlacementState(context) ?: run { + var resultState = blockItem.getPlacementState(context) ?: run { acc.add(PlaceResult.BlockedByEntity(pos)) return@forEach } + var rot = checkedHit.targetRotation - if (!target.matches(resultState, pos, world)) { - acc.add( - PlaceResult.NoIntegrity( + val simulatePlaceState = placeState@ { + resultState = blockItem.getPlacementState(context) + ?: return@placeState PlaceResult.BlockedByEntity(pos) + + if (!target.matches(resultState, pos, world)) { + return@placeState PlaceResult.NoIntegrity( pos, resultState, context, (target as? TargetState.State)?.blockState ) - ) - return@forEach + } else { + rot = fakePlayer.rotation + return@placeState null + } + } + + if (!place.axisRotate && place.rotateForPlace) { + simulatePlaceState()?.let { placeResult -> + acc.add(placeResult) + return@forEach + } + } else if (place.rotateForPlace) run axisRotate@ { + placementRotations.forEachIndexed direction@ { index, angle -> + fakePlayer.rotation = angle + + when (val placeResult = simulatePlaceState()) { + is PlaceResult.BlockedByEntity -> { + acc.add(placeResult) + return@forEach + } + + is PlaceResult.NoIntegrity -> { + if (index != placementRotations.lastIndex) return@direction + acc.add(placeResult) + return@forEach + } + + else -> { + return@axisRotate + } + } + } } val blockHit = checkedResult.blockResult ?: return@forEach @@ -316,7 +351,7 @@ object BuildSimulator { val placeContext = PlaceContext( eye, blockHit, - RotationRequest(lookAt(checkedHit.targetRotation, 0.001), rotation), + RotationRequest(lookAt(rot, 0.001), rotation), eye.distanceTo(blockHit.pos), resultState, blockState(blockHit.blockPos.offset(blockHit.side)), diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 55514a15d..baf72f0cb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -26,6 +26,8 @@ abstract class PlaceConfig( ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean abstract val airPlace: AirPlaceMode + protected abstract val axisRotateSetting: Boolean + abstract val axisRotate: Boolean abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index 5c83018f0..1fadcb531 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -2,6 +2,8 @@ package com.lambda.util.player import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.yaw import com.mojang.authlib.GameProfile import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.network.OtherClientPlayerEntity @@ -10,6 +12,7 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket import net.minecraft.util.Hand +import net.minecraft.util.math.Direction val SafeContext.gamemode get() = interaction.currentGameMode @@ -62,4 +65,15 @@ fun SafeContext.swingHandClient(hand: Hand) { } } -fun SafeContext.isItemOnCooldown(item: Item) = player.itemCooldownManager.isCoolingDown(item) \ No newline at end of file +fun SafeContext.isItemOnCooldown(item: Item) = player.itemCooldownManager.isCoolingDown(item) + +val placementRotations = Direction.entries + .filter { it.axis.isHorizontal } + .flatMap { dir -> + val yaw = dir.yaw + listOf( + Rotation(yaw, 0f), + Rotation(yaw, -90f), + Rotation(yaw, 90f) + ) + } From 239930d0ff60e109b68f4099805874677b79cd15 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 04:13:51 +0000 Subject: [PATCH 081/364] small fix to the setting checks --- .../interaction/construction/simulation/BuildSimulator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index f8e006dc3..2642666f3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -297,7 +297,7 @@ object BuildSimulator { acc.add(PlaceResult.BlockedByEntity(pos)) return@forEach } - var rot = checkedHit.targetRotation + var rot = fakePlayer.rotation val simulatePlaceState = placeState@ { resultState = blockItem.getPlacementState(context) @@ -313,7 +313,7 @@ object BuildSimulator { } } - if (!place.axisRotate && place.rotateForPlace) { + if (!place.axisRotate) { simulatePlaceState()?.let { placeResult -> acc.add(placeResult) return@forEach From adb17510025a67da075067887d404b4b5f779377 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 14:51:38 +0000 Subject: [PATCH 082/364] reset break manager on connect --- .../lambda/interaction/request/breaking/BreakManager.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index b96f9c5dd..026d31432 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -21,11 +21,13 @@ import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post +import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking @@ -239,6 +241,12 @@ object BreakManager : RequestHandler(), PositionBlocking { .firstOrNull { info -> matchesBlockItem(info, it.entity) } ?.internalOnItemDrop(it.entity) } + + listenUnsafe { + breakingInfos.forEach { it?.nullify() } + pendingBreaks.clear() + setBreakCooldown(0) + } } private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { From df78c775ca8aa39e17d66606fb74887f3c720c40 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 15:35:47 +0000 Subject: [PATCH 083/364] fix settings checks --- .../src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt | 2 +- .../interaction/construction/simulation/BuildSimulator.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 56df18afc..69ff90631 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -30,7 +30,7 @@ class PlaceSettings( override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } override val axisRotate - get() = airPlace.isEnabled() && axisRotateSetting + get() = rotateForPlace && airPlace.isEnabled() && axisRotateSetting override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 2642666f3..d7964f232 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -318,7 +318,7 @@ object BuildSimulator { acc.add(placeResult) return@forEach } - } else if (place.rotateForPlace) run axisRotate@ { + } else run axisRotate@ { placementRotations.forEachIndexed direction@ { index, angle -> fakePlayer.rotation = angle From 8dad01edefd712a878a580850b60e7bc07e5fcbc Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 17:54:24 +0000 Subject: [PATCH 084/364] fixed break manager rotations --- .../interaction/construction/context/BreakContext.kt | 6 +++--- .../interaction/construction/context/BuildContext.kt | 2 -- .../interaction/construction/context/PlaceContext.kt | 2 +- .../interaction/construction/simulation/BuildSimulator.kt | 8 ++------ .../lambda/interaction/request/breaking/BreakManager.kt | 2 +- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index daa5bc128..b82e63386 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -22,7 +22,7 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.RotationTarget import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.client.network.ClientPlayerInteractionManager @@ -38,7 +38,7 @@ import java.awt.Color data class BreakContext( override val pov: Vec3d, override val result: BlockHitResult, - override val rotation: RotationRequest, + val rotation: RotationTarget, override var checkedState: BlockState, override val targetState: TargetState, override var hotbarIndex: Int, @@ -64,7 +64,7 @@ data class BreakContext( override fun compareTo(other: BuildContext): Int { return when (other) { is BreakContext -> compareBy { - it.rotation.target.angleDistance + it.rotation.angleDistance }.compare(this, other) else -> 1 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 4801e0337..494a72cb9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -21,7 +21,6 @@ import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.util.hit.BlockHitResult @@ -37,7 +36,6 @@ interface BuildContext : Comparable, Drawable { val expectedPos: BlockPos val checkedState: BlockState val hotbarIndex: Int - val rotation: RotationRequest fun shouldRotate(config: BuildConfig): Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 9b93971ae..042dc8b8b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -35,7 +35,7 @@ import java.awt.Color data class PlaceContext( override val pov: Vec3d, override val result: BlockHitResult, - override val rotation: RotationRequest, + val rotation: RotationRequest, override val distance: Double, override val expectedState: BlockState, override val checkedState: BlockState, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index d7964f232..2d5073dc6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -457,13 +457,10 @@ object BuildSimulator { /* the player is buried inside the block */ if (boxes.any { it.contains(eye) }) { currentCast?.blockResult?.let { blockHit -> - val rotationRequest = RotationRequest( - lookAtBlock(pos, config = interact), rotation - ) val breakContext = BreakContext( eye, blockHit, - rotationRequest, + lookAtBlock(pos, config = interact), state, targetState, player.inventory.selectedSlot, @@ -511,11 +508,10 @@ object BuildSimulator { val bestHit = interact.pointSelection.select(validHits) ?: return acc val blockHit = bestHit.hit.blockResult ?: return acc val target = lookAt(bestHit.targetRotation, 0.001) - val request = RotationRequest(target, rotation) val instant = instantBreakable(state, pos, build.breakSettings.breakThreshold) val breakContext = BreakContext( - eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant + eye, blockHit, target, state, targetState, player.inventory.selectedSlot, instant ) if (gamemode.isCreative) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 026d31432..ede605140 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -267,7 +267,7 @@ object BreakManager : RequestHandler(), PositionBlocking { .filterNotNull() .firstOrNull { it.breakConfig.rotateForBreak } ?.let { info -> - info.rotationConfig.request(info.context.rotation) + info.rotationConfig.request(RotationRequest(info.context.rotation, info.rotationConfig)) } } From 7ea2794da398129950271f712e49c73704c60b4b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 18:42:45 +0000 Subject: [PATCH 085/364] check standard rotation before axis rotation --- .../interaction/construction/simulation/BuildSimulator.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 2d5073dc6..1c1041afd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -313,12 +313,11 @@ object BuildSimulator { } } - if (!place.axisRotate) { - simulatePlaceState()?.let { placeResult -> + simulatePlaceState()?.let simulatePlaceState@ { placeResult -> + if (!place.axisRotate) { acc.add(placeResult) return@forEach } - } else run axisRotate@ { placementRotations.forEachIndexed direction@ { index, angle -> fakePlayer.rotation = angle @@ -335,7 +334,7 @@ object BuildSimulator { } else -> { - return@axisRotate + return@simulatePlaceState } } } From 17ee6fd84dba9e999a7318eea3a50766385d0f39 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 29 Mar 2025 19:28:20 +0000 Subject: [PATCH 086/364] allow disabling Rotate For Place while axis rotations are enabled to ignore needless rotation --- .../kotlin/com/lambda/config/groups/PlaceSettings.kt | 2 +- .../construction/simulation/BuildSimulator.kt | 12 +++++------- .../interaction/request/placing/PlaceManager.kt | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 69ff90631..56df18afc 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -30,7 +30,7 @@ class PlaceSettings( override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } override val axisRotate - get() = rotateForPlace && airPlace.isEnabled() && axisRotateSetting + get() = airPlace.isEnabled() && axisRotateSetting override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 1c1041afd..d21922cf2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -60,6 +60,7 @@ import com.lambda.util.player.copyPlayer import com.lambda.util.player.gamemode import com.lambda.util.player.placementRotations import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition import net.minecraft.enchantment.Enchantments @@ -293,10 +294,7 @@ object BuildSimulator { context = checked } - var resultState = blockItem.getPlacementState(context) ?: run { - acc.add(PlaceResult.BlockedByEntity(pos)) - return@forEach - } + lateinit var resultState: BlockState var rot = fakePlayer.rotation val simulatePlaceState = placeState@ { @@ -308,14 +306,13 @@ object BuildSimulator { pos, resultState, context, (target as? TargetState.State)?.blockState ) } else { - rot = fakePlayer.rotation return@placeState null } } - simulatePlaceState()?.let simulatePlaceState@ { placeResult -> + simulatePlaceState()?.let simulatePlaceState@ { basePlaceResult -> if (!place.axisRotate) { - acc.add(placeResult) + acc.add(basePlaceResult) return@forEach } placementRotations.forEachIndexed direction@ { index, angle -> @@ -334,6 +331,7 @@ object BuildSimulator { } else -> { + rot = fakePlayer.rotation return@simulatePlaceState } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index e152ed9bc..a6299c8a6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -97,8 +97,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { currentRequest?.let { request -> val notSneaking = !player.isSneaking val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)) - val invalidRotation = request.buildConfig.placeSettings.rotateForPlace && !validRotation - if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || invalidRotation) + if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || !validRotation) return@listen val actionResult = placeBlock(request, Hand.MAIN_HAND) @@ -128,7 +127,8 @@ object PlaceManager : RequestHandler(), PositionBlocking { return@onRotate } - rotation = if (request.buildConfig.placeSettings.rotateForPlace) + val placeConfig = request.buildConfig.placeSettings + rotation = if (placeConfig.rotateForPlace || placeConfig.axisRotate) request.rotationConfig.request(request.placeContext.rotation) else null From c2244a1210e1fd06f1b1515b531058bf4c9601d6 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 02:54:20 +0100 Subject: [PATCH 087/364] allow multiple block placements in one tick --- .../com/lambda/config/groups/PlaceSettings.kt | 2 +- .../request/breaking/BreakManager.kt | 15 +- .../request/breaking/BreakRequest.kt | 2 +- .../request/placing/PlaceManager.kt | 134 ++++++++++-------- .../request/placing/PlaceRequest.kt | 4 +- .../request/rotation/RotationManager.kt | 7 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 29 +++- 7 files changed, 117 insertions(+), 76 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 56df18afc..7d9932d26 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -33,7 +33,7 @@ class PlaceSettings( get() = airPlace.isEnabled() && axisRotateSetting override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } - override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } + override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() } override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing } override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index ede605140..4d616d2cb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -35,6 +35,7 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationManager.onRotate @@ -119,12 +120,14 @@ object BreakManager : RequestHandler(), PositionBlocking { currentRequest?.let request@ { request -> if (instantBreaks.isEmpty()) return@request - instantBreaks.forEach { ctx -> - val breakInfo = handleRequestContext(ctx, request) ?: return@request - if (!breakInfo.requestHotbarSwap()) return@forEach - updateBlockBreakingProgress(breakInfo) - activeThisTick = true - } + instantBreaks + .sortedBy { HotbarManager.serverSlot == it.hotbarIndex } + .forEach { ctx -> + val breakInfo = handleRequestContext(ctx, request) ?: return@request + if (!breakInfo.requestHotbarSwap()) return@forEach + updateBlockBreakingProgress(breakInfo) + activeThisTick = true + } instantBreaks = emptyList() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 4f892a96a..b36a8ab16 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -29,7 +29,7 @@ import com.lambda.util.BlockUtils.blockState import net.minecraft.entity.ItemEntity data class BreakRequest( - val contexts: List, + val contexts: Collection, val buildConfig: BuildConfig, val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index a6299c8a6..4d51a4fee 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -25,16 +25,16 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager +import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationManager.onRotatePost -import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.item @@ -62,19 +62,18 @@ import net.minecraft.util.math.Direction import net.minecraft.world.GameMode object PlaceManager : RequestHandler(), PositionBlocking { - private val pendingPlacements = LimitedDecayQueue( + private val pendingPlacements = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { - info("${it::class.simpleName} at ${it.placeContext.expectedPos.toShortString()} timed out") - mc.world?.setBlockState(it.placeContext.expectedPos, it.placeContext.checkedState) - it.pendingInteractionsList.remove(it.placeContext) + info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") + mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + it.pendingInteractionsList.remove(it.context) } - override val blockedPositions - get() = pendingPlacements.map { it.placeContext.expectedPos } + private var placeContexts = emptyList() - private var rotation: RotationRequest? = null - private var validRotation = false + override val blockedPositions + get() = pendingPlacements.map { it.context.expectedPos } fun Any.onPlace( alwaysListen: Boolean = false, @@ -95,23 +94,27 @@ object PlaceManager : RequestHandler(), PositionBlocking { init { listen(priority = Int.MIN_VALUE) { currentRequest?.let { request -> - val notSneaking = !player.isSneaking - val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)) - if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || !validRotation) - return@listen - - val actionResult = placeBlock(request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") - } - activeThisTick = true + placeContexts + .forEach { ctx -> + val notSneaking = !player.isSneaking + val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) + if ((ctx.sneak && notSneaking) || !hotbarRequest.done || !ctx.rotation.done) + return@listen + + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } + activeThisTick = true + } + placeContexts = emptyList() } } onRotate(priority = Int.MIN_VALUE) { preEvent() - if (!updateRequest { request -> canPlace(request.value.placeContext) }) { + if (!updateRequest()) { postEvent() return@onRotate } @@ -122,45 +125,48 @@ object PlaceManager : RequestHandler(), PositionBlocking { } currentRequest?.let request@ { request -> - if (pendingPlacements.size >= request.buildConfig.placeSettings.maxPendingPlacements) { - postEvent() - return@onRotate - } + pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements) + pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) val placeConfig = request.buildConfig.placeSettings - rotation = if (placeConfig.rotateForPlace || placeConfig.axisRotate) - request.rotationConfig.request(request.placeContext.rotation) - else null - pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements) - pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) + val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) + val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) + val isSneaking = player.isSneaking + val currentHotbarIndex = HotbarManager.serverSlot + placeContexts = request.placeContexts + .filter { canPlace(it) } + .sortedBy { isSneaking == it.sneak && currentHotbarIndex == it.hotbarIndex } + .take(takeCount) + + request.placeContexts.firstOrNull()?.let { ctx -> + if (placeConfig.rotateForPlace || placeConfig.axisRotate) + request.rotationConfig.request(ctx.rotation) + } } - } - onRotatePost(priority = Int.MIN_VALUE) { - validRotation = rotation?.done ?: true postEvent() } listen { - if (currentRequest?.placeContext?.sneak == true) it.input.sneaking = true + if (placeContexts.firstOrNull()?.sneak == true) it.input.sneaking = true } listen { event -> pendingPlacements - .firstOrNull { it.placeContext.expectedPos == event.pos } - ?.let { request -> - removePendingPlace(request) + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { info -> + removePendingPlace(info) // return if the block wasn't placed - if (!matchesTargetState(event.pos, request.placeContext.targetState, event.newState)) + if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) return@listen - if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) - with (request.placeContext) { + if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) + with (info.context) { placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos) } - request.onPlace() + info.onPlace() return@listen } } @@ -168,7 +174,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { private fun canPlace(placeContext: PlaceContext) = pendingPlacements.none { pending -> - pending.placeContext.expectedPos == placeContext.expectedPos + pending.context.expectedPos == placeContext.expectedPos } private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = @@ -178,15 +184,16 @@ object PlaceManager : RequestHandler(), PositionBlocking { false } - private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand): ActionResult { + private fun SafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() - val hitResult = request.placeContext.result + val hitResult = placeContext.result if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS - return interactBlockInternal(request, request.buildConfig.placeSettings, hand, hitResult) + return interactBlockInternal(placeContext, request, request.buildConfig.placeSettings, hand, hitResult) } private fun SafeContext.interactBlockInternal( + placeContext: PlaceContext, request: PlaceRequest, placeConfig: PlaceConfig, hand: Hand, @@ -212,17 +219,18 @@ object PlaceManager : RequestHandler(), PositionBlocking { val itemUsageContext = ItemUsageContext(player, hand, hitResult) return if (gamemode.isCreative) { val i = itemStack.count - useOnBlock(request, hand, hitResult, placeConfig, itemStack, itemUsageContext) + useOnBlock(placeContext, request, hand, hitResult, placeConfig, itemStack, itemUsageContext) .also { itemStack.count = i } } else - useOnBlock(request, hand, hitResult, placeConfig, itemStack, itemUsageContext) + useOnBlock(placeContext, request, hand, hitResult, placeConfig, itemStack, itemUsageContext) } return ActionResult.PASS } private fun SafeContext.useOnBlock( + placeContext: PlaceContext, request: PlaceRequest, hand: Hand, hitResult: BlockHitResult, @@ -238,10 +246,11 @@ object PlaceManager : RequestHandler(), PositionBlocking { val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS - return place(request, hand, hitResult, placeConfig, item, ItemPlacementContext(context)) + return place(placeContext, request, hand, hitResult, placeConfig, item, ItemPlacementContext(context)) } private fun SafeContext.place( + placeContext: PlaceContext, request: PlaceRequest, hand: Hand, hitResult: BlockHitResult, @@ -258,10 +267,12 @@ object PlaceManager : RequestHandler(), PositionBlocking { val stackInHand = player.getStackInHand(hand) val stackCountPre = stackInHand.count if (placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) { - addPendingPlace(request) + addPendingPlace( + PlaceInfo(placeContext, request.onPlace, request.pendingInteractionsList, placeConfig) + ) } - if (request.buildConfig.placeSettings.airPlace == PlaceConfig.AirPlaceMode.Grim) { + if (placeConfig.airPlace == PlaceConfig.AirPlaceMode.Grim) { val placeHand = if (hand == Hand.MAIN_HAND) Hand.OFF_HAND else Hand.MAIN_HAND airPlaceOffhandSwap() sendPlacePacket(placeHand, hitResult) @@ -270,8 +281,8 @@ object PlaceManager : RequestHandler(), PositionBlocking { sendPlacePacket(hand, hitResult) } - if (request.buildConfig.placeSettings.swing) { - swingHand(request.buildConfig.placeSettings.swingType, hand) + if (placeConfig.swing) { + swingHand(placeConfig.swingType, hand) if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) { mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) @@ -332,16 +343,23 @@ object PlaceManager : RequestHandler(), PositionBlocking { ) } - private fun addPendingPlace(request: PlaceRequest) { - pendingPlacements.add(request) - request.pendingInteractionsList.add(request.placeContext) + private fun addPendingPlace(info: PlaceInfo) { + pendingPlacements.add(info) + info.pendingInteractionsList.add(info.context) } - private fun removePendingPlace(request: PlaceRequest) { - pendingPlacements.remove(request) - request.pendingInteractionsList.remove(request.placeContext) + private fun removePendingPlace(info: PlaceInfo) { + pendingPlacements.remove(info) + info.pendingInteractionsList.remove(info.context) } + private data class PlaceInfo( + val context: PlaceContext, + val onPlace: () -> Unit, + val pendingInteractionsList: MutableCollection, + val placeConfig: PlaceConfig + ) + override fun preEvent() = UpdateManagerEvent.Place.Pre().post() override fun postEvent() = UpdateManagerEvent.Place.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index cb138d1bc..0b5224b14 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -29,7 +29,7 @@ import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState data class PlaceRequest( - val placeContext: PlaceContext, + val placeContexts: Collection, val buildConfig: BuildConfig, val rotationConfig: RotationConfig, val hotbarConfig: HotbarConfig, @@ -40,6 +40,6 @@ data class PlaceRequest( ) : Request(prio) { override val done: Boolean get() = runSafe { - placeContext.targetState.matches(blockState(placeContext.expectedPos), placeContext.expectedPos, world) + placeContexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index f6cb70d77..fab18bdca 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -21,7 +21,12 @@ import com.lambda.Lambda import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.EventFlow.post -import com.lambda.event.events.* +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.PacketEvent +import com.lambda.event.events.PlayerPacketEvent +import com.lambda.event.events.RotationEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.Priority diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 29c29078c..854e349fb 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -22,17 +22,21 @@ import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InventoryConfig -import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.PropagatingBlueprint -import com.lambda.interaction.construction.blueprint.TickingBlueprint import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.blueprint.TickingBlueprint import com.lambda.interaction.construction.context.BuildContext -import com.lambda.interaction.construction.result.* +import com.lambda.interaction.construction.result.BreakResult +import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.Drawable +import com.lambda.interaction.construction.result.Navigable +import com.lambda.interaction.construction.result.PlaceResult +import com.lambda.interaction.construction.result.Resolvable import com.lambda.interaction.construction.simulation.BuildGoal import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation @@ -41,6 +45,7 @@ import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.tr import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.placing.PlaceRequest +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task @@ -141,14 +146,14 @@ class BuildTask @Ta5kBuilder constructor( val takeCount = build.breakSettings .breaksPerTick .coerceAtMost(emptyPendingInteractionSlots) - val instantResults = breakResults + val instantBreakResults = breakResults .filter { it.context.instantBreak } .take(takeCount) - if (instantResults.isNotEmpty()) { + if (instantBreakResults.isNotEmpty()) { build.breakSettings.request( BreakRequest( - instantResults.map { it.context }, build, rotation, hotbar, + instantBreakResults.map { it.context }, build, rotation, hotbar, pendingInteractionsList = pendingInteractions, onBreak = { breaks++ }, onItemDrop = onItemDrop @@ -168,8 +173,18 @@ class BuildTask @Ta5kBuilder constructor( return@onRotate } is PlaceResult.Place -> { + val takeCount = build.placeSettings + .placementsPerTick + .coerceAtMost(emptyPendingInteractionSlots) + val placeResults = resultsNotBlocked + .filterIsInstance() + .distinctBy { it.blockPos } + .take(takeCount) + build.placeSettings.request( - PlaceRequest(bestResult.context, build, rotation, hotbar, interact, pendingInteractions) { placements++ } + PlaceRequest( + placeResults.map { it.context }, build, rotation, hotbar, interact, pendingInteractions + ) { placements++ } ) } } From 7ba7b72698496a883417d2263c7dadf96f8b4fa6 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 03:20:56 +0100 Subject: [PATCH 088/364] removed ManagerUtils --- .../interaction/request/ManagerUtils.kt | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt deleted file mode 100644 index d5f5e44f9..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.interaction.request - -import com.lambda.interaction.request.breaking.BreakManager -import com.lambda.interaction.request.hotbar.HotbarManager -import com.lambda.interaction.request.placing.PlaceManager -import com.lambda.interaction.request.rotation.RotationManager - -object ManagerUtils { - val managers = listOf( - RotationManager, - HotbarManager, - BreakManager, - PlaceManager - ) - - val positionBlockingManagers = managers - .filterIsInstance() -} \ No newline at end of file From 1ce0eb9d0c5c94feb97e7792d4d14990ea3348e9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 16:17:29 +0100 Subject: [PATCH 089/364] moved request updates in break manager to on tick to remove needless tick delay if rotations are already complete or disabled, and improved general request handling logic --- .../com/lambda/config/groups/BreakSettings.kt | 2 +- .../request/breaking/BreakConfig.kt | 2 +- .../request/breaking/BreakManager.kt | 96 ++++++++----------- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 4 +- 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index e6471567f..fc093b554 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -40,7 +40,7 @@ class BreakSettings( override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } override val maxPendingBreaks by c.setting("Max Pending Breaks", 5, 1..30, 1, "The maximum amount of pending breaks") { vis() } - override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } + override val instantBreaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 9f22413ed..e66f4ff12 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -37,7 +37,7 @@ abstract class BreakConfig( abstract val rotateForBreak: Boolean abstract val breakConfirmation: BreakConfirmationMode abstract val maxPendingBreaks: Int - abstract val breaksPerTick: Int + abstract val instantBreaksPerTick: Int abstract val breakWeakBlocks: Boolean abstract val forceSilkTouch: Boolean abstract val forceFortunePickaxe: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 4d616d2cb..d558efd49 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -37,9 +37,6 @@ import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.interaction.request.placing.PlaceManager -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationManager.onRotatePost import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState @@ -87,7 +84,8 @@ object BreakManager : RequestHandler(), PositionBlocking { get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } private var rotation: RotationRequest? = null - private var validRotation = false + private val validRotation + get() = rotation?.done ?: true private var blockBreakingCooldown = 0 @@ -111,62 +109,43 @@ object BreakManager : RequestHandler(), PositionBlocking { init { listen(priority = Int.MIN_VALUE + 1) { - if (isOnBreakCooldown()) { - blockBreakingCooldown-- - return@listen - } - if (PlaceManager.activeThisTick()) return@listen - - currentRequest?.let request@ { request -> - if (instantBreaks.isEmpty()) return@request - - instantBreaks - .sortedBy { HotbarManager.serverSlot == it.hotbarIndex } - .forEach { ctx -> - val breakInfo = handleRequestContext(ctx, request) ?: return@request - if (!breakInfo.requestHotbarSwap()) return@forEach - updateBlockBreakingProgress(breakInfo) - activeThisTick = true - } - instantBreaks = emptyList() - } - - if (!validRotation) return@listen - - // Reversed so that the breaking order feels natural to the user as the primary break has to - // be started after the secondary - breakingInfos - .filterNotNull() - .reversed() - .forEach { info -> - if (!info.requestHotbarSwap()) return@forEach - updateBlockBreakingProgress(info) - activeThisTick = true - } - } - - onRotate(priority = Int.MIN_VALUE) { preEvent() - if (!updateRequest()) { - requestRotate() - return@onRotate - } + if (updateRequest()) currentRequest?.let request@ { request -> + if (isOnBreakCooldown()) { + blockBreakingCooldown-- + return@request + } - currentRequest?.let request@ { request -> val breakConfig = request.buildConfig.breakSettings - val takeCount = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) - if (takeCount <= 0) return@request + val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) + if (maxBreaksThisTick <= 0) return@request + val validContexts = request.contexts .filter { ctx -> canAccept(ctx) } .sortedBy { it.instantBreak } - .take(takeCount) + .take(maxBreaksThisTick) instantBreaks = validContexts - .take(breakConfig.breaksPerTick) + .take(breakConfig.instantBreaksPerTick) .filter { it.instantBreak } + .sortedBy { it.hotbarIndex == HotbarManager.serverSlot } - if (instantBreaks.isNotEmpty()) return@request + if (instantBreaks.isNotEmpty()) { + instantBreaks.forEach { ctx -> + if (ctx.hotbarIndex != HotbarManager.serverSlot) { + if (!request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)).done) return@request + } + val breakInfo = handleRequestContext(ctx, request) ?: return@request + updateBlockBreakingProgress(breakInfo) + activeThisTick = true + } + if (instantBreaks.size == breakConfig.instantBreaksPerTick) { + instantBreaks = emptyList() + return@request + } + instantBreaks = emptyList() + } validContexts .filter { it.instantBreak.not() } @@ -176,11 +155,19 @@ object BreakManager : RequestHandler(), PositionBlocking { } requestRotate() - } + if (!validRotation) return@listen - onRotatePost(priority = Int.MIN_VALUE) { - validRotation = rotation?.done ?: true - postEvent() + // ToDo: dynamically update hotbarIndex as contexts are persistent and don't get updated by new requests each tick + // Reversed so that the breaking order feels natural to the user as the primary break has to + // be started after the secondary + breakingInfos + .filterNotNull() + .reversed() + .forEach { info -> + if (!info.requestHotbarSwap()) return@forEach + updateBlockBreakingProgress(info) + activeThisTick = true + } } listen { event -> @@ -282,7 +269,8 @@ object BreakManager : RequestHandler(), PositionBlocking { primaryBreakingInfo?.let { primaryInfo -> if (!primaryInfo.breakConfig.doubleBreak || primaryInfo.startedWithSecondary - || secondaryBreakingInfo != null) { + || secondaryBreakingInfo != null + || requestCtx.hotbarIndex != primaryInfo.context.hotbarIndex) { return null } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 854e349fb..dab6b1c49 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -142,9 +142,9 @@ class BuildTask @Ta5kBuilder constructor( val breakResults = resultsNotBlocked .filterIsInstance() - if (build.breakSettings.breaksPerTick > 1) { + if (build.breakSettings.instantBreaksPerTick > 1) { val takeCount = build.breakSettings - .breaksPerTick + .instantBreaksPerTick .coerceAtMost(emptyPendingInteractionSlots) val instantBreakResults = breakResults .filter { it.context.instantBreak } From ff5d79db2d1b2ec0948e3820ef2c4bbb560207f2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 16:19:46 +0100 Subject: [PATCH 090/364] add back postEvent calls --- .../lambda/interaction/request/breaking/BreakManager.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d558efd49..ffe7e4f0c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -155,7 +155,10 @@ object BreakManager : RequestHandler(), PositionBlocking { } requestRotate() - if (!validRotation) return@listen + if (!validRotation) { + postEvent() + return@listen + } // ToDo: dynamically update hotbarIndex as contexts are persistent and don't get updated by new requests each tick // Reversed so that the breaking order feels natural to the user as the primary break has to @@ -168,6 +171,8 @@ object BreakManager : RequestHandler(), PositionBlocking { updateBlockBreakingProgress(info) activeThisTick = true } + + postEvent() } listen { event -> From fd41e6eb3f250a91d43174f9bb71ddbbb0efe2dc Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 16:25:57 +0100 Subject: [PATCH 091/364] inline instantBreaks list --- .../interaction/request/breaking/BreakManager.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index ffe7e4f0c..595f3c13e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -89,8 +89,6 @@ object BreakManager : RequestHandler(), PositionBlocking { private var blockBreakingCooldown = 0 - private var instantBreaks = listOf() - fun Any.onBreak( alwaysListen: Boolean = false, priority: Priority = 0, @@ -126,7 +124,7 @@ object BreakManager : RequestHandler(), PositionBlocking { .sortedBy { it.instantBreak } .take(maxBreaksThisTick) - instantBreaks = validContexts + val instantBreaks = validContexts .take(breakConfig.instantBreaksPerTick) .filter { it.instantBreak } .sortedBy { it.hotbarIndex == HotbarManager.serverSlot } @@ -140,11 +138,7 @@ object BreakManager : RequestHandler(), PositionBlocking { updateBlockBreakingProgress(breakInfo) activeThisTick = true } - if (instantBreaks.size == breakConfig.instantBreaksPerTick) { - instantBreaks = emptyList() - return@request - } - instantBreaks = emptyList() + if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request } validContexts From 9948b4f4354676a801ebb532fff9b27690d545d2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 17:38:51 +0100 Subject: [PATCH 092/364] moved request updates in place manager to on tick to remove needless tick delay if rotations are already complete or disabled --- .../request/placing/PlaceManager.kt | 77 +++++++++++-------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 4d51a4fee..fab5f8854 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -34,7 +34,7 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.item @@ -70,7 +70,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { it.pendingInteractionsList.remove(it.context) } - private var placeContexts = emptyList() + private var rotation: RotationRequest? = null + private var validRotation = false + + private var shouldCrouch = false override val blockedPositions get() = pendingPlacements.map { it.context.expectedPos } @@ -93,38 +96,16 @@ object PlaceManager : RequestHandler(), PositionBlocking { init { listen(priority = Int.MIN_VALUE) { - currentRequest?.let { request -> - placeContexts - .forEach { ctx -> - val notSneaking = !player.isSneaking - val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) - if ((ctx.sneak && notSneaking) || !hotbarRequest.done || !ctx.rotation.done) - return@listen - - val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") - } - activeThisTick = true - } - placeContexts = emptyList() - } - } - - onRotate(priority = Int.MIN_VALUE) { preEvent() if (!updateRequest()) { postEvent() - return@onRotate - } - - if (BreakManager.activeThisTick()) { - postEvent() - return@onRotate + return@listen } currentRequest?.let request@ { request -> + if (BreakManager.activeThisTick()) return@request + pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements) pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) @@ -134,25 +115,53 @@ object PlaceManager : RequestHandler(), PositionBlocking { val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) val isSneaking = player.isSneaking val currentHotbarIndex = HotbarManager.serverSlot - placeContexts = request.placeContexts + val placeContexts = request.placeContexts .filter { canPlace(it) } .sortedBy { isSneaking == it.sneak && currentHotbarIndex == it.hotbarIndex } .take(takeCount) - request.placeContexts.firstOrNull()?.let { ctx -> - if (placeConfig.rotateForPlace || placeConfig.axisRotate) - request.rotationConfig.request(ctx.rotation) + rotation = if (placeConfig.rotateForPlace || placeConfig.axisRotate) { + placeContexts.firstOrNull()?.let { ctx -> + if (ctx.rotation.target.angleDistance == 0.0) null + else request.rotationConfig.request(ctx.rotation) + } + } else null + + placeContexts.forEach { ctx -> + val notSneaking = !player.isSneaking + val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) + if (ctx.sneak && notSneaking) { + shouldCrouch = true + return@listen + } + rotation?.let { rotation -> + if (rotation !== ctx.rotation || !validRotation) return@listen + } + if (!hotbarRequest.done) return@listen + + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } + activeThisTick = true } } postEvent() } - listen { - if (placeContexts.firstOrNull()?.sneak == true) it.input.sneaking = true + listen(priority = Int.MIN_VALUE) { + validRotation = rotation?.done ?: true + } + + listen(priority = Int.MIN_VALUE) { + if (shouldCrouch) { + shouldCrouch = false + it.input.sneaking = true + } } - listen { event -> + listen(priority = Int.MIN_VALUE) { event -> pendingPlacements .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> From 49f0127b8967859c93b965ca281378d638043941 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 17:39:42 +0100 Subject: [PATCH 093/364] moved build task logic to on tick --- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index dab6b1c49..040a90f05 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -46,7 +46,6 @@ import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -92,8 +91,8 @@ class BuildTask @Ta5kBuilder constructor( } init { - onRotate { - if (collectDrops()) return@onRotate + listen { + if (collectDrops()) return@listen // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) @@ -107,17 +106,17 @@ class BuildTask @Ta5kBuilder constructor( .filter { result -> pendingInteractions.none { it.expectedPos == result.blockPos } } .sorted() - val bestResult = resultsNotBlocked.firstOrNull() ?: return@onRotate + val bestResult = resultsNotBlocked.firstOrNull() ?: return@listen when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { - if (pendingInteractions.isNotEmpty()) return@onRotate + if (pendingInteractions.isNotEmpty()) return@listen if (blueprint is PropagatingBlueprint) { blueprint.next() - return@onRotate + return@listen } if (finishOnDone) success() @@ -125,7 +124,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { - if (!build.pathing) return@onRotate + if (!build.pathing) return@listen val sim = blueprint.simulation(interact, rotation, inventory, build) val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) @@ -136,7 +135,7 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { - if (atMaxPendingInteractions) return@onRotate + if (atMaxPendingInteractions) return@listen when (bestResult) { is BreakResult.Break -> { val breakResults = resultsNotBlocked @@ -159,7 +158,7 @@ class BuildTask @Ta5kBuilder constructor( onItemDrop = onItemDrop ) ) - return@onRotate + return@listen } } @@ -170,7 +169,7 @@ class BuildTask @Ta5kBuilder constructor( onItemDrop = onItemDrop ) build.breakSettings.request(request) - return@onRotate + return@listen } is PlaceResult.Place -> { val takeCount = build.placeSettings From 23aee7e69a52a1eacfd2730f62c4bcd07fc2d9a2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 21:23:39 +0100 Subject: [PATCH 094/364] simulate break contexts for each break info independently in the break manager to keep up to date information --- .../request/breaking/BreakManager.kt | 42 +++++++++++++++---- .../request/breaking/BreakRequest.kt | 4 ++ .../kotlin/com/lambda/task/tasks/BuildTask.kt | 4 +- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 595f3c13e..777c878f2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -28,7 +28,11 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe +import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure +import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.result.BreakResult +import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority @@ -51,6 +55,7 @@ import com.lambda.util.player.gamemode import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock +import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.client.world.ClientWorld @@ -84,8 +89,7 @@ object BreakManager : RequestHandler(), PositionBlocking { get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } private var rotation: RotationRequest? = null - private val validRotation - get() = rotation?.done ?: true + private var validRotation = false private var blockBreakingCooldown = 0 @@ -109,6 +113,10 @@ object BreakManager : RequestHandler(), PositionBlocking { listen(priority = Int.MIN_VALUE + 1) { preEvent() + breakingInfos.forEach { + it?.simulate(player) + } + if (updateRequest()) currentRequest?.let request@ { request -> if (isOnBreakCooldown()) { blockBreakingCooldown-- @@ -149,12 +157,11 @@ object BreakManager : RequestHandler(), PositionBlocking { } requestRotate() - if (!validRotation) { + if (!validRotation && rotation != null) { postEvent() return@listen } - // ToDo: dynamically update hotbarIndex as contexts are persistent and don't get updated by new requests each tick // Reversed so that the breaking order feels natural to the user as the primary break has to // be started after the secondary breakingInfos @@ -169,6 +176,10 @@ object BreakManager : RequestHandler(), PositionBlocking { postEvent() } + listen(priority = Int.MIN_VALUE) { + validRotation = rotation?.done ?: true + } + listen { event -> pendingBreaks .firstOrNull { it.context.expectedPos == event.pos } @@ -253,8 +264,7 @@ object BreakManager : RequestHandler(), PositionBlocking { private fun requestRotate() { rotation = breakingInfos - .filterNotNull() - .firstOrNull { it.breakConfig.rotateForBreak } + .firstOrNull { it?.breakConfig?.rotateForBreak == true } ?.let { info -> info.rotationConfig.request(RotationRequest(info.context.rotation, info.rotationConfig)) } @@ -497,13 +507,18 @@ object BreakManager : RequestHandler(), PositionBlocking { } data class BreakInfo( - val context: BreakContext, + var context: BreakContext, var type: BreakType, val request: BreakRequest ) { + // I hate this... val breakConfig = request.buildConfig.breakSettings val rotationConfig = request.rotationConfig + private val interactionConfig = request.interactionConfig + private val buildConfig = request.buildConfig + private val inventoryConfig = request.inventoryConfig private val hotbarConfig = request.hotbarConfig + val pendingInteractionsList = request.pendingInteractionsList private val onBreak = request.onBreak private val onItemDrop = request.onItemDrop @@ -543,6 +558,19 @@ object BreakManager : RequestHandler(), PositionBlocking { fun requestHotbarSwap() = hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done + fun simulate(player: ClientPlayerEntity) { + val result = context.expectedPos + .toStructure(context.targetState) + .toBlueprint() + .simulate(player.eyePos, interactionConfig, rotationConfig, inventoryConfig, buildConfig) + .firstOrNull() + ?: return + + if (result is BreakResult.Break) { + context = result.context + } + } + fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) val progress = (breakDelta * breakingTicks) / breakConfig.breakThreshold diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index b36a8ab16..d9057c929 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -18,6 +18,8 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.Priority @@ -32,6 +34,8 @@ data class BreakRequest( val contexts: Collection, val buildConfig: BuildConfig, val rotationConfig: RotationConfig, + val interactionConfig: InteractionConfig, + val inventoryConfig: InventoryConfig, val hotbarConfig: HotbarConfig, val prio: Priority = 0, val pendingInteractionsList: MutableCollection, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 040a90f05..7cf07d86e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -152,7 +152,7 @@ class BuildTask @Ta5kBuilder constructor( if (instantBreakResults.isNotEmpty()) { build.breakSettings.request( BreakRequest( - instantBreakResults.map { it.context }, build, rotation, hotbar, + instantBreakResults.map { it.context }, build, rotation, interact, inventory, hotbar, pendingInteractionsList = pendingInteractions, onBreak = { breaks++ }, onItemDrop = onItemDrop @@ -163,7 +163,7 @@ class BuildTask @Ta5kBuilder constructor( } val request = BreakRequest( - breakResults.map { it.context }, build, rotation, hotbar, + breakResults.map { it.context }, build, rotation, interact, inventory, hotbar, pendingInteractionsList = pendingInteractions, onBreak = { breaks++ }, onItemDrop = onItemDrop From 19f6d0d556a100b8e383f4ee996acb357250aefa Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 21:51:10 +0100 Subject: [PATCH 095/364] use original rotation request from break context and ignore if keep ticks is <= 0 --- .../interaction/construction/context/BreakContext.kt | 6 +++--- .../interaction/construction/context/BuildContext.kt | 2 ++ .../interaction/construction/context/PlaceContext.kt | 2 +- .../construction/simulation/BuildSimulator.kt | 11 ++++++++--- .../interaction/request/breaking/BreakManager.kt | 12 ++++++++++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index b82e63386..daa5bc128 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -22,7 +22,7 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.rotation.visibilty.RotationTarget +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.client.network.ClientPlayerInteractionManager @@ -38,7 +38,7 @@ import java.awt.Color data class BreakContext( override val pov: Vec3d, override val result: BlockHitResult, - val rotation: RotationTarget, + override val rotation: RotationRequest, override var checkedState: BlockState, override val targetState: TargetState, override var hotbarIndex: Int, @@ -64,7 +64,7 @@ data class BreakContext( override fun compareTo(other: BuildContext): Int { return when (other) { is BreakContext -> compareBy { - it.rotation.angleDistance + it.rotation.target.angleDistance }.compare(this, other) else -> 1 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 494a72cb9..5f3230588 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -21,6 +21,7 @@ import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.util.hit.BlockHitResult @@ -30,6 +31,7 @@ import net.minecraft.util.math.Vec3d interface BuildContext : Comparable, Drawable { val pov: Vec3d val result: BlockHitResult + val rotation: RotationRequest val distance: Double val expectedState: BlockState val targetState: TargetState diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 042dc8b8b..9b93971ae 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -35,7 +35,7 @@ import java.awt.Color data class PlaceContext( override val pov: Vec3d, override val result: BlockHitResult, - val rotation: RotationRequest, + override val rotation: RotationRequest, override val distance: Double, override val expectedState: BlockState, override val checkedState: BlockState, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index d21922cf2..1a01e7cf7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -40,10 +40,11 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.* import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces +import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -454,10 +455,13 @@ object BuildSimulator { /* the player is buried inside the block */ if (boxes.any { it.contains(eye) }) { currentCast?.blockResult?.let { blockHit -> + val rotationRequest = RotationRequest( + lookAtBlock(pos, config = interact), rotation + ) val breakContext = BreakContext( eye, blockHit, - lookAtBlock(pos, config = interact), + rotationRequest, state, targetState, player.inventory.selectedSlot, @@ -505,10 +509,11 @@ object BuildSimulator { val bestHit = interact.pointSelection.select(validHits) ?: return acc val blockHit = bestHit.hit.blockResult ?: return acc val target = lookAt(bestHit.targetRotation, 0.001) + val request = RotationRequest(target, rotation) val instant = instantBreakable(state, pos, build.breakSettings.breakThreshold) val breakContext = BreakContext( - eye, blockHit, target, state, targetState, player.inventory.selectedSlot, instant + eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant ) if (gamemode.isCreative) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 777c878f2..e810e49b8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -266,7 +266,9 @@ object BreakManager : RequestHandler(), PositionBlocking { rotation = breakingInfos .firstOrNull { it?.breakConfig?.rotateForBreak == true } ?.let { info -> - info.rotationConfig.request(RotationRequest(info.context.rotation, info.rotationConfig)) + // If the simulation cant find a valid rotation to break the block, the existing break context stays and the keep ticks deplete until + if (info.context.rotation.keepTicks <= 0) null + else info.rotationConfig.request(info.context.rotation) } } @@ -560,7 +562,13 @@ object BreakManager : RequestHandler(), PositionBlocking { fun simulate(player: ClientPlayerEntity) { val result = context.expectedPos - .toStructure(context.targetState) + .toStructure( + if (!context.checkedState.fluidState.isEmpty) { + TargetState.State(context.checkedState.fluidState.blockState) + } else { + TargetState.Air + } + ) .toBlueprint() .simulate(player.eyePos, interactionConfig, rotationConfig, inventoryConfig, buildConfig) .firstOrNull() From f4311a74e5b38a3e27d21997a926801cdd41ec01 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 22:35:57 +0100 Subject: [PATCH 096/364] postEvent in the place manager --- .../interaction/request/placing/PlaceManager.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index fab5f8854..9d863c29c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -132,12 +132,19 @@ object PlaceManager : RequestHandler(), PositionBlocking { val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) if (ctx.sneak && notSneaking) { shouldCrouch = true + postEvent() return@listen } rotation?.let { rotation -> - if (rotation !== ctx.rotation || !validRotation) return@listen + if (rotation !== ctx.rotation || !validRotation) { + postEvent() + return@listen + } + } + if (!hotbarRequest.done) { + postEvent() + return@listen } - if (!hotbarRequest.done) return@listen val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) if (!actionResult.isAccepted) { From bf3fc3583d61257b2faafeb72c313fc0a36412c3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 31 Mar 2025 22:44:30 +0100 Subject: [PATCH 097/364] priorities on all listeners in the break manager (unnecessary but clean for continuity) --- .../lambda/interaction/request/breaking/BreakManager.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index e810e49b8..5a1bec96d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -176,11 +176,11 @@ object BreakManager : RequestHandler(), PositionBlocking { postEvent() } - listen(priority = Int.MIN_VALUE) { + listen(priority = Int.MIN_VALUE + 1) { validRotation = rotation?.done ?: true } - listen { event -> + listen(priority = Int.MIN_VALUE + 1) { event -> pendingBreaks .firstOrNull { it.context.expectedPos == event.pos } ?.let { pending -> @@ -224,7 +224,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work - listen { + listen(priority = Int.MIN_VALUE + 1) { if (it.entity !is ItemEntity) return@listen pendingBreaks .firstOrNull { info -> matchesBlockItem(info, it.entity) } @@ -242,7 +242,7 @@ object BreakManager : RequestHandler(), PositionBlocking { ?.internalOnItemDrop(it.entity) } - listenUnsafe { + listenUnsafe(priority = Int.MIN_VALUE + 1) { breakingInfos.forEach { it?.nullify() } pendingBreaks.clear() setBreakCooldown(0) From 0700e5b0eda9606c69c01c53b9a7d216517b6fcd Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 1 Apr 2025 05:22:01 +0100 Subject: [PATCH 098/364] more specific context sorting in contextual context managers --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 6 ++++-- .../com/lambda/interaction/request/placing/PlaceManager.kt | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 5a1bec96d..9128b9237 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -129,13 +129,15 @@ object BreakManager : RequestHandler(), PositionBlocking { val validContexts = request.contexts .filter { ctx -> canAccept(ctx) } - .sortedBy { it.instantBreak } + .sortedWith( + compareByDescending { it.instantBreak } + .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } + ) .take(maxBreaksThisTick) val instantBreaks = validContexts .take(breakConfig.instantBreaksPerTick) .filter { it.instantBreak } - .sortedBy { it.hotbarIndex == HotbarManager.serverSlot } if (instantBreaks.isNotEmpty()) { instantBreaks.forEach { ctx -> diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 9d863c29c..a9a29a2a5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -117,7 +117,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { val currentHotbarIndex = HotbarManager.serverSlot val placeContexts = request.placeContexts .filter { canPlace(it) } - .sortedBy { isSneaking == it.sneak && currentHotbarIndex == it.hotbarIndex } + .sortedWith( + compareByDescending { it.hotbarIndex == currentHotbarIndex } + .thenByDescending { it.sneak == isSneaking } + ) .take(takeCount) rotation = if (placeConfig.rotateForPlace || placeConfig.axisRotate) { From 6424ec2989ffad364729b5df7d36ef16b7d6f6c0 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 1 Apr 2025 05:47:17 +0100 Subject: [PATCH 099/364] sort the build results in BreakInfo::simulate --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 9128b9237..1a4aea0ee 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -573,7 +573,7 @@ object BreakManager : RequestHandler(), PositionBlocking { ) .toBlueprint() .simulate(player.eyePos, interactionConfig, rotationConfig, inventoryConfig, buildConfig) - .firstOrNull() + .minOrNull() ?: return if (result is BreakResult.Break) { From 77744a968001aa9c89efc0f85692bb417618b2bb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 1 Apr 2025 19:30:22 +0100 Subject: [PATCH 100/364] cleanup pending interactions on tick --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 1 + .../com/lambda/interaction/request/placing/PlaceManager.kt | 2 ++ .../kotlin/com/lambda/util/collections/LimitedDecayQueue.kt | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 1a4aea0ee..e6f860530 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -113,6 +113,7 @@ object BreakManager : RequestHandler(), PositionBlocking { listen(priority = Int.MIN_VALUE + 1) { preEvent() + pendingBreaks.cleanUp() breakingInfos.forEach { it?.simulate(player) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index a9a29a2a5..5cd71e82d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -98,6 +98,8 @@ object PlaceManager : RequestHandler(), PositionBlocking { listen(priority = Int.MIN_VALUE) { preEvent() + pendingPlacements.cleanUp() + if (!updateRequest()) { postEvent() return@listen diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index 9dceafcb6..db9404cf6 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -125,7 +125,7 @@ class LimitedDecayQueue( cleanUp() } - private fun cleanUp() { + fun cleanUp() { val now = Instant.now() while (queue.isNotEmpty() && now.minusMillis(maxAge).isAfter(queue.peek().second)) { onDecay(queue.poll().first) From 9a5ba9ed3e4fe162bdd60020498b87cfc9ca9a2f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 1 Apr 2025 21:16:21 +0100 Subject: [PATCH 101/364] fix rotations timing issue --- .../mixin/entity/ClientPlayerEntityMixin.java | 1 + .../request/rotation/RotationManager.kt | 98 +++++++++---------- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index 853362ca8..52fe90617 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -74,6 +74,7 @@ void onMove(MovementType movementType, Vec3d movement, CallbackInfo ci) { @Redirect(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(ZF)V")) void processMovement(Input input, boolean slowDown, float slowDownFactor) { input.tick(slowDown, slowDownFactor); + RotationManager.processRotations(); RotationManager.BaritoneProcessor.processPlayerMovement(input, slowDown, slowDownFactor); EventFlow.post(new MovementEvent.InputUpdate(input, slowDown, slowDownFactor)); } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index fab18bdca..88399d91a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -23,7 +23,6 @@ import com.lambda.core.Loadable import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.PacketEvent -import com.lambda.event.events.PlayerPacketEvent import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent @@ -80,69 +79,68 @@ object RotationManager : RequestHandler(), Loadable { } init { - // For some reason we have to update AFTER sending player packets - // instead of updating on TickEvent.Pre (am I doing something wrong?) - listen(Int.MIN_VALUE) { - preEvent() - - // Update the request - val changed = updateRequest(true) { entry -> - // skip requests that have failed to build the rotation - // to free the request place for others - entry.value.target.targetRotation.value != null - } - - if (currentRequest != null) activeThisTick = true + listen { event -> + val packet = event.packet + if (packet !is PlayerPositionLookS2CPacket) return@listen - if (!changed) { // rebuild the rotation if the same context gets used again - currentRequest?.target?.targetRotation?.update() + runGameScheduled { + reset(Rotation(packet.yaw, packet.pitch)) } + } - // Calculate the target rotation - val targetRotation = currentRequest?.let { request -> - val rotationTo = if (request.keepTicks >= 0) - request.target.targetRotation.value - ?: currentRotation // same context gets used again && the rotation is null this tick - else player.rotation + listenUnsafe { + reset(Rotation.ZERO) + } + } - val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier - val turnSpeed = request.turnSpeed() * speedMultiplier + @JvmStatic + fun processRotations() = runSafe { + preEvent() + + // Update the request + val changed = updateRequest(true) { entry -> + // skip requests that have failed to build the rotation + // to free the request place for others + entry.value.target.targetRotation.value != null + } - currentRotation.slerp(rotationTo, turnSpeed) - } ?: player.rotation + if (currentRequest != null) activeThisTick = true - // Update the current rotation - prevRotation = currentRotation - currentRotation = targetRotation/*.fixSensitivity(prevRotation)*/ + if (!changed) { // rebuild the rotation if the same context gets used again + currentRequest?.target?.targetRotation?.update() + } - // Handle LOCK mode - if (currentRequest?.mode == RotationMode.Lock) { - player.yaw = currentRotation.yawF - player.pitch = currentRotation.pitchF - } + // Calculate the target rotation + val targetRotation = currentRequest?.let { request -> + val rotationTo = if (request.keepTicks >= 0) + request.target.targetRotation.value + ?: currentRotation // same context gets used again && the rotation is null this tick + else player.rotation - // Tick and reset the context - currentRequest?.let { - if (--it.keepTicks > 0) return@let - if (--it.decayTicks >= 0) return@let - currentRequest = null - } + val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier + val turnSpeed = request.turnSpeed() * speedMultiplier - postEvent() - } + currentRotation.slerp(rotationTo, turnSpeed) + } ?: player.rotation - listen { event -> - val packet = event.packet - if (packet !is PlayerPositionLookS2CPacket) return@listen + // Update the current rotation + prevRotation = currentRotation + currentRotation = targetRotation/*.fixSensitivity(prevRotation)*/ - runGameScheduled { - reset(Rotation(packet.yaw, packet.pitch)) - } + // Handle LOCK mode + if (currentRequest?.mode == RotationMode.Lock) { + player.yaw = currentRotation.yawF + player.pitch = currentRotation.pitchF } - listenUnsafe { - reset(Rotation.ZERO) + // Tick and reset the context + currentRequest?.let { + if (--it.keepTicks > 0) return@let + if (--it.decayTicks >= 0) return@let + currentRequest = null } + + postEvent() } private fun reset(rotation: Rotation) { From 1fa8c74a0a761d4e22d65c2c7db68b8049dc05d3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 2 Apr 2025 05:51:23 +0100 Subject: [PATCH 102/364] corrected break / place manager rotations to show an underlying issue --- .../request/breaking/BreakManager.kt | 9 +++------ .../interaction/request/placing/PlaceManager.kt | 17 +++-------------- .../request/rotation/RotationRequest.kt | 3 +-- .../rotation/visibilty/RotationTarget.kt | 2 +- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index e6f860530..717bb20b0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -89,7 +89,8 @@ object BreakManager : RequestHandler(), PositionBlocking { get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } private var rotation: RotationRequest? = null - private var validRotation = false + private val validRotation + get() = rotation?.done ?: true private var blockBreakingCooldown = 0 @@ -160,7 +161,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } requestRotate() - if (!validRotation && rotation != null) { + if (!validRotation) { postEvent() return@listen } @@ -179,10 +180,6 @@ object BreakManager : RequestHandler(), PositionBlocking { postEvent() } - listen(priority = Int.MIN_VALUE + 1) { - validRotation = rotation?.done ?: true - } - listen(priority = Int.MIN_VALUE + 1) { event -> pendingBreaks .firstOrNull { it.context.expectedPos == event.pos } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 5cd71e82d..6b167b49c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -71,7 +71,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { } private var rotation: RotationRequest? = null - private var validRotation = false private var shouldCrouch = false @@ -125,13 +124,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { ) .take(takeCount) - rotation = if (placeConfig.rotateForPlace || placeConfig.axisRotate) { - placeContexts.firstOrNull()?.let { ctx -> - if (ctx.rotation.target.angleDistance == 0.0) null - else request.rotationConfig.request(ctx.rotation) - } - } else null - placeContexts.forEach { ctx -> val notSneaking = !player.isSneaking val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) @@ -140,8 +132,9 @@ object PlaceManager : RequestHandler(), PositionBlocking { postEvent() return@listen } - rotation?.let { rotation -> - if (rotation !== ctx.rotation || !validRotation) { + if ((placeConfig.rotateForPlace || placeConfig.axisRotate)) { + rotation = request.rotationConfig.request(ctx.rotation) + if (!ctx.rotation.target.verify()) { postEvent() return@listen } @@ -162,10 +155,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { postEvent() } - listen(priority = Int.MIN_VALUE) { - validRotation = rotation?.done ?: true - } - listen(priority = Int.MIN_VALUE) { if (shouldCrouch) { shouldCrouch = false diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index 0525a85ae..78bcc4310 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -19,7 +19,6 @@ package com.lambda.interaction.request.rotation import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request -import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.visibilty.RotationTarget import com.lambda.threading.runSafe @@ -41,6 +40,6 @@ data class RotationRequest( override val done: Boolean get() = mode == RotationMode.None || runSafe { - target.verify(target) + target.verify() } == true } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index f77f2915a..37a82c170 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -35,7 +35,7 @@ import com.lambda.util.collections.updatableLazy */ data class RotationTarget( val hit: RequestedHit? = null, - val verify: RotationTarget.() -> Boolean = { hit?.verifyRotation() ?: true }, + val verify: () -> Boolean = { hit?.verifyRotation() ?: true }, private val buildRotation: SafeContext.() -> Rotation?, ) { val targetRotation = updatableLazy { From 5b0faf56336b59839f312a809764e179a760b349 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 2 Apr 2025 17:53:11 +0100 Subject: [PATCH 103/364] cleanup and added a rotation prediction for the next place context to prevent tick delay between placing blocks --- .../com/lambda/config/groups/PlaceSettings.kt | 2 - .../request/breaking/BreakManager.kt | 33 +++++------- .../request/placing/PlaceConfig.kt | 5 +- .../request/placing/PlaceManager.kt | 54 ++++++++++--------- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 5 +- 5 files changed, 48 insertions(+), 51 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 7d9932d26..1ea98325f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -29,8 +29,6 @@ class PlaceSettings( override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } - override val axisRotate - get() = airPlace.isEnabled() && axisRotateSetting override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 717bb20b0..6cfe0176b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -41,7 +41,6 @@ import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta @@ -88,10 +87,6 @@ object BreakManager : RequestHandler(), PositionBlocking { override val blockedPositions get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } - private var rotation: RotationRequest? = null - private val validRotation - get() = rotation?.done ?: true - private var blockBreakingCooldown = 0 fun Any.onBreak( @@ -160,11 +155,19 @@ object BreakManager : RequestHandler(), PositionBlocking { } } - requestRotate() - if (!validRotation) { - postEvent() - return@listen - } + breakingInfos + .firstOrNull { it?.breakConfig?.rotateForBreak == true } + ?.let { info -> + // If the simulation cant find a valid rotation to break the block, the existing break context stays and the keep ticks deplete until + if (info.context.rotation.keepTicks <= 0) null + else info.rotationConfig.request(info.context.rotation) + } + ?.let { rot -> + if (!rot.done) { + postEvent() + return@listen + } + } // Reversed so that the breaking order feels natural to the user as the primary break has to // be started after the secondary @@ -262,16 +265,6 @@ object BreakManager : RequestHandler(), PositionBlocking { false } - private fun requestRotate() { - rotation = breakingInfos - .firstOrNull { it?.breakConfig?.rotateForBreak == true } - ?.let { info -> - // If the simulation cant find a valid rotation to break the block, the existing break context stays and the keep ticks deplete until - if (info.context.rotation.keepTicks <= 0) null - else info.rotationConfig.request(info.context.rotation) - } - } - private fun handleRequestContext( requestCtx: BreakContext, request: BreakRequest diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index baf72f0cb..f801672bc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -25,9 +25,12 @@ abstract class PlaceConfig( priority: Priority ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean + val rotate + get() = rotateForPlace || axisRotate abstract val airPlace: AirPlaceMode protected abstract val axisRotateSetting: Boolean - abstract val axisRotate: Boolean + val axisRotate + get() = airPlace.isEnabled() && axisRotateSetting abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 6b167b49c..d4e98c997 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -111,9 +111,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) val placeConfig = request.buildConfig.placeSettings - - val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) - val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) val isSneaking = player.isSneaking val currentHotbarIndex = HotbarManager.serverSlot val placeContexts = request.placeContexts @@ -122,33 +119,42 @@ object PlaceManager : RequestHandler(), PositionBlocking { compareByDescending { it.hotbarIndex == currentHotbarIndex } .thenByDescending { it.sneak == isSneaking } ) - .take(takeCount) - placeContexts.forEach { ctx -> - val notSneaking = !player.isSneaking - val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) - if (ctx.sneak && notSneaking) { - shouldCrouch = true - postEvent() - return@listen - } - if ((placeConfig.rotateForPlace || placeConfig.axisRotate)) { - rotation = request.rotationConfig.request(ctx.rotation) - if (!ctx.rotation.target.verify()) { + val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) + val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) + val nextRotationPrediction = placeContexts.getOrNull(takeCount)?.rotation + + placeContexts + .take(takeCount) + .forEach { ctx -> + val notSneaking = !player.isSneaking + val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) + if (placeConfig.rotate) { + val rot = request.rotationConfig.request(ctx.rotation) + if (!rot.done) { + postEvent() + return@listen + } + } + if (ctx.sneak && notSneaking) { + shouldCrouch = true + postEvent() + return@listen + } + if (!hotbarRequest.done) { postEvent() return@listen } - } - if (!hotbarRequest.done) { - postEvent() - return@listen - } - val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } + activeThisTick = true } - activeThisTick = true + + nextRotationPrediction?.let { rot -> + request.rotationConfig.request(rot) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 7cf07d86e..b8ff3ff2d 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -172,13 +172,10 @@ class BuildTask @Ta5kBuilder constructor( return@listen } is PlaceResult.Place -> { - val takeCount = build.placeSettings - .placementsPerTick - .coerceAtMost(emptyPendingInteractionSlots) val placeResults = resultsNotBlocked .filterIsInstance() .distinctBy { it.blockPos } - .take(takeCount) + .take(emptyPendingInteractionSlots) build.placeSettings.request( PlaceRequest( From 8e26e36a3d57057d37aa31b192a67bdc36cd5053 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 2 Apr 2025 18:54:15 +0100 Subject: [PATCH 104/364] remove unused rotation variable in place manager --- .../com/lambda/interaction/request/placing/PlaceManager.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index d4e98c997..ed40c38a2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -34,7 +34,6 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.item @@ -70,8 +69,6 @@ object PlaceManager : RequestHandler(), PositionBlocking { it.pendingInteractionsList.remove(it.context) } - private var rotation: RotationRequest? = null - private var shouldCrouch = false override val blockedPositions From 3cd9d15072003a00be27e74d28175626c46dc726 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 3 Apr 2025 13:50:15 +0100 Subject: [PATCH 105/364] add swing check for breaking in creative --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 6cfe0176b..7829e74f8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -316,6 +316,10 @@ object BreakManager : RequestHandler(), PositionBlocking { onBlockBreak(info) PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } + val swing = info.breakConfig.swing + if (swing.isEnabled()) { + swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + } return true } From 199e42a78d2b5cc08eb4e154390d06839091c5ff Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 3 Apr 2025 19:25:35 +0100 Subject: [PATCH 106/364] if the best build result is not break or place and the pending interactions list is not empty, return --- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index b8ff3ff2d..0fded9831 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -107,13 +107,14 @@ class BuildTask @Ta5kBuilder constructor( .sorted() val bestResult = resultsNotBlocked.firstOrNull() ?: return@listen + if (bestResult !is BuildResult.Contextual && pendingInteractions.isNotEmpty()) + return@listen when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { - if (pendingInteractions.isNotEmpty()) return@listen if (blueprint is PropagatingBlueprint) { blueprint.next() return@listen From 9b189074c0512881ec5c5241055fa474ab3c6ae1 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 3 Apr 2025 20:23:25 +0100 Subject: [PATCH 107/364] clean up break handling in the build task --- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 0fded9831..b7c990b45 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -30,6 +30,7 @@ import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStruc import com.lambda.interaction.construction.blueprint.PropagatingBlueprint import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.blueprint.TickingBlueprint +import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult @@ -139,32 +140,27 @@ class BuildTask @Ta5kBuilder constructor( if (atMaxPendingInteractions) return@listen when (bestResult) { is BreakResult.Break -> { - val breakResults = resultsNotBlocked - .filterIsInstance() + val breakResults = resultsNotBlocked.filterIsInstance() + val requestContexts = arrayListOf() if (build.breakSettings.instantBreaksPerTick > 1) { val takeCount = build.breakSettings .instantBreaksPerTick .coerceAtMost(emptyPendingInteractionSlots) - val instantBreakResults = breakResults + breakResults .filter { it.context.instantBreak } .take(takeCount) + .let { instantBreakResults -> + requestContexts.addAll(instantBreakResults.map { it.context }) + } + } - if (instantBreakResults.isNotEmpty()) { - build.breakSettings.request( - BreakRequest( - instantBreakResults.map { it.context }, build, rotation, interact, inventory, hotbar, - pendingInteractionsList = pendingInteractions, - onBreak = { breaks++ }, - onItemDrop = onItemDrop - ) - ) - return@listen - } + if (requestContexts.isEmpty()) { + requestContexts.addAll(breakResults.map { it.context }) } val request = BreakRequest( - breakResults.map { it.context }, build, rotation, interact, inventory, hotbar, + requestContexts, build, rotation, interact, inventory, hotbar, pendingInteractionsList = pendingInteractions, onBreak = { breaks++ }, onItemDrop = onItemDrop From b3e21a4f89d3a5811ad4ab12f2bb87267b953cb3 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 4 Apr 2025 03:24:10 +0200 Subject: [PATCH 108/364] Smarter falling block break --- .../interaction/construction/context/BreakContext.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index daa5bc128..4b6c39a0d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -25,6 +25,8 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState +import net.minecraft.block.Blocks +import net.minecraft.block.FallingBlock import net.minecraft.client.network.ClientPlayerInteractionManager import net.minecraft.client.world.ClientWorld import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket @@ -56,14 +58,16 @@ data class BreakContext( fun exposedSides(ctx: SafeContext) = Direction.entries.filter { - ctx.world.isAir(result.blockPos.offset(it)) + ctx.world.isAir(expectedPos.offset(it)) } override val expectedState: BlockState = checkedState.fluidState.blockState override fun compareTo(other: BuildContext): Int { return when (other) { - is BreakContext -> compareBy { + is BreakContext -> compareByDescending { + if (it.checkedState.block is FallingBlock) it.expectedPos.y else 0 + }.thenBy { it.rotation.target.angleDistance }.compare(this, other) From 1012d894ac4c8d788267a414d1d6466e1e897350 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 4 Apr 2025 03:24:26 +0200 Subject: [PATCH 109/364] Smarter intermediate pathing --- .../construction/simulation/Simulation.kt | 6 +++-- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index d00c50568..3a8cea455 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -66,9 +66,11 @@ data class Simulation( blueprint.simulate(view, interact, rotation, inventory, build) } - fun goodPositions() = cache.filter { it.value.any { it.rank.ordinal < 4 } }.map { PossiblePos(it.key.toBlockPos()) } + fun goodPositions() = cache + .filter { entry -> entry.value.any { it.rank.ordinal < 4 } } + .map { PossiblePos(it.key.toBlockPos(), it.value.count { it.rank.ordinal < 4 }) } - class PossiblePos(val pos: BlockPos): Drawable { + class PossiblePos(val pos: BlockPos, val interactions: Int): Drawable { override fun SafeContext.buildRenderer() { withBox(Vec3d.ofBottomCenter(pos).playerBox(), Color(0, 255, 0, 50)) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index b7c990b45..7848760f3 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -18,6 +18,7 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.GoalBlock +import baritone.api.pathing.goals.GoalNear import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig @@ -55,6 +56,7 @@ import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.SlotUtils.hotbarAndStorage +import com.lambda.util.world.toFastVec import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos import java.util.concurrent.ConcurrentLinkedQueue @@ -95,13 +97,26 @@ class BuildTask @Ta5kBuilder constructor( listen { if (collectDrops()) return@listen - // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) + val resultPostions = results.map { it.blockPos } - TaskFlowModule.drawables = results + val sim = blueprint.simulation(interact, rotation, inventory, build) + BlockPos.iterateOutwards(player.blockPos, 2, 2, 2).forEach { + sim.simulate(it.toFastVec()) + } + val bestPos = sim.goodPositions() + .filter { it.pos !in resultPostions } + .maxByOrNull { it.interactions } + + val drawables = results .filterIsInstance() .plus(pendingInteractions.toList()) - // .plus(sim.goodPositions()) + .toMutableList() + + if (bestPos != null) { + drawables.add(bestPos) + } + TaskFlowModule.drawables = drawables val resultsNotBlocked = results .filter { result -> pendingInteractions.none { it.expectedPos == result.blockPos } } @@ -127,7 +142,6 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { if (!build.pathing) return@listen - val sim = blueprint.simulation(interact, rotation, inventory, build) val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) } @@ -137,6 +151,10 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { + bestPos?.let { + BaritoneUtils.setGoalAndPath(GoalNear(it.pos, 1)) + } + if (atMaxPendingInteractions) return@listen when (bestResult) { is BreakResult.Break -> { From 9fd7234b1c77ce91af59704fe3ae079d5d3a5b59 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 4 Apr 2025 02:39:50 +0100 Subject: [PATCH 110/364] simple packet mine as a wrapper for the break manager --- .../lambda/mixin/MinecraftClientMixin.java | 18 +++- .../module/modules/player/PacketMine.kt | 88 +++++++++++++++++++ .../modules/player/PacketMineRewrite.kt | 60 ------------- 3 files changed, 105 insertions(+), 61 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index ebc46893d..17e3c4ca0 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -23,10 +23,14 @@ import com.lambda.event.events.InventoryEvent; import com.lambda.event.events.TickEvent; import com.lambda.module.modules.player.Interact; +import com.lambda.module.modules.player.PacketMine; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; +import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.HitResult; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -41,6 +45,10 @@ public class MinecraftClientMixin { @Nullable public Screen currentScreen; + @Shadow @Nullable public HitResult crosshairTarget; + + @Shadow @Nullable public ClientPlayerEntity player; + @Inject(method = "tick", at = @At("HEAD")) void onTickPre(CallbackInfo ci) { EventFlow.post(new TickEvent.Pre()); @@ -90,8 +98,16 @@ private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) { } } + @Redirect(method = "doAttack()Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;swingHand(Lnet/minecraft/util/Hand;)V")) + private void redirectHandSwing(ClientPlayerEntity instance, Hand hand) { + if (this.crosshairTarget == null || this.player != null) return; + if (this.crosshairTarget.getType() != HitResult.Type.BLOCK || PacketMine.INSTANCE.isDisabled()) { + this.player.swingHand(hand); + } + } + @Redirect(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;isBreakingBlock()Z")) - boolean injectMultiActon(ClientPlayerInteractionManager instance) { + boolean redirectMultiActon(ClientPlayerInteractionManager instance) { if (instance == null) return true; if (Interact.INSTANCE.isEnabled() && Interact.getMultiAction()) return false; diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt new file mode 100644 index 000000000..e99876a72 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.module.modules.player + +import com.lambda.config.groups.BuildSettings +import com.lambda.config.groups.HotbarSettings +import com.lambda.config.groups.InteractionSettings +import com.lambda.config.groups.InventorySettings +import com.lambda.config.groups.RotationSettings +import com.lambda.event.events.PlayerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure +import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.construction.result.BreakResult +import com.lambda.interaction.construction.simulation.BuildSimulator.simulate +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.breaking.BreakRequest +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.world.raycast.InteractionMask +import java.util.concurrent.ConcurrentLinkedQueue + +object PacketMine : Module( + "Packet Mine", + "automatically breaks blocks, and does it faster", + setOf(ModuleTag.PLAYER) +) { + private val page by setting("Page", Page.Build) + + private val build = BuildSettings(this) { page == Page.Build } + private val rotation = RotationSettings(this) { page == Page.Rotation } + private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } + private val inventory = InventorySettings(this) { page == Page.Inventory } + private val hotbar = HotbarSettings(this) { page == Page.Hotbar } + + private val pendingInteractionsList = ConcurrentLinkedQueue() + + private var breaks = 0 + private var itemDrops = 0 + + init { + listen { event -> + event.cancel() + + val blockState = blockState(event.pos) + val buildResult = event.pos + .toStructure(TargetState.State(blockState.fluidState.blockState)) + .toBlueprint() + .simulate( + player.eyePos, + interact = interact, + rotation = rotation, + inventory = inventory, + build = build + ) + .minOrNull() ?: return@listen + + if (buildResult !is BreakResult.Break) return@listen + val request = BreakRequest( + listOf(buildResult.context), build, rotation, interact, inventory, hotbar, + pendingInteractionsList = pendingInteractionsList, + onBreak = { breaks++ } + ) { _ -> itemDrops++ } + build.breakSettings.request(request) + } + } + + enum class Page { + Build, Rotation, Interaction, Inventory, Hotbar + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt deleted file mode 100644 index 7bf28f178..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMineRewrite.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.module.modules.player - -import com.lambda.config.groups.BuildSettings -import com.lambda.event.events.PlayerEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.construction.blueprint.StaticBlueprint -import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint -import com.lambda.interaction.construction.verify.TargetState -import com.lambda.module.Module -import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run -import com.lambda.task.tasks.BuildTask -import com.lambda.task.tasks.BuildTask.Companion.build -import com.lambda.util.Communication.info - -object PacketMineRewrite : Module( - "Packet Mine Rewrite", - "automatically breaks blocks, and does it faster", - setOf(ModuleTag.PLAYER) -) { - private val buildConfig = BuildSettings(this) - - var blueprint: StaticBlueprint? = null - var task: BuildTask? = null - - init { - listen { - it.cancel() - blueprint = setOf( - player.blockPos.add(1, 0, 0), - player.blockPos.add(1, 1, 0), - player.blockPos.add(1, 0, 1), - player.blockPos.add(1, 1, 1), - player.blockPos.add(1, 0, -1), - player.blockPos.add(1, 1, -1), - ).associateWith { TargetState.Air }.toBlueprint() - task?.cancel() - - info("requesting $blueprint") - task = blueprint?.build(build = buildConfig)?.run() - } - } -} \ No newline at end of file From d32f6f5a8f9bcc361174ab27f5467405ba164b73 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 4 Apr 2025 03:44:19 +0200 Subject: [PATCH 111/364] Dont use pickFromInventory for now until 2b is fixed --- .../material/container/containers/MainHandContainer.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt index da830cb09..6bd300ba4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt @@ -63,7 +63,8 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { when (moveStack) { in player.hotbar -> swapToHotbarSlot(player.hotbar.indexOf(moveStack)) - in player.storage -> pickFromInventory(player.combined.indexOf(moveStack)) + // ToDo: Use pickFromInventory + in player.storage -> swap(player.combined.indexOf(moveStack), 0) } if (hand == Hand.OFF_HAND) swapHands() From a63fea2dea2ea0d48a81784e7690cf59d6c9b295 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Apr 2025 20:05:45 +0100 Subject: [PATCH 112/364] - added unsafe cancels option - BreakManager is now like other managers in the sense that you need to keep posting requests to keep a block breaking. - improved double break logic --- .../lambda/mixin/MinecraftClientMixin.java | 11 +- .../mixin/entity/ClientPlayerEntityMixin.java | 6 - .../com/lambda/config/groups/BreakSettings.kt | 1 + .../construction/context/BreakContext.kt | 24 -- .../request/breaking/BreakConfig.kt | 1 + .../request/breaking/BreakManager.kt | 309 +++++++++++------- .../request/breaking/BreakRequest.kt | 7 +- .../module/modules/player/PacketMine.kt | 68 +++- 8 files changed, 254 insertions(+), 173 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 17e3c4ca0..2d330b04f 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -44,10 +44,9 @@ public class MinecraftClientMixin { @Shadow @Nullable public Screen currentScreen; - - @Shadow @Nullable public HitResult crosshairTarget; - - @Shadow @Nullable public ClientPlayerEntity player; + @Shadow + @Nullable + public HitResult crosshairTarget; @Inject(method = "tick", at = @At("HEAD")) void onTickPre(CallbackInfo ci) { @@ -100,9 +99,9 @@ private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) { @Redirect(method = "doAttack()Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;swingHand(Lnet/minecraft/util/Hand;)V")) private void redirectHandSwing(ClientPlayerEntity instance, Hand hand) { - if (this.crosshairTarget == null || this.player != null) return; + if (this.crosshairTarget == null) return; if (this.crosshairTarget.getType() != HitResult.Type.BLOCK || PacketMine.INSTANCE.isDisabled()) { - this.player.swingHand(hand); + instance.swingHand(hand); } } diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index 52fe90617..e6b2d3f01 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -28,7 +28,6 @@ import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.MovementType; import net.minecraft.entity.damage.DamageSource; -import net.minecraft.util.Hand; import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -120,11 +119,6 @@ float fixHeldItemPitch(ClientPlayerEntity instance) { return Objects.requireNonNullElse(RotationManager.getHandPitch(), instance.getPitch()); } - @Inject(method = "swingHand", at = @At("HEAD"), cancellable = true) - void onSwingHandPre(Hand hand, CallbackInfo ci) { - if (EventFlow.post(new PlayerEvent.SwingHand(hand)).isCanceled()) ci.cancel(); - } - @Inject(method = "damage", at = @At("HEAD"), cancellable = true) public void damage(DamageSource source, float amount, CallbackInfoReturnable cir) { if (EventFlow.post(new PlayerEvent.Damage(source, amount)).isCanceled()) { diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index fc093b554..f72705f26 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -28,6 +28,7 @@ class BreakSettings( vis: () -> Boolean = { true } ) : BreakConfig(priority) { override val breakMode by c.setting("Break Mode", BreakMode.Vanilla) { vis() } + override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 4b6c39a0d..8665ab294 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -25,12 +25,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState -import net.minecraft.block.Blocks import net.minecraft.block.FallingBlock -import net.minecraft.client.network.ClientPlayerInteractionManager -import net.minecraft.client.world.ClientWorld -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -81,23 +76,4 @@ data class BreakContext( withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) withState(checkedState, expectedPos, sideColor, result.side) } - - fun startBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = - breakPacket(Action.START_DESTROY_BLOCK, world, interaction) - - fun stopBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = - breakPacket(Action.STOP_DESTROY_BLOCK, world, interaction) - - fun abortBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = - breakPacket(Action.ABORT_DESTROY_BLOCK, world, interaction) - - private fun breakPacket(action: Action, world: ClientWorld, interaction: ClientPlayerInteractionManager) = - interaction.sendSequencedPacket(world) { sequence: Int -> - PlayerActionC2SPacket( - action, - expectedPos, - result.side, - sequence - ) - } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index e66f4ff12..31742f2b8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -26,6 +26,7 @@ abstract class BreakConfig( priority: Priority = 0 ) : RequestConfig(priority) { abstract val breakMode: BreakMode + abstract val unsafeCancels: Boolean abstract val breakThreshold: Float abstract val doubleBreak: Boolean abstract val breakDelay: Int diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 7829e74f8..b0be0883e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -28,11 +28,7 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe -import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure -import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.construction.result.BreakResult -import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority @@ -55,15 +51,18 @@ import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.client.network.ClientPlayerInteractionManager import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.client.world.ClientWorld import net.minecraft.entity.ItemEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.ChunkSectionPos object BreakManager : RequestHandler(), PositionBlocking { private var primaryBreakingInfo: BreakInfo? @@ -76,12 +75,20 @@ object BreakManager : RequestHandler(), PositionBlocking { private val pendingBreaks = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { - info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") - if (!it.broken && it.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) { - mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + ) { info -> + mc.world?.let { world -> + val pos = info.context.expectedPos + val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)) + if (!loaded) return@let + + info("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") + + val awaitThenBreak = info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak + if (!info.broken && awaitThenBreak) { + world.setBlockState(info.context.expectedPos, info.context.checkedState) + } } - it.pendingInteractionsList.remove(it.context) + info.pendingInteractionsList.remove(info.context) } override val blockedPositions @@ -110,30 +117,40 @@ object BreakManager : RequestHandler(), PositionBlocking { preEvent() pendingBreaks.cleanUp() - breakingInfos.forEach { - it?.simulate(player) - } - - if (updateRequest()) currentRequest?.let request@ { request -> - if (isOnBreakCooldown()) { - blockBreakingCooldown-- - return@request - } - + updateRequest() + if (isOnBreakCooldown()) { + blockBreakingCooldown-- + } else if (currentRequest == null) { + breakingInfos.forEach { it?.cancelBreak(player, world, interaction) } + } else currentRequest?.let request@ { request -> val breakConfig = request.buildConfig.breakSettings - val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) - if (maxBreaksThisTick <= 0) return@request - val validContexts = request.contexts + val validNewContexts = request.contexts .filter { ctx -> canAccept(ctx) } .sortedWith( compareByDescending { it.instantBreak } .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } - ) - .take(maxBreaksThisTick) + ).toMutableList() + + breakingInfos + .filterNotNull() + .forEach { info -> + validNewContexts.find { + ctx -> ctx.expectedPos == info.context.expectedPos + }?.let { ctx -> + info.updateInfo(ctx, request) + validNewContexts.remove(ctx) + return@forEach + } - val instantBreaks = validContexts - .take(breakConfig.instantBreaksPerTick) + info.cancelBreak(player, world, interaction) + } + if (atMaxBreakingInfos(request.buildConfig.breakSettings)) return@request + + val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) + if (maxBreaksThisTick <= 0) return@request + val instantBreaks = validNewContexts + .take(breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaksThisTick)) .filter { it.instantBreak } if (instantBreaks.isNotEmpty()) { @@ -142,23 +159,27 @@ object BreakManager : RequestHandler(), PositionBlocking { if (!request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)).done) return@request } val breakInfo = handleRequestContext(ctx, request) ?: return@request + request.onAccept?.invoke(ctx.expectedPos) updateBlockBreakingProgress(breakInfo) activeThisTick = true } if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request } - validContexts - .filter { it.instantBreak.not() } + validNewContexts + .filter { !it.instantBreak } .forEach { ctx -> - if (handleRequestContext(ctx, request) == null) return@request + handleRequestContext(ctx, request) ?: return@request + request.onAccept?.invoke(ctx.expectedPos) + if (atMaxBreakingInfos(request.buildConfig.breakSettings)) return@request } } breakingInfos - .firstOrNull { it?.breakConfig?.rotateForBreak == true } + .firstOrNull { it?.breakConfig?.rotateForBreak == true && !it.redundant } ?.let { info -> - // If the simulation cant find a valid rotation to break the block, the existing break context stays and the keep ticks deplete until + // If the simulation cant find a valid rotation to the block, + // the existing break context stays and the keep ticks deplete until rotations stop if (info.context.rotation.keepTicks <= 0) null else info.rotationConfig.request(info.context.rotation) } @@ -175,9 +196,9 @@ object BreakManager : RequestHandler(), PositionBlocking { .filterNotNull() .reversed() .forEach { info -> - if (!info.requestHotbarSwap()) return@forEach + if (!info.redundant && !info.requestHotbarSwap()) return@forEach updateBlockBreakingProgress(info) - activeThisTick = true + if (!info.redundant) activeThisTick = true } postEvent() @@ -252,6 +273,11 @@ object BreakManager : RequestHandler(), PositionBlocking { } } + private fun atMaxBreakingInfos(breakConfig: BreakConfig): Boolean { + val possibleBreakingCount = if (breakConfig.doubleBreak) 2 else 1 + return breakingInfos.take(possibleBreakingCount).all { it != null } + } + private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) val correctMaterial = info.context.checkedState.block == entity.stack.item.block @@ -265,17 +291,15 @@ object BreakManager : RequestHandler(), PositionBlocking { false } - private fun handleRequestContext( + private fun SafeContext.handleRequestContext( requestCtx: BreakContext, request: BreakRequest ): BreakInfo? { val breakInfo = BreakInfo(requestCtx, BreakType.Primary, request) primaryBreakingInfo?.let { primaryInfo -> - if (!primaryInfo.breakConfig.doubleBreak - || primaryInfo.startedWithSecondary - || secondaryBreakingInfo != null - || requestCtx.hotbarIndex != primaryInfo.context.hotbarIndex) { - return null + if (!breakInfo.breakConfig.doubleBreak || secondaryBreakingInfo != null) { + primaryInfo.abortBreakPacket(world, interaction) + return@let } if (!primaryInfo.breaking) { @@ -283,12 +307,9 @@ object BreakManager : RequestHandler(), PositionBlocking { return secondaryBreakingInfo } - primaryInfo.type = BreakType.Secondary - secondaryBreakingInfo = primaryInfo - primaryBreakingInfo = breakInfo - - setPendingInteractionsLimits(request.buildConfig) - return primaryBreakingInfo + primaryInfo.stopBreakPacket(world, interaction) + primaryInfo.makeSecondary() + return@let } primaryBreakingInfo = breakInfo @@ -301,16 +322,26 @@ object BreakManager : RequestHandler(), PositionBlocking { pendingBreaks.setDecayTime(buildConfig.interactionTimeout * 50L) } - private fun SafeContext.canAccept(ctx: BreakContext) = - pendingBreaks.none { it.context.expectedPos == ctx.expectedPos } - && breakingInfos.none { info -> info?.context?.expectedPos == ctx.expectedPos } - && !blockState(ctx.expectedPos).isAir + private fun SafeContext.canAccept(ctx: BreakContext): Boolean { + if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false + + breakingInfos.firstOrNull { it != null && !it.redundant } + ?.let { info -> + if ( ctx.hotbarIndex != info.context.hotbarIndex) return false + } + + return !blockState(ctx.expectedPos).isAir + } private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo): Boolean { val ctx = info.context val hitResult = ctx.result if (gamemode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { + if (info.redundant) { + onBlockBreak(info) + return true + } setBreakCooldown(info.breakConfig.breakDelay) interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) @@ -349,6 +380,15 @@ object BreakManager : RequestHandler(), PositionBlocking { player.mainHandStack ) * info.breakingTicks + val overBreakThreshold = progress >= info.getBreakThreshold() + + if (info.redundant) { + if (overBreakThreshold) { + onBlockBreak(info) + } + return true + } + if (info.breakConfig.sounds) { if (info.soundsCooldown % 4.0f == 0.0f) { val blockSoundGroup = blockState.soundGroup @@ -371,15 +411,18 @@ object BreakManager : RequestHandler(), PositionBlocking { } if (info.breakConfig.breakingTexture) { - setBreakingTextureStage(info) + info.setBreakingTextureStage(player, world) } val swing = info.breakConfig.swing - - if (progress >= info.getBreakThreshold()) { - interaction.sendSequencedPacket(world) { sequence -> + if (overBreakThreshold) { + if (info.type == BreakType.Primary) { + interaction.sendSequencedPacket(world) { sequence -> + onBlockBreak(info) + PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + } + } else { onBlockBreak(info) - PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) setBreakCooldown(info.breakConfig.breakDelay) @@ -412,31 +455,26 @@ object BreakManager : RequestHandler(), PositionBlocking { blockState.onBlockBreakStart(world, ctx.expectedPos, player) } - val breakingDelta = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, player.mainHandStack) - if (notAir && breakingDelta >= info.getBreakThreshold()) { + val breakDelta = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, player.mainHandStack) + if (notAir && breakDelta >= info.getBreakThreshold()) { onBlockBreak(info) } else { info.apply { breaking = true breakingTicks = 1 soundsCooldown = 0.0f + if (breakConfig.breakingTexture) { + setBreakingTextureStage(player, world) + } } - if (info.breakConfig.breakingTexture) { - setBreakingTextureStage(info) - } - if (secondaryBreakingInfo != null) - primaryBreakingInfo?.startedWithSecondary = true } if (info.breakConfig.breakMode == BreakMode.Packet) { - ctx.stopBreakPacket(world, interaction) - ctx.startBreakPacket(world, interaction) - ctx.stopBreakPacket(world, interaction) - } else { - ctx.startBreakPacket(world, interaction) - if (breakingDelta < 1 && (breakingDelta >= 0.7 || info.breakConfig.doubleBreak)) { - ctx.stopBreakPacket(world, interaction) - } + info.stopBreakPacket(world, interaction) + } + info.startBreakPacket(world, interaction) + if (info.type == BreakType.Secondary || (breakDelta < 1 && breakDelta >= info.breakConfig.breakThreshold)) { + info.stopBreakPacket(world, interaction) } return true @@ -488,18 +526,11 @@ object BreakManager : RequestHandler(), PositionBlocking { val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) - if (info.breakConfig.breakingTexture) setBreakingTextureStage(info, -1) + if (info.breakConfig.breakingTexture) info.setBreakingTextureStage(player, world, -1) return setState } - private fun SafeContext.setBreakingTextureStage( - info: BreakInfo, - stage: Int = info.getBreakTextureProgress(player, world) - ) { - world.setBlockBreakingInfo(player.id, info.context.expectedPos, stage) - } - private fun isOnBreakCooldown() = blockBreakingCooldown > 0 private fun setBreakCooldown(cooldown: Int) { blockBreakingCooldown = cooldown @@ -508,24 +539,22 @@ object BreakManager : RequestHandler(), PositionBlocking { data class BreakInfo( var context: BreakContext, var type: BreakType, - val request: BreakRequest + var request: BreakRequest ) { - // I hate this... - val breakConfig = request.buildConfig.breakSettings - val rotationConfig = request.rotationConfig - private val interactionConfig = request.interactionConfig - private val buildConfig = request.buildConfig - private val inventoryConfig = request.inventoryConfig - private val hotbarConfig = request.hotbarConfig - - val pendingInteractionsList = request.pendingInteractionsList - private val onBreak = request.onBreak - private val onItemDrop = request.onItemDrop + val breakConfig get() = request.buildConfig.breakSettings + val rotationConfig get() = request.rotationConfig + + val pendingInteractionsList get() = request.pendingInteractionsList + private val onCancel get() = request.onCancel + private val onBreak get() = request.onBreak + private val onItemDrop get() = request.onItemDrop var breaking = false var breakingTicks = 0 var soundsCooldown = 0.0f - var startedWithSecondary = false + + val redundant + get() = type == BreakType.RedundantSecondary @Volatile var broken = false @@ -538,7 +567,7 @@ object BreakManager : RequestHandler(), PositionBlocking { fun internalOnBreak() { synchronized(this) { broken = true - onBreak() + onBreak?.invoke(context.expectedPos) item?.let { item -> onItemDrop?.invoke(item) } @@ -554,53 +583,99 @@ object BreakManager : RequestHandler(), PositionBlocking { } } - fun requestHotbarSwap() = - hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done - - fun simulate(player: ClientPlayerEntity) { - val result = context.expectedPos - .toStructure( - if (!context.checkedState.fluidState.isEmpty) { - TargetState.State(context.checkedState.fluidState.blockState) - } else { - TargetState.Air - } - ) - .toBlueprint() - .simulate(player.eyePos, interactionConfig, rotationConfig, inventoryConfig, buildConfig) - .minOrNull() - ?: return - - if (result is BreakResult.Break) { - context = result.context + fun updateInfo(context: BreakContext, request: BreakRequest) { + this.context = context + this.request = request + if (redundant) { + type = BreakType.Secondary } } - fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { + fun requestHotbarSwap() = + request.hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done + + fun setBreakingTextureStage( + player: ClientPlayerEntity, + world: ClientWorld, + stage: Int = getBreakTextureProgress(player, world) + ) { + world.setBlockBreakingInfo(player.id, context.expectedPos, stage) + } + + private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) - val progress = (breakDelta * breakingTicks) / breakConfig.breakThreshold + val progress = (breakDelta * breakingTicks) / getBreakThreshold() return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } - fun nullify() = type.nullify() - fun getBreakThreshold() = type.getBreakThreshold(breakConfig) + + fun makeSecondary() { + if (secondaryBreakingInfo === this) return + secondaryBreakingInfo = this.apply { + type = BreakType.Secondary + } + primaryBreakingInfo = null + } + + fun cancelBreak(player: ClientPlayerEntity, world: ClientWorld, interaction: ClientPlayerInteractionManager) { + setBreakingTextureStage(player, world, -1) + if (type == BreakType.Primary) { + abortBreakPacket(world, interaction) + nullify() + return + } + if (type == BreakType.Secondary && breakConfig.unsafeCancels) { + makeRedundant() + } + } + + private fun makeRedundant() { + makeSecondary() + type = BreakType.RedundantSecondary + onCancel?.invoke(context.expectedPos) + } + + fun nullify() { + type.nullify() + if (!broken) onCancel?.invoke(context.expectedPos) + } + + fun startBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.START_DESTROY_BLOCK, world, interaction) + + fun stopBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.STOP_DESTROY_BLOCK, world, interaction) + + fun abortBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.ABORT_DESTROY_BLOCK, world, interaction) + + private fun breakPacket(action: Action, world: ClientWorld, interaction: ClientPlayerInteractionManager) = + interaction.sendSequencedPacket(world) { sequence: Int -> + PlayerActionC2SPacket( + action, + context.expectedPos, + context.result.side, + sequence + ) + } } enum class BreakType(val index: Int) { Primary(0), - Secondary(1); + Secondary(1), + RedundantSecondary(2); fun getBreakThreshold(breakConfig: BreakConfig) = when (this) { Primary -> breakConfig.breakThreshold - Secondary -> 1.0f + else -> 1.0f } fun nullify() = when (this) { Primary -> primaryBreakingInfo = null - Secondary -> secondaryBreakingInfo = null + else -> secondaryBreakingInfo = null } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index d9057c929..d6cd1a09f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -29,6 +29,7 @@ import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import net.minecraft.entity.ItemEntity +import net.minecraft.util.math.BlockPos data class BreakRequest( val contexts: Collection, @@ -39,8 +40,10 @@ data class BreakRequest( val hotbarConfig: HotbarConfig, val prio: Priority = 0, val pendingInteractionsList: MutableCollection, - val onBreak: () -> Unit, - val onItemDrop: ((ItemEntity) -> Unit)?, + val onAccept: ((BlockPos) -> Unit)? = null, + val onCancel: ((BlockPos) -> Unit)? = null, + val onBreak: ((BlockPos) -> Unit)? = null, + val onItemDrop: ((ItemEntity) -> Unit)? = null, ) : Request(prio) { override val done: Boolean get() = runSafe { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index e99876a72..e698972cf 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -22,9 +22,10 @@ import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings +import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.result.BreakResult @@ -35,6 +36,7 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.InteractionMask +import net.minecraft.util.math.BlockPos import java.util.concurrent.ConcurrentLinkedQueue object PacketMine : Module( @@ -45,6 +47,7 @@ object PacketMine : Module( private val page by setting("Page", Page.Build) private val build = BuildSettings(this) { page == Page.Build } + private val breakConfig = build.breakSettings private val rotation = RotationSettings(this) { page == Page.Rotation } private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } private val inventory = InventorySettings(this) { page == Page.Inventory } @@ -55,33 +58,62 @@ object PacketMine : Module( private var breaks = 0 private var itemDrops = 0 + private val breakingPositions = arrayOfNulls(2) + private var hitPos: BlockPos? = null + init { - listen { event -> + listen { it.cancel() } + listen { event -> event.cancel() + if (breakingPositions.any { it == event.pos }) return@listen + if (breakConfig.doubleBreak && breakingPositions[1] == null) { + breakingPositions[1] = breakingPositions[0] + } + breakingPositions[0] = null + hitPos = event.pos + } - val blockState = blockState(event.pos) - val buildResult = event.pos - .toStructure(TargetState.State(blockState.fluidState.blockState)) - .toBlueprint() - .simulate( - player.eyePos, - interact = interact, - rotation = rotation, - inventory = inventory, - build = build - ) - .minOrNull() ?: return@listen + listen { + val requestPositions = arrayListOf().apply { addAll(breakingPositions.filterNotNull()) } + hitPos?.let { pos -> + requestPositions.add(pos) + hitPos = null + } - if (buildResult !is BreakResult.Break) return@listen val request = BreakRequest( - listOf(buildResult.context), build, rotation, interact, inventory, hotbar, + breakContexts(requestPositions), build, rotation, interact, inventory, hotbar, pendingInteractionsList = pendingInteractionsList, - onBreak = { breaks++ } + onAccept = { breakingPositions[0] = it }, + onCancel = { nullifyBreakPos(it) }, + onBreak = { breaks++; nullifyBreakPos(it) } ) { _ -> itemDrops++ } - build.breakSettings.request(request) + breakConfig.request(request) + } + } + + private fun nullifyBreakPos(pos: BlockPos) { + breakingPositions.forEachIndexed { index, breakPos -> + if (breakPos == pos) { + breakingPositions[index] = null + return + } } } + private fun SafeContext.breakContexts(breakPositions: Collection) = + breakPositions + .associateWith { TargetState.State(blockState(it).fluidState.blockState) } + .toBlueprint() + .simulate( + player.eyePos, + interact = interact, + rotation = rotation, + inventory = inventory, + build = build + ) + .filterIsInstance() + .map { it.context } + enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } From 58c2e7f86ffe974cb480ac05e76b5b9a1ce7e86e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 8 Apr 2025 05:12:41 +0100 Subject: [PATCH 113/364] rewrote HotbarManager to allow for multiple swaps per tick - added swapDelay, and swapsPerTick settings --- .../lambda/config/groups/HotbarSettings.kt | 4 +- .../interaction/request/RequestHandler.kt | 2 +- .../request/breaking/BreakManager.kt | 164 +++++++++--------- .../request/hotbar/HotbarConfig.kt | 14 +- .../request/hotbar/HotbarManager.kt | 99 +++++++++-- .../request/hotbar/HotbarRequest.kt | 25 ++- .../request/placing/PlaceManager.kt | 63 +++---- .../lambda/module/modules/debug/SilentSwap.kt | 9 +- .../util/collections/LimitedDecayQueue.kt | 4 +- 9 files changed, 240 insertions(+), 144 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt index 5d7391bcd..0d9f97e71 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt @@ -27,5 +27,7 @@ class HotbarSettings( vis: () -> Boolean = { true } ) : HotbarConfig(priority) { override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", vis) - override var switchPause by c.setting("Switch Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) + override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", vis) + override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() } + override var swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 7515a96bb..433209e0f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap */ abstract class RequestHandler { - private val requestMap = ConcurrentHashMap, R>() + protected val requestMap = ConcurrentHashMap, R>() /** * Represents if the handler performed any external actions within this tick diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index b0be0883e..3cd2534c6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -23,7 +23,6 @@ import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent -import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -113,94 +112,100 @@ object BreakManager : RequestHandler(), PositionBlocking { } init { - listen(priority = Int.MIN_VALUE + 1) { + listen { preEvent() pendingBreaks.cleanUp() updateRequest() - if (isOnBreakCooldown()) { - blockBreakingCooldown-- - } else if (currentRequest == null) { - breakingInfos.forEach { it?.cancelBreak(player, world, interaction) } - } else currentRequest?.let request@ { request -> - val breakConfig = request.buildConfig.breakSettings - - val validNewContexts = request.contexts - .filter { ctx -> canAccept(ctx) } - .sortedWith( - compareByDescending { it.instantBreak } - .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } - ).toMutableList() + val request = currentRequest ?: breakingInfos.filterNotNull().firstOrNull()?.request ?: return@listen + + val hotbarRequest = HotbarRequest(request.hotbarConfig) { + if (isOnBreakCooldown()) { + blockBreakingCooldown-- + } else if (currentRequest == null) { + breakingInfos.forEach { it?.cancelBreak(player, world, interaction) } + } else currentRequest?.let request@ { currentRequest -> + val breakConfig = currentRequest.buildConfig.breakSettings + val validNewContexts = currentRequest.contexts + .filter { ctx -> canAccept(ctx) } + .sortedWith( + compareByDescending { it.instantBreak } + .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } + ).toMutableList() + + breakingInfos + .filterNotNull() + .forEach { info -> + validNewContexts.find { + ctx -> ctx.expectedPos == info.context.expectedPos + }?.let { ctx -> + info.updateInfo(ctx, currentRequest) + validNewContexts.remove(ctx) + return@forEach + } + + info.cancelBreak(player, world, interaction) + } + if (atMaxBreakingInfos(currentRequest.buildConfig.breakSettings)) return@request + + val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) + if (maxBreaksThisTick <= 0) return@request + val instantBreaks = validNewContexts + .take(breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaksThisTick)) + .filter { it.instantBreak } + + if (instantBreaks.isNotEmpty()) { + instantBreaks.forEach { ctx -> + if (ctx.hotbarIndex != HotbarManager.serverSlot) { + if (!swapTo(ctx.hotbarIndex)) return@request + } + val breakInfo = handleRequestContext(ctx, currentRequest) ?: return@request + currentRequest.onAccept?.invoke(ctx.expectedPos) + updateBlockBreakingProgress(breakInfo) + activeThisTick = true + } + if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request + } - breakingInfos - .filterNotNull() - .forEach { info -> - validNewContexts.find { - ctx -> ctx.expectedPos == info.context.expectedPos - }?.let { ctx -> - info.updateInfo(ctx, request) - validNewContexts.remove(ctx) - return@forEach + validNewContexts + .filter { !it.instantBreak } + .forEach { ctx -> + handleRequestContext(ctx, currentRequest) ?: return@request + currentRequest.onAccept?.invoke(ctx.expectedPos) + if (atMaxBreakingInfos(currentRequest.buildConfig.breakSettings)) return@request } + } - info.cancelBreak(player, world, interaction) + breakingInfos + .firstOrNull { it?.breakConfig?.rotateForBreak == true && !it.redundant } + ?.let { info -> + // If the simulation cant find a valid rotation to the block, + // the existing break context stays and the keep ticks deplete until rotations stop + if (info.context.rotation.keepTicks <= 0) null + else info.rotationConfig.request(info.context.rotation) } - if (atMaxBreakingInfos(request.buildConfig.breakSettings)) return@request - - val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) - if (maxBreaksThisTick <= 0) return@request - val instantBreaks = validNewContexts - .take(breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaksThisTick)) - .filter { it.instantBreak } - - if (instantBreaks.isNotEmpty()) { - instantBreaks.forEach { ctx -> - if (ctx.hotbarIndex != HotbarManager.serverSlot) { - if (!request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)).done) return@request + ?.let { rot -> + if (!rot.done) { + return@HotbarRequest } - val breakInfo = handleRequestContext(ctx, request) ?: return@request - request.onAccept?.invoke(ctx.expectedPos) - updateBlockBreakingProgress(breakInfo) - activeThisTick = true } - if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request - } - validNewContexts - .filter { !it.instantBreak } - .forEach { ctx -> - handleRequestContext(ctx, request) ?: return@request - request.onAccept?.invoke(ctx.expectedPos) - if (atMaxBreakingInfos(request.buildConfig.breakSettings)) return@request + // Reversed so that the breaking order feels natural to the user as the primary break has to + // be started after the secondary + breakingInfos + .filterNotNull() + .reversed() + .forEach { info -> + if (!info.redundant && !swapTo(info.context.hotbarIndex)) return@HotbarRequest + updateBlockBreakingProgress(info) + if (!info.redundant) activeThisTick = true } + done() } + request.hotbarConfig.request(hotbarRequest) + } - breakingInfos - .firstOrNull { it?.breakConfig?.rotateForBreak == true && !it.redundant } - ?.let { info -> - // If the simulation cant find a valid rotation to the block, - // the existing break context stays and the keep ticks deplete until rotations stop - if (info.context.rotation.keepTicks <= 0) null - else info.rotationConfig.request(info.context.rotation) - } - ?.let { rot -> - if (!rot.done) { - postEvent() - return@listen - } - } - - // Reversed so that the breaking order feels natural to the user as the primary break has to - // be started after the secondary - breakingInfos - .filterNotNull() - .reversed() - .forEach { info -> - if (!info.redundant && !info.requestHotbarSwap()) return@forEach - updateBlockBreakingProgress(info) - if (!info.redundant) activeThisTick = true - } - + listen { postEvent() } @@ -345,7 +350,7 @@ object BreakManager : RequestHandler(), PositionBlocking { setBreakCooldown(info.breakConfig.breakDelay) interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) - PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } val swing = info.breakConfig.swing if (swing.isEnabled()) { @@ -419,7 +424,7 @@ object BreakManager : RequestHandler(), PositionBlocking { if (info.type == BreakType.Primary) { interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) - PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) + PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } } else { onBlockBreak(info) @@ -442,7 +447,7 @@ object BreakManager : RequestHandler(), PositionBlocking { if (gamemode.isCreative) { interaction.sendSequencedPacket(world) { sequence: Int -> onBlockBreak(info) - PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) + PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } setBreakCooldown(info.breakConfig.breakDelay) return true @@ -591,9 +596,6 @@ object BreakManager : RequestHandler(), PositionBlocking { } } - fun requestHotbarSwap() = - request.hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done - fun setBreakingTextureStage( player: ClientPlayerEntity, world: ClientWorld, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt index 140ec6d0f..d19715d07 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -34,12 +34,24 @@ abstract class HotbarConfig( */ abstract val keepTicks: Int + /** + * The delay, in ticks, between swapping hotbar selections + */ + abstract val swapDelay: Int + + /** + * The amount of hotbar selection swaps that can happen per tick + * + * Only makes a difference if swapDelay is set to 0 + */ + abstract val swapsPerTick: Int + /** * The delay in ticks to pause actions after switching to the slot. * * Affects the validity state of the request */ - abstract var switchPause: Int + abstract var swapPause: Int /** * Registers a hotbar request with the HotbarManager. diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index f1cda3553..1c8de5b2a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request.hotbar +import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.EventFlow.post @@ -26,9 +27,9 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler -import com.lambda.threading.runSafe import com.lambda.mixin.entity.PlayerInventoryMixin import com.lambda.mixin.render.InGameHudMixin +import com.lambda.threading.runSafe /** * See mixins: @@ -43,6 +44,11 @@ object HotbarManager : RequestHandler(), Loadable { override fun load() = "Loaded Hotbar Manager" + private var currentSlotInfo: SlotInfo? = null + private var maxSwapsThisTick = 0 + private var swapsThisTick = 0 + private var swapDelay = 0 + fun Any.onHotbarUpdate( alwaysListen: Boolean = false, priority: Priority = 0, @@ -60,27 +66,96 @@ object HotbarManager : RequestHandler(), Loadable { } init { - listen { - it.slot = currentRequest?.slot ?: return@listen - } - listen(priority = Int.MIN_VALUE) { preEvent() - if (updateRequest()) interaction.syncSelectedSlot() - if (currentRequest != null) activeThisTick = true + + swapsThisTick = 0 + if (swapDelay > 0) swapDelay-- + + if (requestMap.isNotEmpty()) { + val sortedRequests = requestMap.toSortedMap(compareByDescending { it.priority }).values + + sortedRequests.first().let { request -> + maxSwapsThisTick = request.hotbarConfig.swapsPerTick + } + + sortedRequests.forEach { request -> + val actionSequence = request.actionSequence + HotbarActionSequence(request).apply { actionSequence() } + } + } + currentSlotInfo?.let { current -> + if (current.keepTicks <= 0) { + currentSlotInfo = null + interaction.syncSelectedSlot() + } + } + requestMap.clear() postEvent() } - listen { - val request = currentRequest ?: return@listen + listen(priority = Int.MIN_VALUE) { + swapsThisTick = 0 + val slotInfo = currentSlotInfo ?: return@listen - request.keepTicks-- - request.switchPause-- + slotInfo.swapPauseAge++ + slotInfo.activeRequestAge++ + slotInfo.keepTicks-- - if (request.keepTicks <= 0) { + if (slotInfo.keepTicks <= 0) { currentRequest = null } } + + listen(priority = Int.MIN_VALUE) { + it.slot = currentSlotInfo?.slot ?: return@listen + } + } + + @DslMarker + private annotation class ActionSequence + + @ActionSequence + class HotbarActionSequence(val request: HotbarRequest) { + @ActionSequence + fun swapTo( + slot: Int, + keepTicks: Int = request.hotbarConfig.keepTicks + ): Boolean { + request.swapSlot = SlotInfo(slot, keepTicks, request.hotbarConfig.swapDelay) + if (slot != currentSlotInfo?.slot) { + if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return false + + currentSlotInfo?.let { current -> + if (current.activeRequestAge == 0 && (current.keepTicks > 0 || current.swapPause > 0)) { + request.failedSwap = true + return false + } + } + + swapsThisTick++ + swapDelay = request.hotbarConfig.swapDelay + } else currentSlotInfo?.let { current -> + request.swapSlot?.swapPauseAge = current.swapPauseAge + } + currentSlotInfo = request.swapSlot + mc.interactionManager?.syncSelectedSlot() + return true + } + + @ActionSequence + fun done() { + request.instantActionsComplete = true + } + } + + data class SlotInfo( + val slot: Int, + var keepTicks: Int = 3, + var swapPause: Int = 0 + ) { + var activeRequestAge = 0 + var swapPauseAge = 0 } override fun preEvent() = UpdateManagerEvent.Hotbar.Pre().post() diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index 9d8b620dd..158997140 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -21,13 +21,24 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request class HotbarRequest( - val slot: Int, + val hotbarConfig: HotbarConfig, priority: Priority = 0, - var keepTicks: Int = 3, - var switchPause: Int = 0, + val actionSequence: HotbarManager.HotbarActionSequence.() -> Unit, ) : Request(priority) { - override val done: Boolean get() = - // The request has to be valid at least for 1 tick - // (if for some dumb reason the switch pause is bigger than the decay time) - HotbarManager.serverSlot == slot && (switchPause <= 0 || keepTicks <= 0) + var failedSwap = false + var swapSlot: HotbarManager.SlotInfo? = null + + var instantActionsComplete = false + override val done: Boolean + get() = swapSlot?.let { it.slot == HotbarManager.serverSlot && it.activeRequestAge >= it.swapPause } ?: true + + constructor ( + slot: Int, + hotbarConfig: HotbarConfig, + priority: Priority = 0 + ) : this( + hotbarConfig, + priority, + { if (swapTo(slot, hotbarConfig.keepTicks.coerceAtLeast(1))) done() } + ) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index ed40c38a2..fa67cfa60 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -21,7 +21,6 @@ import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.MovementEvent -import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -91,7 +90,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { } init { - listen(priority = Int.MIN_VALUE) { + listen { preEvent() pendingPlacements.cleanUp() @@ -119,42 +118,44 @@ object PlaceManager : RequestHandler(), PositionBlocking { val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) - val nextRotationPrediction = placeContexts.getOrNull(takeCount)?.rotation - - placeContexts - .take(takeCount) - .forEach { ctx -> - val notSneaking = !player.isSneaking - val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)) - if (placeConfig.rotate) { - val rot = request.rotationConfig.request(ctx.rotation) - if (!rot.done) { - postEvent() - return@listen + + val hotbarRequest = HotbarRequest(request.hotbarConfig) { + placeContexts + .take(takeCount) + .forEach { ctx -> + val notSneaking = !player.isSneaking + val swapped = swapTo(ctx.hotbarIndex) + if (placeConfig.rotate) { + val rot = request.rotationConfig.request(ctx.rotation) + if (!rot.done) { + return@HotbarRequest + } + } + if (ctx.sneak && notSneaking) { + shouldCrouch = true + return@HotbarRequest + } + if (!swapped) { + return@HotbarRequest } - } - if (ctx.sneak && notSneaking) { - shouldCrouch = true - postEvent() - return@listen - } - if (!hotbarRequest.done) { - postEvent() - return@listen - } - val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } + activeThisTick = true } - activeThisTick = true - } - nextRotationPrediction?.let { rot -> - request.rotationConfig.request(rot) + placeContexts.getOrNull(takeCount)?.rotation?.let { rot -> + request.rotationConfig.request(rot) + } + done() } + request.hotbarConfig.request(hotbarRequest) } + } + listen(priority = Int.MIN_VALUE) { postEvent() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt index c41d5c37e..4990294bf 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt @@ -19,18 +19,11 @@ package com.lambda.module.modules.debug import com.lambda.config.groups.HotbarSettings import com.lambda.event.events.PlayerEvent -import com.lambda.event.events.RenderEvent -import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.Communication.info -import com.lambda.util.world.blockSearch -import net.minecraft.block.Blocks -import net.minecraft.util.math.Vec3i -import java.awt.Color object SilentSwap : Module( name = "SilentSwap", @@ -41,7 +34,7 @@ object SilentSwap : Module( init { listen { - if (!hotbar.request(HotbarRequest(0)).done) { + if (!hotbar.request(HotbarRequest(0, hotbar)).done) { it.cancel() return@listen } diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index db9404cf6..dbab57415 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentLinkedQueue * * @param E The type of elements held in this collection. * @property sizeLimit The maximum number of elements the queue can hold at any given time. - * @property maxAge The age (in milliseconds) after which elements are considered expired and are removed from the queue. + * @property maxAge The activeRequestAge (in milliseconds) after which elements are considered expired and are removed from the queue. * @property onDecay Lambda function that is executed on decay of element [E]. */ class LimitedDecayQueue( @@ -115,7 +115,7 @@ class LimitedDecayQueue( /** * Sets the decay time for the elements in the queue. The decay time determines the - * maximum age that any element in the queue can have before being considered expired + * maximum activeRequestAge that any element in the queue can have before being considered expired * and removed. Updates the internal state and triggers a cleanup of expired elements. * * @param decayTime The decay time in milliseconds. Must be a non-negative value. From 8a46bd20739420840cd8a199f4fea7f3d414c572 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 8 Apr 2025 15:44:19 +0100 Subject: [PATCH 114/364] give activeThisTick a protected setter and remove the getter method --- .../kotlin/com/lambda/interaction/request/RequestHandler.kt | 5 ++--- .../com/lambda/interaction/request/placing/PlaceManager.kt | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 433209e0f..62324c298 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -34,7 +34,8 @@ abstract class RequestHandler { /** * Represents if the handler performed any external actions within this tick */ - protected var activeThisTick = false + var activeThisTick = false + protected set /** * The currently active request. @@ -47,8 +48,6 @@ abstract class RequestHandler { } } - fun activeThisTick() = activeThisTick - /** * Registers a new request with the given configuration. * diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index fa67cfa60..d2ae3094f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -101,7 +101,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { } currentRequest?.let request@ { request -> - if (BreakManager.activeThisTick()) return@request + if (BreakManager.activeThisTick) return@request pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements) pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) From 6e2691d887866f534a31fb2345d3dd71c7134418 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 8 Apr 2025 19:06:16 +0100 Subject: [PATCH 115/364] hotbar cleanup and small fix --- .../com/lambda/interaction/request/hotbar/HotbarManager.kt | 5 +++-- .../com/lambda/interaction/request/hotbar/HotbarRequest.kt | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 1c8de5b2a..df49462eb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -124,11 +124,12 @@ object HotbarManager : RequestHandler(), Loadable { ): Boolean { request.swapSlot = SlotInfo(slot, keepTicks, request.hotbarConfig.swapDelay) if (slot != currentSlotInfo?.slot) { - if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return false + if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) { + return false + } currentSlotInfo?.let { current -> if (current.activeRequestAge == 0 && (current.keepTicks > 0 || current.swapPause > 0)) { - request.failedSwap = true return false } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index 158997140..7777d59ee 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -25,12 +25,11 @@ class HotbarRequest( priority: Priority = 0, val actionSequence: HotbarManager.HotbarActionSequence.() -> Unit, ) : Request(priority) { - var failedSwap = false var swapSlot: HotbarManager.SlotInfo? = null var instantActionsComplete = false override val done: Boolean - get() = swapSlot?.let { it.slot == HotbarManager.serverSlot && it.activeRequestAge >= it.swapPause } ?: true + get() = swapSlot?.let { it.slot == HotbarManager.serverSlot && it.swapPauseAge >= it.swapPause } ?: true constructor ( slot: Int, From b803bf199e2a0b141d102f838afe61dde8b4ade5 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 8 Apr 2025 23:09:14 +0100 Subject: [PATCH 116/364] sequence mode settings and an implementation in the place manager. Also a couple fixes for the hotbar manager --- .../com/lambda/config/groups/BreakSettings.kt | 1 + .../com/lambda/config/groups/BuildConfig.kt | 6 + .../com/lambda/config/groups/PlaceSettings.kt | 1 + .../request/breaking/BreakConfig.kt | 1 + .../request/hotbar/HotbarManager.kt | 12 +- .../request/hotbar/HotbarRequest.kt | 2 +- .../request/placing/PlaceConfig.kt | 1 + .../request/placing/PlaceManager.kt | 106 +++++++++++------- 8 files changed, 85 insertions(+), 45 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index f72705f26..d4599986e 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -32,6 +32,7 @@ class BreakSettings( override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } + override val sequenceMode by c.setting("Break Sequence Mode", BuildConfig.InteractSequenceMode.PostMovement, "The sub-tick timing at which break actions are performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 74d857371..2976e48dc 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -36,4 +36,10 @@ interface BuildConfig { Server, Client } + + enum class InteractSequenceMode { + TickStart, + Vanilla, + PostMovement, + } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 1ea98325f..7d37da389 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -29,6 +29,7 @@ class PlaceSettings( override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } + override val sequenceMode by c.setting("Place Sequence Mode", BuildConfig.InteractSequenceMode.PostMovement, "The sub-tick timing at which break actions are performed") { vis() } override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 31742f2b8..e7b18e495 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -30,6 +30,7 @@ abstract class BreakConfig( abstract val breakThreshold: Float abstract val doubleBreak: Boolean abstract val breakDelay: Int + abstract val sequenceMode: BuildConfig.InteractSequenceMode abstract val swing: SwingMode abstract val swingType: BuildConfig.SwingType abstract val sounds: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index df49462eb..a8bce1e18 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -69,7 +69,6 @@ object HotbarManager : RequestHandler(), Loadable { listen(priority = Int.MIN_VALUE) { preEvent() - swapsThisTick = 0 if (swapDelay > 0) swapDelay-- if (requestMap.isNotEmpty()) { @@ -122,14 +121,14 @@ object HotbarManager : RequestHandler(), Loadable { slot: Int, keepTicks: Int = request.hotbarConfig.keepTicks ): Boolean { - request.swapSlot = SlotInfo(slot, keepTicks, request.hotbarConfig.swapDelay) + request.swapSlot = SlotInfo(slot, keepTicks, request.hotbarConfig.swapPause) if (slot != currentSlotInfo?.slot) { if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) { return false } currentSlotInfo?.let { current -> - if (current.activeRequestAge == 0 && (current.keepTicks > 0 || current.swapPause > 0)) { + if (current.activeRequestAge == 0 && current.keepTicks > 0) { return false } } @@ -141,7 +140,7 @@ object HotbarManager : RequestHandler(), Loadable { } currentSlotInfo = request.swapSlot mc.interactionManager?.syncSelectedSlot() - return true + return request.done } @ActionSequence @@ -152,11 +151,12 @@ object HotbarManager : RequestHandler(), Loadable { data class SlotInfo( val slot: Int, - var keepTicks: Int = 3, - var swapPause: Int = 0 + var keepTicks: Int, + var swapPause: Int ) { var activeRequestAge = 0 var swapPauseAge = 0 + val swapPaused get() = swapPauseAge < swapPause } override fun preEvent() = UpdateManagerEvent.Hotbar.Pre().post() diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index 7777d59ee..f3bf80346 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -29,7 +29,7 @@ class HotbarRequest( var instantActionsComplete = false override val done: Boolean - get() = swapSlot?.let { it.slot == HotbarManager.serverSlot && it.swapPauseAge >= it.swapPause } ?: true + get() = swapSlot?.let { it.slot == HotbarManager.serverSlot && !it.swapPaused } ?: true constructor ( slot: Int, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index f801672bc..036ee8346 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -31,6 +31,7 @@ abstract class PlaceConfig( protected abstract val axisRotateSetting: Boolean val axisRotate get() = airPlace.isEnabled() && axisRotateSetting + abstract val sequenceMode: BuildConfig.InteractSequenceMode abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index d2ae3094f..e9cdaf86c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request.placing import com.lambda.Lambda.mc +import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.MovementEvent @@ -33,6 +34,7 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.item @@ -44,6 +46,7 @@ import com.lambda.util.player.isItemOnCooldown import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.pattern.CachedBlockPosition +import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.item.BlockItem import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack @@ -68,7 +71,19 @@ object PlaceManager : RequestHandler(), PositionBlocking { it.pendingInteractionsList.remove(it.context) } - private var shouldCrouch = false + private var potentialPlacements = listOf() + private var nextPredictedRotation: RotationRequest? = null + + private var hotbarRequest: HotbarRequest? = null + private val swappedTo: (Int) -> Boolean = { slot -> + hotbarRequest?.let { hotbarRequest -> + hotbarRequest.swapSlot?.slot == slot && hotbarRequest.done + } ?: false + } + + private var shouldSneak = false + private val validSneak: (player: ClientPlayerEntity) -> Boolean = + { player -> shouldSneak == player.isSneaking } override val blockedPositions get() = pendingPlacements.map { it.context.expectedPos } @@ -102,11 +117,11 @@ object PlaceManager : RequestHandler(), PositionBlocking { currentRequest?.let request@ { request -> if (BreakManager.activeThisTick) return@request + val placeConfig = request.buildConfig.placeSettings - pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements) + pendingPlacements.setMaxSize(placeConfig.maxPendingPlacements) pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) - val placeConfig = request.buildConfig.placeSettings val isSneaking = player.isSneaking val currentHotbarIndex = HotbarManager.serverSlot val placeContexts = request.placeContexts @@ -118,50 +133,41 @@ object PlaceManager : RequestHandler(), PositionBlocking { val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) - - val hotbarRequest = HotbarRequest(request.hotbarConfig) { - placeContexts - .take(takeCount) - .forEach { ctx -> - val notSneaking = !player.isSneaking - val swapped = swapTo(ctx.hotbarIndex) - if (placeConfig.rotate) { - val rot = request.rotationConfig.request(ctx.rotation) - if (!rot.done) { - return@HotbarRequest - } - } - if (ctx.sneak && notSneaking) { - shouldCrouch = true - return@HotbarRequest - } - if (!swapped) { - return@HotbarRequest - } - - val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") - } - activeThisTick = true - } - - placeContexts.getOrNull(takeCount)?.rotation?.let { rot -> - request.rotationConfig.request(rot) + nextPredictedRotation = if (request.rotationConfig.rotate) placeContexts.getOrNull(takeCount)?.rotation else null + + hotbarRequest = HotbarRequest(request.hotbarConfig) { + potentialPlacements = placeContexts.take(takeCount) + potentialPlacements.forEach { ctx -> + swapTo(ctx.hotbarIndex) + if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) + if (ctx.sneak) shouldSneak = true + if (placeConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart) return@HotbarRequest + if (!attemptContextPlace(ctx, request)) return@HotbarRequest } + requestNextPredictedRotation(request) done() } - request.hotbarConfig.request(hotbarRequest) + hotbarRequest?.let { hotbarRequest -> + request.hotbarConfig.request(hotbarRequest) + } + if (potentialPlacements.isNotEmpty()) activeThisTick = true } } - listen(priority = Int.MIN_VALUE) { - postEvent() + //ToDo: add mixin for vanilla place timings + + listen { + currentRequest?.let { request -> + if (request.buildConfig.placeSettings.sequenceMode == BuildConfig.InteractSequenceMode.PostMovement) { + handlePlacementContexts(request) + postEvent() + } + } } listen(priority = Int.MIN_VALUE) { - if (shouldCrouch) { - shouldCrouch = false + if (shouldSneak) { + shouldSneak = false it.input.sneaking = true } } @@ -186,6 +192,30 @@ object PlaceManager : RequestHandler(), PositionBlocking { } } + private fun SafeContext.handlePlacementContexts(request: PlaceRequest): Boolean { + potentialPlacements.forEach { ctx -> + if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) + if (ctx.sneak) shouldSneak = true + if (!attemptContextPlace(ctx, request)) return false + } + requestNextPredictedRotation(request) + return true + } + + private fun SafeContext.attemptContextPlace(ctx: PlaceContext, request: PlaceRequest): Boolean { + if (!swappedTo(ctx.hotbarIndex) || !ctx.rotation.done || !validSneak(player)) return false + + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } + return true + } + + private fun requestNextPredictedRotation(request: PlaceRequest) { + nextPredictedRotation?.let { rot -> request.rotationConfig.request(rot) } + } + private fun canPlace(placeContext: PlaceContext) = pendingPlacements.none { pending -> pending.context.expectedPos == placeContext.expectedPos From 37c28b9e37b103acfc7782e9b6b1f33085803493 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 9 Apr 2025 15:27:39 +0100 Subject: [PATCH 117/364] cleanup --- .../interaction/request/breaking/BreakInfo.kt | 135 +++++++ .../request/breaking/BreakManager.kt | 365 +++++++----------- .../request/hotbar/HotbarManager.kt | 1 - .../request/placing/PlaceManager.kt | 76 ++-- 4 files changed, 317 insertions(+), 260 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt new file mode 100644 index 000000000..793116c20 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.client.network.ClientPlayerInteractionManager +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.ItemEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action + +data class BreakInfo( + var context: BreakContext, + var type: BreakType, + var request: BreakRequest +) { + val breakConfig get() = request.buildConfig.breakSettings + val rotationConfig get() = request.rotationConfig + + val pendingInteractionsList get() = request.pendingInteractionsList + private val onCancel get() = request.onCancel + private val onBreak get() = request.onBreak + private val onItemDrop get() = request.onItemDrop + + var breaking = false + var breakingTicks = 0 + var soundsCooldown = 0.0f + + val redundant + get() = type == BreakType.RedundantSecondary + + @Volatile + var broken = false + private set + private var item: ItemEntity? = null + + val callbacksCompleted + @Synchronized get() = broken && (onItemDrop == null || item != null) + + fun internalOnBreak() { + synchronized(this) { + broken = true + onBreak?.invoke(context.expectedPos) + item?.let { item -> + onItemDrop?.invoke(item) + } + } + } + + fun internalOnItemDrop(item: ItemEntity) { + synchronized(this) { + this.item = item + if (broken) { + onItemDrop?.invoke(item) + } + } + } + + fun internalOnCancel() { + onCancel?.invoke(context.expectedPos) + } + + fun updateInfo(context: BreakContext, request: BreakRequest) { + this.context = context + this.request = request + if (redundant) { + type = BreakType.Secondary + } + } + + fun setBreakingTextureStage( + player: ClientPlayerEntity, + world: ClientWorld, + stage: Int = getBreakTextureProgress(player, world) + ) { + world.setBlockBreakingInfo(player.id, context.expectedPos, stage) + } + + private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { + val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) + val progress = (breakDelta * breakingTicks) / getBreakThreshold() + return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 + } + + fun getBreakThreshold() = type.getBreakThreshold(breakConfig) + + fun startBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.START_DESTROY_BLOCK, world, interaction) + + fun stopBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.STOP_DESTROY_BLOCK, world, interaction) + + fun abortBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = + breakPacket(Action.ABORT_DESTROY_BLOCK, world, interaction) + + private fun breakPacket(action: Action, world: ClientWorld, interaction: ClientPlayerInteractionManager) = + interaction.sendSequencedPacket(world) { sequence: Int -> + PlayerActionC2SPacket( + action, + context.expectedPos, + context.result.side, + sequence + ) + } +} + +enum class BreakType(val index: Int) { + Primary(0), + Secondary(1), + RedundantSecondary(2); + + fun getBreakThreshold(breakConfig: BreakConfig) = + when (this) { + Primary -> breakConfig.breakThreshold + else -> 1.0f + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 3cd2534c6..694182b70 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -34,9 +34,12 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.interaction.request.breaking.BreakType.Primary import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.BlockUtils.fluidState @@ -49,13 +52,9 @@ import com.lambda.util.player.gamemode import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock -import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.client.network.ClientPlayerInteractionManager import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance -import net.minecraft.client.world.ClientWorld import net.minecraft.entity.ItemEntity -import net.minecraft.entity.player.PlayerEntity import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.sound.SoundCategory @@ -64,13 +63,14 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkSectionPos object BreakManager : RequestHandler(), PositionBlocking { - private var primaryBreakingInfo: BreakInfo? - get() = breakingInfos[0] - set(value) { breakingInfos[0] = value } - private var secondaryBreakingInfo: BreakInfo? - get() = breakingInfos[1] - set(value) { breakingInfos[1] = value } - private val breakingInfos = arrayOfNulls(2) + private var primaryBreak: BreakInfo? + get() = breakInfos[0] + set(value) { breakInfos[0] = value } + private var secondaryBreak: BreakInfo? + get() = breakInfos[1] + set(value) { breakInfos[1] = value } + private val breakInfos = arrayOfNulls(2) + private var liveBreakInfos = listOf() private val pendingBreaks = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L @@ -91,10 +91,20 @@ object BreakManager : RequestHandler(), PositionBlocking { } override val blockedPositions - get() = breakingInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } + get() = breakInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } private var blockBreakingCooldown = 0 + private var hotbarRequest: HotbarRequest? = null + private val swapped get() = hotbarRequest?.done == true + + private var rotation: RotationRequest? = null + private val validRotation get() = rotation?.done != false + + private var newBreaks = mutableListOf() + private var instantBreaks = listOf() + private var excessInstantBreaks = false + fun Any.onBreak( alwaysListen: Boolean = false, priority: Priority = 0, @@ -117,96 +127,67 @@ object BreakManager : RequestHandler(), PositionBlocking { pendingBreaks.cleanUp() updateRequest() - val request = currentRequest ?: breakingInfos.filterNotNull().firstOrNull()?.request ?: return@listen + val hotbarConfig = currentRequest?.hotbarConfig + ?: breakInfos.firstOrNull { it != null }?.request?.hotbarConfig + ?: return@listen - val hotbarRequest = HotbarRequest(request.hotbarConfig) { + hotbarRequest = HotbarRequest(hotbarConfig) { if (isOnBreakCooldown()) { blockBreakingCooldown-- } else if (currentRequest == null) { - breakingInfos.forEach { it?.cancelBreak(player, world, interaction) } - } else currentRequest?.let request@ { currentRequest -> - val breakConfig = currentRequest.buildConfig.breakSettings - val validNewContexts = currentRequest.contexts + breakInfos.forEach { it?.cancelBreak() } + } else currentRequest?.let request@ { request -> + val breakConfig = request.buildConfig.breakSettings + + newBreaks = request.contexts .filter { ctx -> canAccept(ctx) } .sortedWith( compareByDescending { it.instantBreak } .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } ).toMutableList() - breakingInfos - .filterNotNull() - .forEach { info -> - validNewContexts.find { - ctx -> ctx.expectedPos == info.context.expectedPos - }?.let { ctx -> - info.updateInfo(ctx, currentRequest) - validNewContexts.remove(ctx) - return@forEach - } - - info.cancelBreak(player, world, interaction) - } - if (atMaxBreakingInfos(currentRequest.buildConfig.breakSettings)) return@request - - val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size) - if (maxBreaksThisTick <= 0) return@request - val instantBreaks = validNewContexts - .take(breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaksThisTick)) - .filter { it.instantBreak } - - if (instantBreaks.isNotEmpty()) { - instantBreaks.forEach { ctx -> - if (ctx.hotbarIndex != HotbarManager.serverSlot) { - if (!swapTo(ctx.hotbarIndex)) return@request - } - val breakInfo = handleRequestContext(ctx, currentRequest) ?: return@request - currentRequest.onAccept?.invoke(ctx.expectedPos) - updateBlockBreakingProgress(breakInfo) - activeThisTick = true - } - if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request - } + refreshOrCancelBreaks(newBreaks, request) + if (atMaxBreakInfos(breakConfig)) return@request - validNewContexts - .filter { !it.instantBreak } - .forEach { ctx -> - handleRequestContext(ctx, currentRequest) ?: return@request - currentRequest.onAccept?.invoke(ctx.expectedPos) - if (atMaxBreakingInfos(currentRequest.buildConfig.breakSettings)) return@request - } - } + val maxBreaks = getMaxBreaks(breakConfig) + if (maxBreaks <= 0) return@request + val maxInstantBreaks = breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaks) - breakingInfos - .firstOrNull { it?.breakConfig?.rotateForBreak == true && !it.redundant } - ?.let { info -> - // If the simulation cant find a valid rotation to the block, - // the existing break context stays and the keep ticks deplete until rotations stop - if (info.context.rotation.keepTicks <= 0) null - else info.rotationConfig.request(info.context.rotation) - } - ?.let { rot -> - if (!rot.done) { - return@HotbarRequest - } + val uncappedInstantBreaks = newBreaks.filter { it.instantBreak } + instantBreaks = uncappedInstantBreaks.take(maxInstantBreaks) + excessInstantBreaks = uncappedInstantBreaks.size > instantBreaks.size + + instantBreaks.forEach { ctx -> + if (!swapTo(ctx.hotbarIndex)) return@request + val breakInfo = handleNewBreak(ctx, request) ?: return@request + request.onAccept?.invoke(ctx.expectedPos) + updateBreakProgress(breakInfo) } + if (excessInstantBreaks) return@request + processNewBreaks(newBreaks, request) + } + + updateLiveBreakInfos() + rotation = liveBreakInfos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> + info.rotationConfig.request(info.context.rotation) + } // Reversed so that the breaking order feels natural to the user as the primary break has to // be started after the secondary - breakingInfos - .filterNotNull() + liveBreakInfos .reversed() .forEach { info -> - if (!info.redundant && !swapTo(info.context.hotbarIndex)) return@HotbarRequest - updateBlockBreakingProgress(info) - if (!info.redundant) activeThisTick = true + if (!swapTo(info.context.hotbarIndex) || !validRotation) return@HotbarRequest + updateBreakProgress(info) } done() } - request.hotbarConfig.request(hotbarRequest) - } - - listen { - postEvent() + hotbarRequest?.let { hotbarRequest -> + hotbarConfig.request(hotbarRequest) + } + if (instantBreaks.isNotEmpty() || liveBreakInfos.isNotEmpty()) { + activeThisTick = true + } } listen(priority = Int.MIN_VALUE + 1) { event -> @@ -233,7 +214,7 @@ object BreakManager : RequestHandler(), PositionBlocking { return@listen } - breakingInfos + breakInfos .filterNotNull() .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> @@ -265,22 +246,60 @@ object BreakManager : RequestHandler(), PositionBlocking { return@listen } - breakingInfos + breakInfos .filterNotNull() .firstOrNull { info -> matchesBlockItem(info, it.entity) } ?.internalOnItemDrop(it.entity) } listenUnsafe(priority = Int.MIN_VALUE + 1) { - breakingInfos.forEach { it?.nullify() } + breakInfos.forEach { it?.nullify() } pendingBreaks.clear() setBreakCooldown(0) } } - private fun atMaxBreakingInfos(breakConfig: BreakConfig): Boolean { + private fun refreshOrCancelBreaks(newContexts: MutableCollection, request: BreakRequest) { + breakInfos + .filterNotNull() + .forEach { info -> + newContexts.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> + info.updateInfo(ctx, request) + newContexts.remove(ctx) + return@forEach + } + + info.cancelBreak() + } + } + + private fun SafeContext.processNewBreaks(newBreaks: Collection, request: BreakRequest) { + newBreaks + .filter { !it.instantBreak } + .forEach { ctx -> + handleNewBreak(ctx, request) ?: return + request.onAccept?.invoke(ctx.expectedPos) + if (atMaxBreakInfos(request.buildConfig.breakSettings)) return + } + } + + private fun SafeContext.updateLiveBreakInfos() { + liveBreakInfos = breakInfos + .filterNotNull() + .filter { + if (it.redundant) { + updateBreakProgress(it) + false + } else true + } + } + + private fun getMaxBreaks(breakConfig: BreakConfig): Int = + breakConfig.maxPendingBreaks - (breakInfos.count { it != null } + pendingBreaks.size) + + private fun atMaxBreakInfos(breakConfig: BreakConfig): Boolean { val possibleBreakingCount = if (breakConfig.doubleBreak) 2 else 1 - return breakingInfos.take(possibleBreakingCount).all { it != null } + return breakInfos.take(possibleBreakingCount).all { it != null } } private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { @@ -296,20 +315,20 @@ object BreakManager : RequestHandler(), PositionBlocking { false } - private fun SafeContext.handleRequestContext( + private fun SafeContext.handleNewBreak( requestCtx: BreakContext, request: BreakRequest ): BreakInfo? { - val breakInfo = BreakInfo(requestCtx, BreakType.Primary, request) - primaryBreakingInfo?.let { primaryInfo -> - if (!breakInfo.breakConfig.doubleBreak || secondaryBreakingInfo != null) { + val breakInfo = BreakInfo(requestCtx, Primary, request) + primaryBreak?.let { primaryInfo -> + if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) { primaryInfo.abortBreakPacket(world, interaction) return@let } if (!primaryInfo.breaking) { - secondaryBreakingInfo = breakInfo.apply { type = BreakType.Secondary } - return secondaryBreakingInfo + secondaryBreak = breakInfo.apply { type = BreakType.Secondary } + return secondaryBreak } primaryInfo.stopBreakPacket(world, interaction) @@ -317,12 +336,12 @@ object BreakManager : RequestHandler(), PositionBlocking { return@let } - primaryBreakingInfo = breakInfo - setPendingInteractionsLimits(request.buildConfig) - return primaryBreakingInfo + primaryBreak = breakInfo + setPendingBreaksLimits(request.buildConfig) + return primaryBreak } - private fun setPendingInteractionsLimits(buildConfig: BuildConfig) { + private fun setPendingBreaksLimits(buildConfig: BuildConfig) { pendingBreaks.setMaxSize(buildConfig.breakSettings.maxPendingBreaks) pendingBreaks.setDecayTime(buildConfig.interactionTimeout * 50L) } @@ -330,7 +349,7 @@ object BreakManager : RequestHandler(), PositionBlocking { private fun SafeContext.canAccept(ctx: BreakContext): Boolean { if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false - breakingInfos.firstOrNull { it != null && !it.redundant } + breakInfos.firstOrNull { it != null && !it.redundant } ?.let { info -> if ( ctx.hotbarIndex != info.context.hotbarIndex) return false } @@ -338,7 +357,7 @@ object BreakManager : RequestHandler(), PositionBlocking { return !blockState(ctx.expectedPos).isAir } - private fun SafeContext.updateBlockBreakingProgress(info: BreakInfo): Boolean { + private fun SafeContext.updateBreakProgress(info: BreakInfo): Boolean { val ctx = info.context val hitResult = ctx.result @@ -421,7 +440,7 @@ object BreakManager : RequestHandler(), PositionBlocking { val swing = info.breakConfig.swing if (overBreakThreshold) { - if (info.type == BreakType.Primary) { + if (info.type == Primary) { interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) @@ -541,146 +560,44 @@ object BreakManager : RequestHandler(), PositionBlocking { blockBreakingCooldown = cooldown } - data class BreakInfo( - var context: BreakContext, - var type: BreakType, - var request: BreakRequest - ) { - val breakConfig get() = request.buildConfig.breakSettings - val rotationConfig get() = request.rotationConfig - - val pendingInteractionsList get() = request.pendingInteractionsList - private val onCancel get() = request.onCancel - private val onBreak get() = request.onBreak - private val onItemDrop get() = request.onItemDrop - - var breaking = false - var breakingTicks = 0 - var soundsCooldown = 0.0f - - val redundant - get() = type == BreakType.RedundantSecondary - - @Volatile - var broken = false - private set - private var item: ItemEntity? = null - - val callbacksCompleted - @Synchronized get() = broken && (onItemDrop == null || item != null) - - fun internalOnBreak() { - synchronized(this) { - broken = true - onBreak?.invoke(context.expectedPos) - item?.let { item -> - onItemDrop?.invoke(item) - } - } - } - - fun internalOnItemDrop(item: ItemEntity) { - synchronized(this) { - this.item = item - if (broken) { - onItemDrop?.invoke(item) - } - } - } - - fun updateInfo(context: BreakContext, request: BreakRequest) { - this.context = context - this.request = request - if (redundant) { - type = BreakType.Secondary - } - } - - fun setBreakingTextureStage( - player: ClientPlayerEntity, - world: ClientWorld, - stage: Int = getBreakTextureProgress(player, world) - ) { - world.setBlockBreakingInfo(player.id, context.expectedPos, stage) - } - - private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { - val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) - val progress = (breakDelta * breakingTicks) / getBreakThreshold() - return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 - } - - fun getBreakThreshold() = type.getBreakThreshold(breakConfig) - - fun makeSecondary() { - if (secondaryBreakingInfo === this) return - secondaryBreakingInfo = this.apply { - type = BreakType.Secondary - } - primaryBreakingInfo = null + private fun BreakInfo.makeSecondary() { + if (secondaryBreak === this) return + secondaryBreak = this.apply { + type = BreakType.Secondary } + primaryBreak = null + } - fun cancelBreak(player: ClientPlayerEntity, world: ClientWorld, interaction: ClientPlayerInteractionManager) { + private fun BreakInfo.cancelBreak() = + runSafe { setBreakingTextureStage(player, world, -1) - if (type == BreakType.Primary) { + if (type == Primary) { abortBreakPacket(world, interaction) nullify() - return + return@runSafe } if (type == BreakType.Secondary && breakConfig.unsafeCancels) { makeRedundant() } } - private fun makeRedundant() { - makeSecondary() - type = BreakType.RedundantSecondary - onCancel?.invoke(context.expectedPos) - } - - fun nullify() { - type.nullify() - if (!broken) onCancel?.invoke(context.expectedPos) - } - - fun startBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = - breakPacket(Action.START_DESTROY_BLOCK, world, interaction) - - fun stopBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = - breakPacket(Action.STOP_DESTROY_BLOCK, world, interaction) - - fun abortBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = - breakPacket(Action.ABORT_DESTROY_BLOCK, world, interaction) - - private fun breakPacket(action: Action, world: ClientWorld, interaction: ClientPlayerInteractionManager) = - interaction.sendSequencedPacket(world) { sequence: Int -> - PlayerActionC2SPacket( - action, - context.expectedPos, - context.result.side, - sequence - ) - } + private fun BreakInfo.nullify() { + type.nullify() + if (!broken) internalOnCancel() } - enum class BreakType(val index: Int) { - Primary(0), - Secondary(1), - RedundantSecondary(2); - - fun getBreakThreshold(breakConfig: BreakConfig) = - when (this) { - Primary -> breakConfig.breakThreshold - else -> 1.0f - } - - fun nullify() = - when (this) { - Primary -> primaryBreakingInfo = null - else -> secondaryBreakingInfo = null - } + private fun BreakInfo.makeRedundant() { + makeSecondary() + type = BreakType.RedundantSecondary + internalOnCancel() } + private fun BreakType.nullify() = + when (this) { + Primary -> primaryBreak = null + else -> secondaryBreak = null + } + override fun preEvent() = UpdateManagerEvent.Break.Pre().post() override fun postEvent() = UpdateManagerEvent.Break.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index a8bce1e18..2c900c480 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -68,7 +68,6 @@ object HotbarManager : RequestHandler(), Loadable { init { listen(priority = Int.MIN_VALUE) { preEvent() - if (swapDelay > 0) swapDelay-- if (requestMap.isNotEmpty()) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index e9cdaf86c..4860f6845 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -21,10 +21,12 @@ import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post +import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.MovementEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.verify.TargetState @@ -115,43 +117,43 @@ object PlaceManager : RequestHandler(), PositionBlocking { return@listen } - currentRequest?.let request@ { request -> - if (BreakManager.activeThisTick) return@request - val placeConfig = request.buildConfig.placeSettings - - pendingPlacements.setMaxSize(placeConfig.maxPendingPlacements) - pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) - - val isSneaking = player.isSneaking - val currentHotbarIndex = HotbarManager.serverSlot - val placeContexts = request.placeContexts - .filter { canPlace(it) } - .sortedWith( - compareByDescending { it.hotbarIndex == currentHotbarIndex } - .thenByDescending { it.sneak == isSneaking } - ) - - val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) - val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) - nextPredictedRotation = if (request.rotationConfig.rotate) placeContexts.getOrNull(takeCount)?.rotation else null - - hotbarRequest = HotbarRequest(request.hotbarConfig) { - potentialPlacements = placeContexts.take(takeCount) - potentialPlacements.forEach { ctx -> - swapTo(ctx.hotbarIndex) - if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) - if (ctx.sneak) shouldSneak = true - if (placeConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart) return@HotbarRequest - if (!attemptContextPlace(ctx, request)) return@HotbarRequest - } - requestNextPredictedRotation(request) - done() - } - hotbarRequest?.let { hotbarRequest -> - request.hotbarConfig.request(hotbarRequest) + val request = currentRequest ?: return@listen + + if (BreakManager.activeThisTick) return@listen + val placeConfig = request.buildConfig.placeSettings + + pendingPlacements.setMaxSize(placeConfig.maxPendingPlacements) + pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) + + val isSneaking = player.isSneaking + val currentHotbarIndex = HotbarManager.serverSlot + val placeContexts = request.placeContexts + .filter { canPlace(it) } + .sortedWith( + compareByDescending { it.hotbarIndex == currentHotbarIndex } + .thenByDescending { it.sneak == isSneaking } + ) + + val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) + val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) + nextPredictedRotation = if (request.rotationConfig.rotate) placeContexts.getOrNull(takeCount)?.rotation else null + + hotbarRequest = HotbarRequest(request.hotbarConfig) { + potentialPlacements = placeContexts.take(takeCount) + potentialPlacements.forEach { ctx -> + swapTo(ctx.hotbarIndex) + if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) + if (ctx.sneak) shouldSneak = true + if (placeConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart) return@HotbarRequest + if (!attemptContextPlace(ctx, request)) return@HotbarRequest } - if (potentialPlacements.isNotEmpty()) activeThisTick = true + requestNextPredictedRotation(request) + done() + } + hotbarRequest?.let { hotbarRequest -> + request.hotbarConfig.request(hotbarRequest) } + if (potentialPlacements.isNotEmpty()) activeThisTick = true } //ToDo: add mixin for vanilla place timings @@ -190,6 +192,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { return@listen } } + + listenUnsafe { + pendingPlacements.clear() + } } private fun SafeContext.handlePlacementContexts(request: PlaceRequest): Boolean { From fbd505b78872c1d1878462e8ef71dd342ce48512 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 9 Apr 2025 18:20:16 +0100 Subject: [PATCH 118/364] abide by break sequence timings --- .../request/breaking/BreakManager.kt | 133 +++++++++++------- 1 file changed, 79 insertions(+), 54 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 694182b70..94cb35b2c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -23,6 +23,7 @@ import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent +import com.lambda.event.events.MovementEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -131,57 +132,7 @@ object BreakManager : RequestHandler(), PositionBlocking { ?: breakInfos.firstOrNull { it != null }?.request?.hotbarConfig ?: return@listen - hotbarRequest = HotbarRequest(hotbarConfig) { - if (isOnBreakCooldown()) { - blockBreakingCooldown-- - } else if (currentRequest == null) { - breakInfos.forEach { it?.cancelBreak() } - } else currentRequest?.let request@ { request -> - val breakConfig = request.buildConfig.breakSettings - - newBreaks = request.contexts - .filter { ctx -> canAccept(ctx) } - .sortedWith( - compareByDescending { it.instantBreak } - .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } - ).toMutableList() - - refreshOrCancelBreaks(newBreaks, request) - if (atMaxBreakInfos(breakConfig)) return@request - - val maxBreaks = getMaxBreaks(breakConfig) - if (maxBreaks <= 0) return@request - val maxInstantBreaks = breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaks) - - val uncappedInstantBreaks = newBreaks.filter { it.instantBreak } - instantBreaks = uncappedInstantBreaks.take(maxInstantBreaks) - excessInstantBreaks = uncappedInstantBreaks.size > instantBreaks.size - - instantBreaks.forEach { ctx -> - if (!swapTo(ctx.hotbarIndex)) return@request - val breakInfo = handleNewBreak(ctx, request) ?: return@request - request.onAccept?.invoke(ctx.expectedPos) - updateBreakProgress(breakInfo) - } - if (excessInstantBreaks) return@request - processNewBreaks(newBreaks, request) - } - - updateLiveBreakInfos() - rotation = liveBreakInfos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> - info.rotationConfig.request(info.context.rotation) - } - - // Reversed so that the breaking order feels natural to the user as the primary break has to - // be started after the secondary - liveBreakInfos - .reversed() - .forEach { info -> - if (!swapTo(info.context.hotbarIndex) || !validRotation) return@HotbarRequest - updateBreakProgress(info) - } - done() - } + hotbarRequest = HotbarRequest(hotbarConfig) { if (update(::swapTo, tickPre = true)) done() } hotbarRequest?.let { hotbarRequest -> hotbarConfig.request(hotbarRequest) } @@ -190,6 +141,15 @@ object BreakManager : RequestHandler(), PositionBlocking { } } + listen { + val sequenceMode = currentRequest?.buildConfig?.breakSettings?.sequenceMode + ?: breakInfos.find { it != null }?.breakConfig?.sequenceMode + ?: return@listen + if (sequenceMode == BuildConfig.InteractSequenceMode.PostMovement) { + update(null) + } + } + listen(priority = Int.MIN_VALUE + 1) { event -> pendingBreaks .firstOrNull { it.context.expectedPos == event.pos } @@ -259,14 +219,75 @@ object BreakManager : RequestHandler(), PositionBlocking { } } + private fun SafeContext.update(swapTo: ((slot: Int) -> Boolean)?, tickPre: Boolean = false): Boolean { + if (tickPre) { + if (isOnBreakCooldown()) { + blockBreakingCooldown-- + } else if (currentRequest == null) { + breakInfos.forEach { it?.cancelBreak() } + } + } + + if (!isOnBreakCooldown()) currentRequest?.let request@ { request -> + val breakConfig = request.buildConfig.breakSettings + + if (tickPre) { + newBreaks = request.contexts + .filter { ctx -> canAccept(ctx) } + .sortedWith( + compareByDescending { it.instantBreak } + .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } + ).toMutableList() + + refreshOrCancelBreaks(newBreaks, request) + + val maxBreaks = getMaxBreaks(breakConfig).coerceAtLeast(0) + val maxInstantBreaks = breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaks) + + val uncappedInstantBreaks = newBreaks.filter { it.instantBreak } + instantBreaks = uncappedInstantBreaks.take(maxInstantBreaks) + excessInstantBreaks = uncappedInstantBreaks.size > instantBreaks.size + } + + if (atMaxBreakInfos(breakConfig)) return@request + instantBreaks.forEach { ctx -> + swapTo?.invoke(ctx.hotbarIndex) + val mismatchedTiming = tickPre && breakConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart + if (mismatchedTiming) return false + if (!swapped) return@request + val breakInfo = handleNewBreak(ctx, request) ?: return@request + request.onAccept?.invoke(ctx.expectedPos) + updateBreakProgress(breakInfo) + } + if (!excessInstantBreaks) processNewBreaks(newBreaks, request) + } + + updateLiveBreakInfos() + rotation = liveBreakInfos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> + info.rotationConfig.request(info.context.rotation) + } + + // Reversed so that the breaking order feels natural to the user as the primary break has to + // be started after the secondary + liveBreakInfos + .reversed() + .forEach { info -> + swapTo?.invoke(info.context.hotbarIndex) + val mismatchedTiming = tickPre && info.breakConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart + if (!swapped || !validRotation || mismatchedTiming) return false + updateBreakProgress(info) + } + + return true + } + private fun refreshOrCancelBreaks(newContexts: MutableCollection, request: BreakRequest) { breakInfos - .filterNotNull() - .forEach { info -> + .forEachNotNull { info -> newContexts.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> info.updateInfo(ctx, request) newContexts.remove(ctx) - return@forEach + return@forEachNotNull } info.cancelBreak() @@ -598,6 +619,10 @@ object BreakManager : RequestHandler(), PositionBlocking { else -> secondaryBreak = null } + private fun Array.forEachNotNull(block: (BreakInfo) -> Unit) { + for (info in this) info?.run(block) + } + override fun preEvent() = UpdateManagerEvent.Break.Pre().post() override fun postEvent() = UpdateManagerEvent.Break.Post().post() } \ No newline at end of file From e08684f15c92cdf5aff6d745d6c2a8c5a5a5ef85 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 9 Apr 2025 19:25:26 +0100 Subject: [PATCH 119/364] decoupled SlotInfo from HotbarManager --- .../request/hotbar/HotbarManager.kt | 10 ------- .../request/hotbar/HotbarRequest.kt | 2 +- .../interaction/request/hotbar/SlotInfo.kt | 28 +++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 2c900c480..8bc41f7e3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -148,16 +148,6 @@ object HotbarManager : RequestHandler(), Loadable { } } - data class SlotInfo( - val slot: Int, - var keepTicks: Int, - var swapPause: Int - ) { - var activeRequestAge = 0 - var swapPauseAge = 0 - val swapPaused get() = swapPauseAge < swapPause - } - override fun preEvent() = UpdateManagerEvent.Hotbar.Pre().post() override fun postEvent() = UpdateManagerEvent.Hotbar.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index f3bf80346..a3106ee30 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -25,7 +25,7 @@ class HotbarRequest( priority: Priority = 0, val actionSequence: HotbarManager.HotbarActionSequence.() -> Unit, ) : Request(priority) { - var swapSlot: HotbarManager.SlotInfo? = null + var swapSlot: SlotInfo? = null var instantActionsComplete = false override val done: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt new file mode 100644 index 000000000..c419319a5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.hotbar + +data class SlotInfo( + val slot: Int, + var keepTicks: Int, + var swapPause: Int +) { + var activeRequestAge = 0 + var swapPauseAge = 0 + val swapPaused get() = swapPauseAge < swapPause +} \ No newline at end of file From 3aebf1ea88f9a8aee3a38f342fcb9ebf744cfc00 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 9 Apr 2025 21:06:29 +0100 Subject: [PATCH 120/364] move all place logic to the update method --- .../request/breaking/BreakManager.kt | 4 +- .../request/placing/PlaceManager.kt | 84 +++++++++---------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 94cb35b2c..f6389090f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -132,7 +132,9 @@ object BreakManager : RequestHandler(), PositionBlocking { ?: breakInfos.firstOrNull { it != null }?.request?.hotbarConfig ?: return@listen - hotbarRequest = HotbarRequest(hotbarConfig) { if (update(::swapTo, tickPre = true)) done() } + hotbarRequest = HotbarRequest(hotbarConfig) { + if (update(::swapTo, tickPre = true)) done() + } hotbarRequest?.let { hotbarRequest -> hotbarConfig.request(hotbarRequest) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 4860f6845..1f656539d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -118,37 +118,10 @@ object PlaceManager : RequestHandler(), PositionBlocking { } val request = currentRequest ?: return@listen - if (BreakManager.activeThisTick) return@listen - val placeConfig = request.buildConfig.placeSettings - - pendingPlacements.setMaxSize(placeConfig.maxPendingPlacements) - pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) - - val isSneaking = player.isSneaking - val currentHotbarIndex = HotbarManager.serverSlot - val placeContexts = request.placeContexts - .filter { canPlace(it) } - .sortedWith( - compareByDescending { it.hotbarIndex == currentHotbarIndex } - .thenByDescending { it.sneak == isSneaking } - ) - - val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) - val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) - nextPredictedRotation = if (request.rotationConfig.rotate) placeContexts.getOrNull(takeCount)?.rotation else null hotbarRequest = HotbarRequest(request.hotbarConfig) { - potentialPlacements = placeContexts.take(takeCount) - potentialPlacements.forEach { ctx -> - swapTo(ctx.hotbarIndex) - if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) - if (ctx.sneak) shouldSneak = true - if (placeConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart) return@HotbarRequest - if (!attemptContextPlace(ctx, request)) return@HotbarRequest - } - requestNextPredictedRotation(request) - done() + if (update(::swapTo, tickPre = true)) done() } hotbarRequest?.let { hotbarRequest -> request.hotbarConfig.request(hotbarRequest) @@ -161,7 +134,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { listen { currentRequest?.let { request -> if (request.buildConfig.placeSettings.sequenceMode == BuildConfig.InteractSequenceMode.PostMovement) { - handlePlacementContexts(request) + update(null) postEvent() } } @@ -198,24 +171,47 @@ object PlaceManager : RequestHandler(), PositionBlocking { } } - private fun SafeContext.handlePlacementContexts(request: PlaceRequest): Boolean { - potentialPlacements.forEach { ctx -> - if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) - if (ctx.sneak) shouldSneak = true - if (!attemptContextPlace(ctx, request)) return false - } - requestNextPredictedRotation(request) - return true - } + private fun SafeContext.update(swapTo: ((slot: Int) -> Boolean)?, tickPre: Boolean = false): Boolean { + currentRequest?.let { request -> + val placeConfig = request.buildConfig.placeSettings - private fun SafeContext.attemptContextPlace(ctx: PlaceContext, request: PlaceRequest): Boolean { - if (!swappedTo(ctx.hotbarIndex) || !ctx.rotation.done || !validSneak(player)) return false + if (tickPre) { + pendingPlacements.setMaxSize(placeConfig.maxPendingPlacements) + pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) + + val isSneaking = player.isSneaking + val currentHotbarIndex = HotbarManager.serverSlot + val placeContexts = request.placeContexts + .filter { canPlace(it) } + .sortedWith( + compareByDescending { it.hotbarIndex == currentHotbarIndex } + .thenByDescending { it.sneak == isSneaking } + ) + + val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) + val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) + nextPredictedRotation = if (request.rotationConfig.rotate) placeContexts.getOrNull(takeCount)?.rotation else null + potentialPlacements = placeContexts.take(takeCount) + } - val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") + potentialPlacements.forEach { ctx -> + swapTo?.invoke(ctx.hotbarIndex) + if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) + if (ctx.sneak) shouldSneak = true + val mismatchedTiming = tickPre && placeConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart + if (mismatchedTiming) return false + if (!swappedTo(ctx.hotbarIndex) || !ctx.rotation.done || !validSneak(player)) return false + + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) { + warn("Placement interaction failed with $actionResult") + } + } + requestNextPredictedRotation(request) + return true } - return true + + return false } private fun requestNextPredictedRotation(request: PlaceRequest) { From aa92241a7a2dfd24036467b8581f89f2538a4666 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 10 Apr 2025 00:48:28 +0100 Subject: [PATCH 121/364] cleanup and fixed small newBreaks issue --- .../interaction/request/breaking/BreakManager.kt | 14 ++++++-------- .../interaction/request/placing/PlaceManager.kt | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index f6389090f..38a7fa523 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -285,22 +285,24 @@ object BreakManager : RequestHandler(), PositionBlocking { private fun refreshOrCancelBreaks(newContexts: MutableCollection, request: BreakRequest) { breakInfos - .forEachNotNull { info -> + .filterNotNull() + .forEach { info -> newContexts.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> info.updateInfo(ctx, request) newContexts.remove(ctx) - return@forEachNotNull + return@forEach } info.cancelBreak() } } - private fun SafeContext.processNewBreaks(newBreaks: Collection, request: BreakRequest) { + private fun SafeContext.processNewBreaks(newBreaks: MutableCollection, request: BreakRequest) { newBreaks .filter { !it.instantBreak } .forEach { ctx -> handleNewBreak(ctx, request) ?: return + newBreaks.remove(ctx) request.onAccept?.invoke(ctx.expectedPos) if (atMaxBreakInfos(request.buildConfig.breakSettings)) return } @@ -365,7 +367,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } private fun setPendingBreaksLimits(buildConfig: BuildConfig) { - pendingBreaks.setMaxSize(buildConfig.breakSettings.maxPendingBreaks) + pendingBreaks.setSizeLimit(buildConfig.breakSettings.maxPendingBreaks) pendingBreaks.setDecayTime(buildConfig.interactionTimeout * 50L) } @@ -621,10 +623,6 @@ object BreakManager : RequestHandler(), PositionBlocking { else -> secondaryBreak = null } - private fun Array.forEachNotNull(block: (BreakInfo) -> Unit) { - for (info in this) info?.run(block) - } - override fun preEvent() = UpdateManagerEvent.Break.Pre().post() override fun postEvent() = UpdateManagerEvent.Break.Post().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 1f656539d..87c0735e9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -176,7 +176,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { val placeConfig = request.buildConfig.placeSettings if (tickPre) { - pendingPlacements.setMaxSize(placeConfig.maxPendingPlacements) + pendingPlacements.setSizeLimit(placeConfig.maxPendingPlacements) pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) val isSneaking = player.isSneaking From 960ede6edac27c5b8d4abe561afe72c339d99c89 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 10 Apr 2025 02:10:49 +0100 Subject: [PATCH 122/364] default build settings for 2b2t --- .../kotlin/com/lambda/config/groups/BreakSettings.kt | 10 +++++----- .../kotlin/com/lambda/config/groups/BuildSettings.kt | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index d4599986e..76c0050da 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -27,11 +27,11 @@ class BreakSettings( priority: Priority = 0, vis: () -> Boolean = { true } ) : BreakConfig(priority) { - override val breakMode by c.setting("Break Mode", BreakMode.Vanilla) { vis() } + override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() } override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } - override val breakThreshold by c.setting("Break Threshold", 1.0f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } - override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() } - override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } + override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } + override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } + override val breakDelay by c.setting("Break Delay", 0, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } override val sequenceMode by c.setting("Break Sequence Mode", BuildConfig.InteractSequenceMode.PostMovement, "The sub-tick timing at which break actions are performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } @@ -41,7 +41,7 @@ class BreakSettings( override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() } override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } - override val maxPendingBreaks by c.setting("Max Pending Breaks", 5, 1..30, 1, "The maximum amount of pending breaks") { vis() } + override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() } override val instantBreaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index b9210e391..f092246e1 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -35,7 +35,7 @@ class BuildSettings( override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.General } override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.General && pathing } override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.General } - override val maxPendingInteractions by c.setting("Max Pending Interactions", 10, 1..30, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } + override val maxPendingInteractions by c.setting("Max Pending Interactions", 20, 1..30, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking override val breakSettings = BreakSettings(c) { page == Page.Break && vis() } @@ -43,5 +43,5 @@ class BuildSettings( // Placing override val placeSettings = PlaceSettings(c) { page == Page.Place && vis() } - override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeSettings.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) } + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && ((page == Page.Place && placeSettings.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) || (page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None)) } } \ No newline at end of file From 25707c5f87e8ac59e5fd7ea101ce73a549b4d9c8 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 01:24:07 +0100 Subject: [PATCH 123/364] init request handler structure rewrite --- .../com/lambda/config/groups/BreakSettings.kt | 4 +- .../com/lambda/config/groups/BuildConfig.kt | 10 +- .../com/lambda/config/groups/BuildSettings.kt | 6 +- .../lambda/config/groups/HotbarSettings.kt | 3 +- .../com/lambda/config/groups/PlaceSettings.kt | 2 +- .../com/lambda/config/groups/TickStage.kt | 26 + .../lambda/event/events/UpdateManagerEvent.kt | 23 +- .../construction/context/BreakContext.kt | 9 +- .../construction/context/PlaceContext.kt | 10 +- .../construction/simulation/BuildSimulator.kt | 20 +- .../com/lambda/interaction/request/Request.kt | 2 + .../interaction/request/RequestHandler.kt | 106 ++-- .../request/breaking/BreakConfig.kt | 7 +- .../interaction/request/breaking/BreakInfo.kt | 32 +- .../request/breaking/BreakManager.kt | 565 ++++++++++-------- .../request/breaking/BreakRequest.kt | 14 +- .../request/hotbar/HotbarConfig.kt | 10 +- .../request/hotbar/HotbarManager.kt | 129 ++-- .../request/hotbar/HotbarRequest.kt | 24 +- .../interaction/request/hotbar/SlotInfo.kt | 3 + .../request/placing/PlaceConfig.kt | 5 +- .../request/placing/PlaceManager.kt | 169 +++--- .../request/placing/PlaceRequest.kt | 14 +- .../request/rotation/RotationConfig.kt | 2 +- .../request/rotation/RotationManager.kt | 89 ++- .../lambda/module/modules/movement/Speed.kt | 4 +- .../module/modules/player/PacketMine.kt | 11 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 18 +- 28 files changed, 700 insertions(+), 617 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/config/groups/TickStage.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 76c0050da..7f27479dc 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -32,7 +32,7 @@ class BreakSettings( override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 0, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } - override val sequenceMode by c.setting("Break Sequence Mode", BuildConfig.InteractSequenceMode.PostMovement, "The sub-tick timing at which break actions are performed", vis) + override val breakStageMask by c.setting("Break Stage Mask", setOf(*TickStage.entries.toTypedArray()), "The sub-tick timing at which break actions can be performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } @@ -42,7 +42,7 @@ class BreakSettings( override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() } - override val instantBreaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } + override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 2976e48dc..2edb7dc36 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -26,20 +26,14 @@ interface BuildConfig { val interactionTimeout: Int // Breaking - val breakSettings: BreakSettings + val breaking: BreakSettings // Placing - val placeSettings: PlaceSettings + val placing: PlaceSettings enum class SwingType { Vanilla, Server, Client } - - enum class InteractSequenceMode { - TickStart, - Vanilla, - PostMovement, - } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index f092246e1..68d1e1c4c 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -38,10 +38,10 @@ class BuildSettings( override val maxPendingInteractions by c.setting("Max Pending Interactions", 20, 1..30, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking - override val breakSettings = BreakSettings(c) { page == Page.Break && vis() } + override val breaking = BreakSettings(c) { page == Page.Break && vis() } // Placing - override val placeSettings = PlaceSettings(c) { page == Page.Place && vis() } + override val placing = PlaceSettings(c) { page == Page.Place && vis() } - override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && ((page == Page.Place && placeSettings.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) || (page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None)) } + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && ((page == Page.Place && placing.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) || (page == Page.Break && breaking.breakConfirmation != BreakConfirmationMode.None)) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt index 0d9f97e71..adab14c43 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt @@ -29,5 +29,6 @@ class HotbarSettings( override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", vis) override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", vis) override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() } - override var swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) + override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) + override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(*TickStage.entries.toTypedArray()), "The sub-tick timing at which hotbar actions are performed", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 7d37da389..e3fad9e38 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -29,7 +29,7 @@ class PlaceSettings( override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } - override val sequenceMode by c.setting("Place Sequence Mode", BuildConfig.InteractSequenceMode.PostMovement, "The sub-tick timing at which break actions are performed") { vis() } + override val placeStageMask by c.setting("Place Sequence Mode", setOf(*TickStage.entries.toTypedArray()), "The sub-tick timing at which break actions are performed") { vis() } override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt new file mode 100644 index 000000000..1e09b8873 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.config.groups + +enum class TickStage { + TickStart, + PostHotbar, + PostInteract, + PreMovement, + PostMovement, +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt index ebc2c0715..201fdee31 100644 --- a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt @@ -20,23 +20,8 @@ package com.lambda.event.events import com.lambda.event.Event sealed class UpdateManagerEvent { - sealed class Rotation { - class Pre : Event - class Post : Event - } - - sealed class Hotbar { - class Pre : Event - class Post : Event - } - - sealed class Break { - class Pre : Event - class Post : Event - } - - sealed class Place { - class Pre : Event - class Post : Event - } + class Rotation : Event + class Hotbar : Event + class Break : Event + class Place : Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 8665ab294..6d81ba7df 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -22,6 +22,8 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.breaking.BreakRequest +import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState @@ -70,10 +72,15 @@ data class BreakContext( } } - override fun shouldRotate(config: BuildConfig) = config.breakSettings.rotateForBreak + override fun shouldRotate(config: BuildConfig) = config.breaking.rotateForBreak override fun SafeContext.buildRenderer() { withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) withState(checkedState, expectedPos, sideColor, result.side) } + + fun requestDependencies(request: BreakRequest): Boolean { + val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) + return hotbarRequest.done + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 9b93971ae..a88658a0a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -22,6 +22,8 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState @@ -73,5 +75,11 @@ data class PlaceContext( withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) } - override fun shouldRotate(config: BuildConfig) = config.placeSettings.rotateForPlace + override fun shouldRotate(config: BuildConfig) = config.placing.rotate + + fun requestDependencies(request: PlaceRequest): Boolean { + val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) + val validRotation = if (request.build.placing.rotate) request.rotation.request(rotation).done else true + return hotbarRequest.done && validRotation + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 1a01e7cf7..a2ea5f22a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -92,11 +92,11 @@ object BuildSimulator { checkRequirements(pos, target, build)?.let { return@flatMap setOf(it) } - checkPlaceResults(pos, target, eye, build.placeSettings, interact, rotation, inventory).let { + checkPlaceResults(pos, target, eye, build.placing, interact, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } - checkBreakResults(pos, eye, build.placeSettings, interact, rotation, inventory, build).let { + checkBreakResults(pos, eye, build.placing, interact, rotation, inventory, build).let { if (it.isEmpty()) return@let return@flatMap it } @@ -122,7 +122,7 @@ object BuildSimulator { } /* block should be ignored */ - if (state.block in build.breakSettings.ignoredBlocks && target.type == TargetState.Type.AIR) { + if (state.block in build.breaking.ignoredBlocks && target.type == TargetState.Type.AIR) { return BuildResult.Ignored(pos) } @@ -392,7 +392,7 @@ object BuildSimulator { val state = blockState(pos) /* is a block that will be destroyed by breaking adjacent blocks */ - if (build.breakSettings.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (build.breaking.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { acc.add(BuildResult.Ignored(pos)) return acc } @@ -465,7 +465,7 @@ object BuildSimulator { state, targetState, player.inventory.selectedSlot, - instantBreakable(state, pos, build.breakSettings.breakThreshold) + instantBreakable(state, pos, build.breaking.breakThreshold) ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -510,7 +510,7 @@ object BuildSimulator { val blockHit = bestHit.hit.blockResult ?: return acc val target = lookAt(bestHit.targetRotation, 0.001) val request = RotationRequest(target, rotation) - val instant = instantBreakable(state, pos, build.breakSettings.breakThreshold) + val instant = instantBreakable(state, pos, build.breaking.breakThreshold) val breakContext = BreakContext( eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant @@ -538,10 +538,10 @@ object BuildSimulator { return acc } - val toolSelection = if (build.breakSettings.forceSilkTouch) { + val toolSelection = if (build.breaking.forceSilkTouch) { selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.SILK_TOUCH) } - } else if (build.breakSettings.forceFortunePickaxe) { - selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.breakSettings.minFortuneLevel) } + } else if (build.breaking.forceFortunePickaxe) { + selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.breaking.minFortuneLevel) } } else { bestTools.select() } @@ -568,7 +568,7 @@ object BuildSimulator { if (toolPair == null) return acc breakContext.hotbarIndex = player.hotbar.indexOf(toolPair.first) - breakContext.instantBreak = instantBreakable(state, pos, toolPair.first, build.breakSettings.breakThreshold) + breakContext.instantBreak = instantBreakable(state, pos, toolPair.first, build.breaking.breakThreshold) acc.add(BreakResult.Break(pos, breakContext)) return acc } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt index 49e253ad2..9459c2111 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt @@ -20,5 +20,7 @@ package com.lambda.interaction.request abstract class Request ( val priority: Priority ) { + var fresh = true + abstract val done: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 62324c298..13864b6f7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -17,71 +17,107 @@ package com.lambda.interaction.request +import com.lambda.config.groups.TickStage +import com.lambda.context.SafeContext import com.lambda.event.Event +import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import java.util.concurrent.ConcurrentHashMap +import com.lambda.threading.runSafe /** - * This class manages a collection of requests, each associated with a `RequestConfig`. - * It provides a mechanism to register requests and select the highest priority request - * for processing. + * This class handles requests, offering specific opening times, and an option to queue a request for the + * next opening if closed */ -abstract class RequestHandler { +abstract class RequestHandler( + vararg openStages: TickStage, + val preOpen: (SafeContext.() -> Unit)? = null, + val onOpen: (SafeContext.() -> Unit)? = null, + val postClose: (SafeContext.() -> Unit)? = null +) { + /** + * Represents if the handler is accepting requests at any given time + */ + private var acceptingRequests = false - protected val requestMap = ConcurrentHashMap, R>() + /** + * Represents the sequence stage the current tick is at + */ + var tickStage = TickStage.TickStart; private set /** - * Represents if the handler performed any external actions within this tick + * If a request is made while the handler isn't accepting requests, it is placed into [queuedRequest] and run + * at the start of the next open request timeframe */ - var activeThisTick = false - protected set + var queuedRequest: R? = null; protected set /** - * The currently active request. + * Represents if the handler performed any external actions within this tick */ - var currentRequest: R? = null; protected set + var activeThisTick = false; protected set init { + openStages.forEach { stage -> + when(stage) { + TickStage.TickStart -> openRequestsFor(TickStage.TickStart) + TickStage.PostHotbar -> TODO() + TickStage.PostInteract -> TODO() + TickStage.PreMovement -> openRequestsFor(TickStage.PreMovement) + TickStage.PostMovement -> openRequestsFor(TickStage.PostMovement) + } + } + listen(Int.MIN_VALUE) { activeThisTick = false } } /** - * Registers a new request with the given configuration. + * opens the handler for requests for the duration of the given event + */ + private inline fun openRequestsFor(stage: TickStage) { + listen(priority = Int.MAX_VALUE) { + tickStage = stage + preOpen?.invoke(this) + queuedRequest?.let { request -> + handleRequest(request) + queuedRequest = null + } + acceptingRequests = true + onOpen?.invoke(this) + } + listen(priority = Int.MIN_VALUE) { + acceptingRequests = false + postClose?.invoke(this) + } + } + + /** + * Registers a new request * - * @param config The configuration for the request. * @param request The request to register. + * @param queueIfClosed queues the request for the next time the handlers accepting requests * @return The registered request. */ - fun registerRequest(config: RequestConfig, request: R): R { - requestMap[config] = request + fun request(request: R, queueIfClosed: Boolean = true): R { + if (!acceptingRequests) { + if (queueIfClosed && queuedRequest == null) { + queuedRequest = request + } + return request + } + + runSafe { + handleRequest(request) + request.fresh = false + } return request } /** - * Updates the current request to the highest priority registered request. - * Clears the internal request map after updating. - * - * @return True, if the request was updated. + * Handles a request */ - protected fun updateRequest( - keepIfNull: Boolean = false, - filter: (Map.Entry, R>) -> Boolean = { true } - ): Boolean { - val prev = currentRequest - - currentRequest = requestMap.entries - .filter(filter) - .maxByOrNull { it.key.priority }?.value - - if (keepIfNull && currentRequest == null) currentRequest = prev - - requestMap.clear() - return prev != currentRequest - } + abstract fun SafeContext.handleRequest(request: R) protected abstract fun preEvent(): Event - protected abstract fun postEvent(): Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index e7b18e495..c06ec99c4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.TickStage import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig import net.minecraft.block.Block @@ -30,7 +31,7 @@ abstract class BreakConfig( abstract val breakThreshold: Float abstract val doubleBreak: Boolean abstract val breakDelay: Int - abstract val sequenceMode: BuildConfig.InteractSequenceMode + abstract val breakStageMask: Set abstract val swing: SwingMode abstract val swingType: BuildConfig.SwingType abstract val sounds: Boolean @@ -39,7 +40,7 @@ abstract class BreakConfig( abstract val rotateForBreak: Boolean abstract val breakConfirmation: BreakConfirmationMode abstract val maxPendingBreaks: Int - abstract val instantBreaksPerTick: Int + abstract val breaksPerTick: Int abstract val breakWeakBlocks: Boolean abstract val forceSilkTouch: Boolean abstract val forceFortunePickaxe: Boolean @@ -47,7 +48,7 @@ abstract class BreakConfig( abstract val ignoredBlocks: Set override fun requestInternal(request: BreakRequest) { - BreakManager.registerRequest(this, request) + BreakManager.request(request) } enum class BreakMode { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 793116c20..7eacbb803 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -32,35 +32,34 @@ data class BreakInfo( var type: BreakType, var request: BreakRequest ) { - val breakConfig get() = request.buildConfig.breakSettings - val rotationConfig get() = request.rotationConfig + val breakConfig get() = request.build.breaking + val pendingInteractions get() = request.pendingInteractions - val pendingInteractionsList get() = request.pendingInteractionsList - private val onCancel get() = request.onCancel - private val onBreak get() = request.onBreak - private val onItemDrop get() = request.onItemDrop + var activeAge = 0 + var updatedThisTick = true + var updatedProgressThisTick = false var breaking = false var breakingTicks = 0 var soundsCooldown = 0.0f - val redundant - get() = type == BreakType.RedundantSecondary + val isPrimary get() = type == BreakType.Primary + val isSecondary get() = type == BreakType.Secondary + val isRedundant get() = type == BreakType.RedundantSecondary @Volatile - var broken = false - private set + var broken = false; private set private var item: ItemEntity? = null val callbacksCompleted - @Synchronized get() = broken && (onItemDrop == null || item != null) + @Synchronized get() = broken && (request.onItemDrop == null || item != null) fun internalOnBreak() { synchronized(this) { broken = true - onBreak?.invoke(context.expectedPos) + request.onBreak?.invoke(context.expectedPos) item?.let { item -> - onItemDrop?.invoke(item) + request.onItemDrop?.invoke(item) } } } @@ -69,19 +68,20 @@ data class BreakInfo( synchronized(this) { this.item = item if (broken) { - onItemDrop?.invoke(item) + request.onItemDrop?.invoke(item) } } } fun internalOnCancel() { - onCancel?.invoke(context.expectedPos) + request.onCancel?.invoke(context.expectedPos) } fun updateInfo(context: BreakContext, request: BreakRequest) { + updatedThisTick = true this.context = context this.request = request - if (redundant) { + if (isRedundant) { type = BreakType.Secondary } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 38a7fa523..6854c3ff0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -18,12 +18,13 @@ package com.lambda.interaction.request.breaking import com.lambda.Lambda.mc -import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext +import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent -import com.lambda.event.events.MovementEvent +import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -35,9 +36,12 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode +import com.lambda.interaction.request.breaking.BreakManager.activeRequest +import com.lambda.interaction.request.breaking.BreakManager.preEvent +import com.lambda.interaction.request.breaking.BreakManager.processRequest import com.lambda.interaction.request.breaking.BreakType.Primary import com.lambda.interaction.request.hotbar.HotbarManager -import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe @@ -63,7 +67,12 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkSectionPos -object BreakManager : RequestHandler(), PositionBlocking { +object BreakManager : RequestHandler( + TickStage.TickStart, + TickStage.PostMovement, + preOpen = { activeRequest?.let { processRequest(it) } }, + onOpen = { preEvent() } +), PositionBlocking { private var primaryBreak: BreakInfo? get() = breakInfos[0] set(value) { breakInfos[0] = value } @@ -71,7 +80,6 @@ object BreakManager : RequestHandler(), PositionBlocking { get() = breakInfos[1] set(value) { breakInfos[1] = value } private val breakInfos = arrayOfNulls(2) - private var liveBreakInfos = listOf() private val pendingBreaks = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L @@ -88,68 +96,48 @@ object BreakManager : RequestHandler(), PositionBlocking { world.setBlockState(info.context.expectedPos, info.context.checkedState) } } - info.pendingInteractionsList.remove(info.context) + info.pendingInteractions.remove(info.context) } + private val pendingBreakCount get() = breakInfos.count { it != null } + pendingBreaks.size + + private var activeRequest: BreakRequest? = null override val blockedPositions get() = breakInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } - private var blockBreakingCooldown = 0 - - private var hotbarRequest: HotbarRequest? = null - private val swapped get() = hotbarRequest?.done == true + private var rotationRequest: RotationRequest? = null + private val rotated get() = rotationRequest?.done != false - private var rotation: RotationRequest? = null - private val validRotation get() = rotation?.done != false + private var breakCooldown = 0 + private var breaksThisTick = 0 + private var maxBreaksThisTick = 0 - private var newBreaks = mutableListOf() - private var instantBreaks = listOf() - private var excessInstantBreaks = false + private var breaks = mutableListOf() + private var instantBreaks = mutableListOf() fun Any.onBreak( alwaysListen: Boolean = false, priority: Priority = 0, block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { - block() - } - - fun Any.onBreakPost( - alwaysListen: Boolean = false, - priority: Priority = 0, - block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { + ) = this.listen(priority, alwaysListen) { block() } init { - listen { - preEvent() - - pendingBreaks.cleanUp() - updateRequest() - val hotbarConfig = currentRequest?.hotbarConfig - ?: breakInfos.firstOrNull { it != null }?.request?.hotbarConfig - ?: return@listen - - hotbarRequest = HotbarRequest(hotbarConfig) { - if (update(::swapTo, tickPre = true)) done() - } - hotbarRequest?.let { hotbarRequest -> - hotbarConfig.request(hotbarRequest) - } - if (instantBreaks.isNotEmpty() || liveBreakInfos.isNotEmpty()) { - activeThisTick = true + listen(priority = Int.MIN_VALUE) { + if (breakCooldown > 0) { + breakCooldown-- } - } - - listen { - val sequenceMode = currentRequest?.buildConfig?.breakSettings?.sequenceMode - ?: breakInfos.find { it != null }?.breakConfig?.sequenceMode - ?: return@listen - if (sequenceMode == BuildConfig.InteractSequenceMode.PostMovement) { - update(null) + breakInfos.forEach { info -> + info?.apply { + if (isRedundant) updateBreakProgress(this) + activeAge++ + updatedThisTick = false + updatedProgressThisTick = false + } } + activeRequest = null + breaksThisTick = 0 } listen(priority = Int.MIN_VALUE + 1) { event -> @@ -162,7 +150,7 @@ object BreakManager : RequestHandler(), PositionBlocking { // return if the block's not broken if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) { - removePendingBreak(pending) + pending.stopPending() return@listen } @@ -171,7 +159,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } pending.internalOnBreak() if (pending.callbacksCompleted) { - removePendingBreak(pending) + pending.stopPending() } return@listen } @@ -189,7 +177,7 @@ object BreakManager : RequestHandler(), PositionBlocking { destroyBlock(info) info.internalOnBreak() if (!info.callbacksCompleted) { - addPendingBreak(info) + info.startPending() } info.nullify() } @@ -203,7 +191,7 @@ object BreakManager : RequestHandler(), PositionBlocking { ?.let { pending -> pending.internalOnItemDrop(it.entity) if (pending.callbacksCompleted) { - removePendingBreak(pending) + pending.stopPending() } return@listen } @@ -217,138 +205,146 @@ object BreakManager : RequestHandler(), PositionBlocking { listenUnsafe(priority = Int.MIN_VALUE + 1) { breakInfos.forEach { it?.nullify() } pendingBreaks.clear() - setBreakCooldown(0) + breakCooldown = 0 } } - private fun SafeContext.update(swapTo: ((slot: Int) -> Boolean)?, tickPre: Boolean = false): Boolean { - if (tickPre) { - if (isOnBreakCooldown()) { - blockBreakingCooldown-- - } else if (currentRequest == null) { - breakInfos.forEach { it?.cancelBreak() } - } - } - - if (!isOnBreakCooldown()) currentRequest?.let request@ { request -> - val breakConfig = request.buildConfig.breakSettings + override fun SafeContext.handleRequest(request: BreakRequest) { + if (activeRequest != null || PlaceManager.activeThisTick) return - if (tickPre) { - newBreaks = request.contexts - .filter { ctx -> canAccept(ctx) } - .sortedWith( - compareByDescending { it.instantBreak } - .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } - ).toMutableList() - - refreshOrCancelBreaks(newBreaks, request) + activeRequest = request + processRequest(request) + if (instantBreaks.isNotEmpty() || breaks.isNotEmpty()) { + activeRequest = null + } + if (breaksThisTick > 0 || breakInfos.any { it != null && !it.isRedundant }) { + activeThisTick = true + } + } - val maxBreaks = getMaxBreaks(breakConfig).coerceAtLeast(0) - val maxInstantBreaks = breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaks) + private fun SafeContext.processRequest(request: BreakRequest) { + pendingBreaks.cleanUp() - val uncappedInstantBreaks = newBreaks.filter { it.instantBreak } - instantBreaks = uncappedInstantBreaks.take(maxInstantBreaks) - excessInstantBreaks = uncappedInstantBreaks.size > instantBreaks.size - } + val breakConfig = request.build.breaking + if (request.fresh) populateFrom(request) - if (atMaxBreakInfos(breakConfig)) return@request - instantBreaks.forEach { ctx -> - swapTo?.invoke(ctx.hotbarIndex) - val mismatchedTiming = tickPre && breakConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart - if (mismatchedTiming) return false - if (!swapped) return@request - val breakInfo = handleNewBreak(ctx, request) ?: return@request - request.onAccept?.invoke(ctx.expectedPos) - updateBreakProgress(breakInfo) - } - if (!excessInstantBreaks) processNewBreaks(newBreaks, request) + if (!atMaxBreakInfos(breakConfig)) run processNewBreaks@ { + if (!performInstantBreaks(request)) return@processNewBreaks + processNewBreaks(request) } - updateLiveBreakInfos() - rotation = liveBreakInfos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> - info.rotationConfig.request(info.context.rotation) - } - - // Reversed so that the breaking order feels natural to the user as the primary break has to - // be started after the secondary - liveBreakInfos + // Reversed so that the breaking order feels natural to the user as the primary break is always the + // last break to be started + breakInfos + .filterNotNull() + .filter { !it.isRedundant } + .also { infos -> + infos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> + info.request.rotation.request(info.context.rotation) + } + } .reversed() .forEach { info -> - swapTo?.invoke(info.context.hotbarIndex) - val mismatchedTiming = tickPre && info.breakConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart - if (!swapped || !validRotation || mismatchedTiming) return false + if (info.updatedProgressThisTick) return@forEach + if (!info.context.requestDependencies(request)) return + if (!rotated || tickStage !in info.breakConfig.breakStageMask) return updateBreakProgress(info) } - - return true } - private fun refreshOrCancelBreaks(newContexts: MutableCollection, request: BreakRequest) { + private fun SafeContext.populateFrom(request: BreakRequest) { + // Sanitize and sort the new breaks + val newBreaks = request.contexts + .filter { ctx -> canAccept(ctx) } + .sortedWith( + compareByDescending { it.instantBreak } + .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } + ).toMutableList() + + // Update the current break infos or cancel if abandoned breakInfos .filterNotNull() .forEach { info -> - newContexts.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> - info.updateInfo(ctx, request) - newContexts.remove(ctx) + newBreaks.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> + if (!info.updatedThisTick) info.updateInfo(ctx, request) + newBreaks.remove(ctx) return@forEach } - info.cancelBreak() + if (!info.updatedThisTick) info.cancelBreak() } - } - private fun SafeContext.processNewBreaks(newBreaks: MutableCollection, request: BreakRequest) { - newBreaks - .filter { !it.instantBreak } - .forEach { ctx -> - handleNewBreak(ctx, request) ?: return - newBreaks.remove(ctx) - request.onAccept?.invoke(ctx.expectedPos) - if (atMaxBreakInfos(request.buildConfig.breakSettings)) return - } - } - - private fun SafeContext.updateLiveBreakInfos() { - liveBreakInfos = breakInfos - .filterNotNull() - .filter { - if (it.redundant) { - updateBreakProgress(it) - false - } else true - } - } + instantBreaks = newBreaks + .filter { it.instantBreak } + .toMutableList() - private fun getMaxBreaks(breakConfig: BreakConfig): Int = - breakConfig.maxPendingBreaks - (breakInfos.count { it != null } + pendingBreaks.size) + breaks = newBreaks + .filter { !it.instantBreak } + .toMutableList() - private fun atMaxBreakInfos(breakConfig: BreakConfig): Boolean { - val possibleBreakingCount = if (breakConfig.doubleBreak) 2 else 1 - return breakInfos.take(possibleBreakingCount).all { it != null } + val breakConfig = request.build.breaking + val pendingLimit = (breakConfig.maxPendingBreaks - pendingBreakCount).coerceAtLeast(0) + maxBreaksThisTick = breakConfig.breaksPerTick.coerceAtMost(pendingLimit) } - private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { - val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) - val correctMaterial = info.context.checkedState.block == entity.stack.item.block - return inRange && correctMaterial + /** + * Attempts to break as many [BreakContext]'s as possible from the [instantBreaks] collection within this tick. + * + * @return false if a break could not be performed. + */ + private fun SafeContext.performInstantBreaks(request: BreakRequest): Boolean { + val iterator = instantBreaks.iterator() + while (iterator.hasNext()) { + if (breaksThisTick + 1 > maxBreaksThisTick) return false + + val ctx = iterator.next() + + if (!ctx.requestDependencies(request)) return false + if (tickStage !in request.build.breaking.breakStageMask) return false + + val breakInfo = initNewBreak(ctx, request) ?: return false + request.onAccept?.invoke(ctx.expectedPos) + updateBreakProgress(breakInfo) + breaksThisTick++ + iterator.remove() + } + return true } - private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = - if (targetState.matches(newState, pos, world)) true - else { - this@BreakManager.warn("Break at ${pos.toShortString()} was rejected with $newState instead of $targetState") - false + /** + * Attempts to start breaking as many [BreakContext]'s from the [breaks] collection as possible. + * + * @return false if a context cannot be started or the maximum active breaks has been reached. + * + * @see initNewBreak + * @see atMaxBreakInfos + */ + private fun SafeContext.processNewBreaks(request: BreakRequest): Boolean { + breaks.forEach { ctx -> + initNewBreak(ctx, request) ?: return false + request.onAccept?.invoke(ctx.expectedPos) + breaks.remove(ctx) + if (atMaxBreakInfos(request.build.breaking)) return false } + return true + } - private fun SafeContext.handleNewBreak( + /** + * Attempts to accept the [requestCtx] into the [breakInfos]. + * + * @return the [BreakInfo] or null if the break context wasn't accepted. + */ + private fun SafeContext.initNewBreak( requestCtx: BreakContext, request: BreakRequest ): BreakInfo? { val breakInfo = BreakInfo(requestCtx, Primary, request) primaryBreak?.let { primaryInfo -> if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) { - primaryInfo.abortBreakPacket(world, interaction) - return@let + if (primaryInfo.activeAge > 0) { + primaryInfo.abortBreakPacket(world, interaction) + return@let + } else return null } if (!primaryInfo.breaking) { @@ -362,19 +358,47 @@ object BreakManager : RequestHandler(), PositionBlocking { } primaryBreak = breakInfo - setPendingBreaksLimits(request.buildConfig) + pendingBreaks.setSizeLimit(request.build.breaking.maxPendingBreaks) + pendingBreaks.setDecayTime(request.build.interactionTimeout * 50L) return primaryBreak } - private fun setPendingBreaksLimits(buildConfig: BuildConfig) { - pendingBreaks.setSizeLimit(buildConfig.breakSettings.maxPendingBreaks) - pendingBreaks.setDecayTime(buildConfig.interactionTimeout * 50L) + /** + * @return if the [breakInfos] are at capacity and no new breaks can be started. + */ + private fun atMaxBreakInfos(breakConfig: BreakConfig): Boolean { + val possibleBreakingCount = if (breakConfig.doubleBreak) 2 else 1 + return breakInfos.take(possibleBreakingCount).all { it != null } + } + + /** + * @return if the [ItemEntity] matches the [BreakInfo]'s expected item drop. + */ + private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { + val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) + val correctMaterial = info.context.checkedState.block == entity.stack.item.block + return inRange && correctMaterial } + /** + * @return if the [newState] matches the [targetState]. + * + * @see TargetState + */ + private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = + if (targetState.matches(newState, pos, world)) true + else { + this@BreakManager.warn("Break at ${pos.toShortString()} was rejected with $newState instead of $targetState") + false + } + + /** + * @return if the break context can be accepted. + */ private fun SafeContext.canAccept(ctx: BreakContext): Boolean { if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false - breakInfos.firstOrNull { it != null && !it.redundant } + breakInfos.firstOrNull { it != null && !it.isRedundant } ?.let { info -> if ( ctx.hotbarIndex != info.context.hotbarIndex) return false } @@ -382,16 +406,140 @@ object BreakManager : RequestHandler(), PositionBlocking { return !blockState(ctx.expectedPos).isAir } + /** + * Begins the post-break logic sequence for the given [info]. + * + * [BreakConfirmationMode.None] Will assume the block has been broken server side, and will only persist + * the [info] if the requester has any un-triggered callbacks. E.G. if the block has broken, but the item hasn't dropped + * and the requester has specified an itemDrop callback. + * + * [BreakConfirmationMode.BreakThenAwait] Will perform all post block break actions, such as spawning break particles, + * playing sounds, etc. However, it will store the [info] in the pending interaction collections before triggering the + * [BreakInfo.internalOnBreak] callback, in case the server rejects the break. + * + * [BreakConfirmationMode.AwaitThenBreak] Will immediately place the [info] into the pending interaction collections. + * Once the server responds, confirming the break, the post break actions will take place, and the [BreakInfo.internalOnBreak] + * callback will be triggered. + * + * @see destroyBlock + * @see addPendingBreak + */ + private fun SafeContext.onBlockBreak(info: BreakInfo) { + when (info.breakConfig.breakConfirmation) { + BreakConfirmationMode.None -> { + destroyBlock(info) + info.internalOnBreak() + if (!info.callbacksCompleted) { + info.startPending() + } + } + BreakConfirmationMode.BreakThenAwait -> { + destroyBlock(info) + info.startPending() + } + BreakConfirmationMode.AwaitThenBreak -> { + info.startPending() + } + } + breaksThisTick++ + info.nullify() + } + + /** + * Adds the [info] to the break manager, and requesters, pending interaction collections. + */ + private fun BreakInfo.startPending() { + pendingBreaks.add(this) + pendingInteractions.add(context) + } + + /** + * Removes the [info] from the break manager, and requesters, pending interation collections. + */ + private fun BreakInfo.stopPending() { + pendingBreaks.remove(this) + pendingInteractions.remove(context) + } + + /** + * Makes the [BreakInfo] a secondary if not already. + */ + private fun BreakInfo.makeSecondary() { + if (secondaryBreak === this) return + secondaryBreak = this.apply { + type = BreakType.Secondary + } + primaryBreak = null + } + + /** + * Attempts to cancel the break. + * + * Secondary blocks are monitored by the server, and keep breaking regardless of the clients actions. + * This means that the break cannot be completely stopped, instead, it must be monitored as we can't start + * more secondary break infos until the previous has broken or its state has turned to air. + * + * If the user has [BreakConfig.unsafeCancels] enabled, the info is made redundant, and mostly ignored. + * If not, the break continues. + * + * @see makeRedundant + */ + private fun BreakInfo.cancelBreak() = + runSafe { + setBreakingTextureStage(player, world, -1) + if (isPrimary) { + abortBreakPacket(world, interaction) + nullify() + return@runSafe + } + if (isSecondary && breakConfig.unsafeCancels) { + makeRedundant() + } + } + + /** + * Nullifies the break. If the block is not broken, the [BreakInfo.internalOnBreak] callback gets triggered + */ + private fun BreakInfo.nullify() { + type.nullify() + if (!broken) internalOnCancel() + } + + /** + * Makes the [BreakInfo] redundant and triggers the [BreakInfo.internalOnCancel] callback + */ + private fun BreakInfo.makeRedundant() { + type = BreakType.RedundantSecondary + internalOnCancel() + } + + /** + * Nullifies the [BreakInfo] reference in the [breakInfos] array based on the [BreakType] + */ + private fun BreakType.nullify() = + when (this) { + Primary -> primaryBreak = null + else -> secondaryBreak = null + } + + /** + * A modified version of the vanilla updateBlockBreakingProgress method. + * + * @return if the update was successful. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress + */ private fun SafeContext.updateBreakProgress(info: BreakInfo): Boolean { + info.updatedProgressThisTick = true val ctx = info.context val hitResult = ctx.result if (gamemode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { - if (info.redundant) { + if (info.isRedundant) { onBlockBreak(info) return true } - setBreakCooldown(info.breakConfig.breakDelay) + breakCooldown = info.breakConfig.breakDelay interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) @@ -404,7 +552,7 @@ object BreakManager : RequestHandler(), PositionBlocking { } if (!info.breaking) { - if (!attackBlock(info)) { + if (!startBreaking(info)) { info.nullify() return false } @@ -431,7 +579,7 @@ object BreakManager : RequestHandler(), PositionBlocking { val overBreakThreshold = progress >= info.getBreakThreshold() - if (info.redundant) { + if (info.isRedundant) { if (overBreakThreshold) { onBlockBreak(info) } @@ -465,7 +613,7 @@ object BreakManager : RequestHandler(), PositionBlocking { val swing = info.breakConfig.swing if (overBreakThreshold) { - if (info.type == Primary) { + if (info.isPrimary) { interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) @@ -474,7 +622,7 @@ object BreakManager : RequestHandler(), PositionBlocking { onBlockBreak(info) } if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) - setBreakCooldown(info.breakConfig.breakDelay) + breakCooldown = info.breakConfig.breakDelay } else { if (swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) } @@ -482,7 +630,14 @@ object BreakManager : RequestHandler(), PositionBlocking { return true } - private fun SafeContext.attackBlock(info: BreakInfo): Boolean { + /** + * A modified version of the minecraft attackBlock method. + * + * @return if the block started breaking successfully. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock + */ + private fun SafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false @@ -493,7 +648,7 @@ object BreakManager : RequestHandler(), PositionBlocking { onBlockBreak(info) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } - setBreakCooldown(info.breakConfig.breakDelay) + breakCooldown = info.breakConfig.breakDelay return true } if (info.breaking) return false @@ -522,43 +677,23 @@ object BreakManager : RequestHandler(), PositionBlocking { info.stopBreakPacket(world, interaction) } info.startBreakPacket(world, interaction) - if (info.type == BreakType.Secondary || (breakDelta < 1 && breakDelta >= info.breakConfig.breakThreshold)) { + if (info.isSecondary || (breakDelta < 1 && breakDelta >= info.breakConfig.breakThreshold)) { info.stopBreakPacket(world, interaction) } return true } - private fun SafeContext.onBlockBreak(info: BreakInfo) { - when (info.breakConfig.breakConfirmation) { - BreakConfirmationMode.None -> { - destroyBlock(info) - info.internalOnBreak() - if (!info.callbacksCompleted) { - addPendingBreak(info) - } - } - BreakConfirmationMode.BreakThenAwait -> { - destroyBlock(info) - addPendingBreak(info) - } - BreakConfirmationMode.AwaitThenBreak -> { - addPendingBreak(info) - } - } - info.nullify() - } - - private fun addPendingBreak(info: BreakInfo) { - pendingBreaks.add(info) - info.pendingInteractionsList.add(info.context) - } - - private fun removePendingBreak(info: BreakInfo) { - pendingBreaks.remove(info) - info.pendingInteractionsList.remove(info.context) - } - + /** + * A modified version of the minecraft breakBlock method. + * + * Performs the actions required to display break particles, sounds, texture overlay, etc. + * based on the users settings. + * + * @return if the blocks state was set or not. + * + * @see net.minecraft.client.world.ClientWorld.breakBlock + */ private fun SafeContext.destroyBlock(info: BreakInfo): Boolean { val ctx = info.context @@ -580,49 +715,5 @@ object BreakManager : RequestHandler(), PositionBlocking { return setState } - private fun isOnBreakCooldown() = blockBreakingCooldown > 0 - private fun setBreakCooldown(cooldown: Int) { - blockBreakingCooldown = cooldown - } - - private fun BreakInfo.makeSecondary() { - if (secondaryBreak === this) return - secondaryBreak = this.apply { - type = BreakType.Secondary - } - primaryBreak = null - } - - private fun BreakInfo.cancelBreak() = - runSafe { - setBreakingTextureStage(player, world, -1) - if (type == Primary) { - abortBreakPacket(world, interaction) - nullify() - return@runSafe - } - if (type == BreakType.Secondary && breakConfig.unsafeCancels) { - makeRedundant() - } - } - - private fun BreakInfo.nullify() { - type.nullify() - if (!broken) internalOnCancel() - } - - private fun BreakInfo.makeRedundant() { - makeSecondary() - type = BreakType.RedundantSecondary - internalOnCancel() - } - - private fun BreakType.nullify() = - when (this) { - Primary -> primaryBreak = null - else -> secondaryBreak = null - } - - override fun preEvent() = UpdateManagerEvent.Break.Pre().post() - override fun postEvent() = UpdateManagerEvent.Break.Post().post() + override fun preEvent(): Event = UpdateManagerEvent.Break().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index d6cd1a09f..1615368da 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -18,8 +18,6 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.Priority @@ -33,17 +31,15 @@ import net.minecraft.util.math.BlockPos data class BreakRequest( val contexts: Collection, - val buildConfig: BuildConfig, - val rotationConfig: RotationConfig, - val interactionConfig: InteractionConfig, - val inventoryConfig: InventoryConfig, - val hotbarConfig: HotbarConfig, - val prio: Priority = 0, - val pendingInteractionsList: MutableCollection, + val build: BuildConfig, + val rotation: RotationConfig, + val hotbar: HotbarConfig, + val pendingInteractions: MutableCollection, val onAccept: ((BlockPos) -> Unit)? = null, val onCancel: ((BlockPos) -> Unit)? = null, val onBreak: ((BlockPos) -> Unit)? = null, val onItemDrop: ((ItemEntity) -> Unit)? = null, + private val prio: Priority = 0 ) : Request(prio) { override val done: Boolean get() = runSafe { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt index d19715d07..5f1ae8bcf 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request.hotbar +import com.lambda.config.groups.TickStage import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -51,7 +52,12 @@ abstract class HotbarConfig( * * Affects the validity state of the request */ - abstract var swapPause: Int + abstract val swapPause: Int + + /** + * The sub-tick timings at which hotbar actions can be performed + */ + abstract val sequenceStageMask: Set /** * Registers a hotbar request with the HotbarManager. @@ -59,6 +65,6 @@ abstract class HotbarConfig( * @param request The hotbar request to register. */ override fun requestInternal(request: HotbarRequest) { - HotbarManager.registerRequest(this, request) + HotbarManager.request(request) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 8bc41f7e3..0f2f099ea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -17,16 +17,18 @@ package com.lambda.interaction.request.hotbar -import com.lambda.Lambda.mc +import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.core.Loadable +import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.InventoryEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap +import com.lambda.interaction.request.hotbar.HotbarManager.preEvent import com.lambda.mixin.entity.PlayerInventoryMixin import com.lambda.mixin.render.InGameHudMixin import com.lambda.threading.runSafe @@ -37,117 +39,78 @@ import com.lambda.threading.runSafe * @see PlayerInventoryMixin.handleSpoofedBlockBreakingSpeed * @see InGameHudMixin.onTick */ -object HotbarManager : RequestHandler(), Loadable { +object HotbarManager : RequestHandler( + TickStage.TickStart, + TickStage.PostMovement, + postClose = { checkResetSwap() }, + onOpen = { preEvent() } +), Loadable { val serverSlot get() = runSafe { interaction.lastSelectedSlot } ?: 0 override fun load() = "Loaded Hotbar Manager" - private var currentSlotInfo: SlotInfo? = null - private var maxSwapsThisTick = 0 private var swapsThisTick = 0 + private var maxSwapsThisTick = 0 private var swapDelay = 0 - fun Any.onHotbarUpdate( - alwaysListen: Boolean = false, - priority: Priority = 0, - block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { - block() - } - - fun Any.onHotbarUpdatePost( - alwaysListen: Boolean = false, - priority: Priority = 0, - block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { - block() - } + private var activeRequest: HotbarRequest? = null init { - listen(priority = Int.MIN_VALUE) { - preEvent() - if (swapDelay > 0) swapDelay-- - - if (requestMap.isNotEmpty()) { - val sortedRequests = requestMap.toSortedMap(compareByDescending { it.priority }).values - - sortedRequests.first().let { request -> - maxSwapsThisTick = request.hotbarConfig.swapsPerTick - } - - sortedRequests.forEach { request -> - val actionSequence = request.actionSequence - HotbarActionSequence(request).apply { actionSequence() } - } - } - currentSlotInfo?.let { current -> - if (current.keepTicks <= 0) { - currentSlotInfo = null - interaction.syncSelectedSlot() - } - } - requestMap.clear() - postEvent() - } - listen(priority = Int.MIN_VALUE) { swapsThisTick = 0 - val slotInfo = currentSlotInfo ?: return@listen + if (swapDelay > 0) swapDelay-- + val slotInfo = activeRequest ?: return@listen slotInfo.swapPauseAge++ slotInfo.activeRequestAge++ slotInfo.keepTicks-- if (slotInfo.keepTicks <= 0) { - currentRequest = null + activeRequest = null } } listen(priority = Int.MIN_VALUE) { - it.slot = currentSlotInfo?.slot ?: return@listen + it.slot = activeRequest?.slot ?: return@listen } } - @DslMarker - private annotation class ActionSequence - - @ActionSequence - class HotbarActionSequence(val request: HotbarRequest) { - @ActionSequence - fun swapTo( - slot: Int, - keepTicks: Int = request.hotbarConfig.keepTicks - ): Boolean { - request.swapSlot = SlotInfo(slot, keepTicks, request.hotbarConfig.swapPause) - if (slot != currentSlotInfo?.slot) { - if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) { - return false - } - - currentSlotInfo?.let { current -> - if (current.activeRequestAge == 0 && current.keepTicks > 0) { - return false - } - } - - swapsThisTick++ - swapDelay = request.hotbarConfig.swapDelay - } else currentSlotInfo?.let { current -> - request.swapSlot?.swapPauseAge = current.swapPauseAge + override fun SafeContext.handleRequest(request: HotbarRequest) { + val hotbarConfig = request.hotbarConfig + maxSwapsThisTick = hotbarConfig.swapsPerTick + swapDelay = swapDelay.coerceAtMost(hotbarConfig.swapDelay) + + if (tickStage !in hotbarConfig.sequenceStageMask) return + + if (request.slot != activeRequest?.slot) { + if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return + + activeRequest?.let { current -> + if (current.swappedThisTick && current.keeping) return } - currentSlotInfo = request.swapSlot - mc.interactionManager?.syncSelectedSlot() - return request.done + + swapsThisTick++ + swapDelay = hotbarConfig.swapDelay + } else activeRequest?.let { current -> + request.swapPauseAge = current.swapPauseAge + if (current.swappedThisTick && current.keeping) return } - @ActionSequence - fun done() { - request.instantActionsComplete = true + activeRequest = request + interaction.syncSelectedSlot() + return + } + + private fun SafeContext.checkResetSwap() { + activeRequest?.let { active -> + if (active.keepTicks <= 0) { + activeRequest = null + interaction.syncSelectedSlot() + } } } - override fun preEvent() = UpdateManagerEvent.Hotbar.Pre().post() - override fun postEvent() = UpdateManagerEvent.Hotbar.Post().post() + override fun preEvent(): Event = UpdateManagerEvent.Hotbar().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index a3106ee30..8c0539735 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -21,23 +21,19 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request class HotbarRequest( + val slot: Int, val hotbarConfig: HotbarConfig, + var keepTicks: Int = hotbarConfig.keepTicks, + var swapPause: Int = hotbarConfig.swapPause, priority: Priority = 0, - val actionSequence: HotbarManager.HotbarActionSequence.() -> Unit, ) : Request(priority) { - var swapSlot: SlotInfo? = null + var activeRequestAge = 0 + var swapPauseAge = 0 - var instantActionsComplete = false - override val done: Boolean - get() = swapSlot?.let { it.slot == HotbarManager.serverSlot && !it.swapPaused } ?: true + val swapPaused get() = swapPauseAge < swapPause + val swappedThisTick get() = activeRequestAge <= 0 + val keeping get() = keepTicks > 0 - constructor ( - slot: Int, - hotbarConfig: HotbarConfig, - priority: Priority = 0 - ) : this( - hotbarConfig, - priority, - { if (swapTo(slot, hotbarConfig.keepTicks.coerceAtLeast(1))) done() } - ) + override val done: Boolean + get() = slot == HotbarManager.serverSlot && !swapPaused } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt index c419319a5..c83ac3a59 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt @@ -24,5 +24,8 @@ data class SlotInfo( ) { var activeRequestAge = 0 var swapPauseAge = 0 + val swapPaused get() = swapPauseAge < swapPause + val swappedThisTick get() = activeRequestAge <= 0 + val keeping get() = keepTicks > 0 } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 036ee8346..fad02fe90 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.TickStage import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -31,7 +32,7 @@ abstract class PlaceConfig( protected abstract val axisRotateSetting: Boolean val axisRotate get() = airPlace.isEnabled() && axisRotateSetting - abstract val sequenceMode: BuildConfig.InteractSequenceMode + abstract val placeStageMask: Set abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int @@ -40,7 +41,7 @@ abstract class PlaceConfig( abstract val sounds: Boolean override fun requestInternal(request: PlaceRequest) { - PlaceManager.registerRequest(this, request) + PlaceManager.request(request) } enum class AirPlaceMode { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 87c0735e9..b8be34a38 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -18,11 +18,13 @@ package com.lambda.interaction.request.placing import com.lambda.Lambda.mc -import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext +import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.MovementEvent +import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -35,8 +37,9 @@ import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarManager -import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.placing.PlaceManager.activeRequest +import com.lambda.interaction.request.placing.PlaceManager.preEvent +import com.lambda.interaction.request.placing.PlaceManager.processRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.item @@ -64,7 +67,12 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.GameMode -object PlaceManager : RequestHandler(), PositionBlocking { +object PlaceManager : RequestHandler( + TickStage.TickStart, + TickStage.PostMovement, + preOpen = { activeRequest?.let { processRequest(it) } }, + onOpen = { preEvent() } +), PositionBlocking { private val pendingPlacements = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { @@ -73,15 +81,12 @@ object PlaceManager : RequestHandler(), PositionBlocking { it.pendingInteractionsList.remove(it.context) } - private var potentialPlacements = listOf() - private var nextPredictedRotation: RotationRequest? = null + private var activeRequest: PlaceRequest? = null - private var hotbarRequest: HotbarRequest? = null - private val swappedTo: (Int) -> Boolean = { slot -> - hotbarRequest?.let { hotbarRequest -> - hotbarRequest.swapSlot?.slot == slot && hotbarRequest.done - } ?: false - } + private var placementsThisTick = 0 + private var maxPlacementsThisTick = 0 + + private var potentialPlacements = mutableListOf() private var shouldSneak = false private val validSneak: (player: ClientPlayerEntity) -> Boolean = @@ -94,50 +99,14 @@ object PlaceManager : RequestHandler(), PositionBlocking { alwaysListen: Boolean = false, priority: Priority = 0, block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { - block() - } - - fun Any.onPlacePost( - alwaysListen: Boolean = false, - priority: Priority = 0, - block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { + ) = this.listen(priority, alwaysListen) { block() } init { - listen { - preEvent() - - pendingPlacements.cleanUp() - - if (!updateRequest()) { - postEvent() - return@listen - } - - val request = currentRequest ?: return@listen - if (BreakManager.activeThisTick) return@listen - - hotbarRequest = HotbarRequest(request.hotbarConfig) { - if (update(::swapTo, tickPre = true)) done() - } - hotbarRequest?.let { hotbarRequest -> - request.hotbarConfig.request(hotbarRequest) - } - if (potentialPlacements.isNotEmpty()) activeThisTick = true - } - - //ToDo: add mixin for vanilla place timings - - listen { - currentRequest?.let { request -> - if (request.buildConfig.placeSettings.sequenceMode == BuildConfig.InteractSequenceMode.PostMovement) { - update(null) - postEvent() - } - } + listen(priority = Int.MIN_VALUE) { + activeRequest = null + placementsThisTick = 0 } listen(priority = Int.MIN_VALUE) { @@ -171,51 +140,60 @@ object PlaceManager : RequestHandler(), PositionBlocking { } } - private fun SafeContext.update(swapTo: ((slot: Int) -> Boolean)?, tickPre: Boolean = false): Boolean { - currentRequest?.let { request -> - val placeConfig = request.buildConfig.placeSettings - - if (tickPre) { - pendingPlacements.setSizeLimit(placeConfig.maxPendingPlacements) - pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L) - - val isSneaking = player.isSneaking - val currentHotbarIndex = HotbarManager.serverSlot - val placeContexts = request.placeContexts - .filter { canPlace(it) } - .sortedWith( - compareByDescending { it.hotbarIndex == currentHotbarIndex } - .thenByDescending { it.sneak == isSneaking } - ) - - val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) - val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick)) - nextPredictedRotation = if (request.rotationConfig.rotate) placeContexts.getOrNull(takeCount)?.rotation else null - potentialPlacements = placeContexts.take(takeCount) - } + override fun SafeContext.handleRequest(request: PlaceRequest) { + if (activeRequest != null || BreakManager.activeThisTick) return - potentialPlacements.forEach { ctx -> - swapTo?.invoke(ctx.hotbarIndex) - if (request.buildConfig.placeSettings.rotate) request.rotationConfig.request(ctx.rotation) - if (ctx.sneak) shouldSneak = true - val mismatchedTiming = tickPre && placeConfig.sequenceMode != BuildConfig.InteractSequenceMode.TickStart - if (mismatchedTiming) return false - if (!swappedTo(ctx.hotbarIndex) || !ctx.rotation.done || !validSneak(player)) return false - - val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) { - warn("Placement interaction failed with $actionResult") - } - } - requestNextPredictedRotation(request) - return true + activeRequest = request + processRequest(request) + if (placementsThisTick > 0) activeThisTick = true + } + + fun SafeContext.processRequest(request: PlaceRequest) { + pendingPlacements.cleanUp() + + if (request.fresh) populateFrom(request) + + val iterator = potentialPlacements.iterator() + while (iterator.hasNext()) { + if (placementsThisTick + 1 > maxPlacementsThisTick) break + val ctx = iterator.next() + + if (ctx.sneak) shouldSneak = true + if (!ctx.requestDependencies(request) || !validSneak(player)) return + if (tickStage !in request.build.placing.placeStageMask) return + + val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) + if (!actionResult.isAccepted) warn("Placement interaction failed with $actionResult") + placementsThisTick++ + iterator.remove() + } + if (potentialPlacements.isEmpty()) { + activeRequest = null + return } + if (!request.rotation.rotate) return - return false + // In case you cant rotate and place within the same tick + potentialPlacements.getOrNull(maxPlacementsThisTick)?.let { nextPredictedPlacement -> + request.rotation.request(nextPredictedPlacement.rotation) + } } - private fun requestNextPredictedRotation(request: PlaceRequest) { - nextPredictedRotation?.let { rot -> request.rotationConfig.request(rot) } + private fun SafeContext.populateFrom(request: PlaceRequest) { + val place = request.build.placing + + pendingPlacements.setSizeLimit(place.maxPendingPlacements) + pendingPlacements.setDecayTime(request.build.interactionTimeout * 50L) + + potentialPlacements = request.contexts + .filter { canPlace(it) } + .sortedWith( + compareByDescending { it.hotbarIndex == HotbarManager.serverSlot } + .thenByDescending { it.sneak == player.isSneaking } + ).toMutableList() + + val pendingLimit = (place.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) + maxPlacementsThisTick = (place.placementsPerTick.coerceAtMost(pendingLimit)) } private fun canPlace(placeContext: PlaceContext) = @@ -235,7 +213,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { val hitResult = placeContext.result if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS - return interactBlockInternal(placeContext, request, request.buildConfig.placeSettings, hand, hitResult) + return interactBlockInternal(placeContext, request, request.build.placing, hand, hitResult) } private fun SafeContext.interactBlockInternal( @@ -314,7 +292,7 @@ object PlaceManager : RequestHandler(), PositionBlocking { val stackCountPre = stackInHand.count if (placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) { addPendingPlace( - PlaceInfo(placeContext, request.onPlace, request.pendingInteractionsList, placeConfig) + PlaceInfo(placeContext, request.onPlace, request.pendingInteractions, placeConfig) ) } @@ -406,6 +384,5 @@ object PlaceManager : RequestHandler(), PositionBlocking { val placeConfig: PlaceConfig ) - override fun preEvent() = UpdateManagerEvent.Place.Pre().post() - override fun postEvent() = UpdateManagerEvent.Place.Post().post() + override fun preEvent(): Event = UpdateManagerEvent.Place().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index 0b5224b14..4f91d09b7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InteractionConfig import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.request.Priority @@ -29,17 +28,16 @@ import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState data class PlaceRequest( - val placeContexts: Collection, - val buildConfig: BuildConfig, - val rotationConfig: RotationConfig, - val hotbarConfig: HotbarConfig, - val interactionConfig: InteractionConfig, - val pendingInteractionsList: MutableCollection, + val contexts: Collection, + val build: BuildConfig, + val rotation: RotationConfig, + val hotbar: HotbarConfig, + val pendingInteractions: MutableCollection, val prio: Priority = 0, val onPlace: () -> Unit ) : Request(prio) { override val done: Boolean get() = runSafe { - placeContexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } + contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index 67d2bd924..c3314ad17 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -52,7 +52,7 @@ abstract class RotationConfig(priority: Priority) : RequestConfig(), Loadable { +object RotationManager : RequestHandler( + TickStage.PreMovement, + preOpen = { preEvent() } +), Loadable { var currentRotation = Rotation.ZERO private var prevRotation = Rotation.ZERO + private var changedThisTick = false + private var activeRequest: RotationRequest? = null + override fun load() = "Loaded Rotation Manager" - /** - * Registers a listener called immediately before [RotationManager] handles its context and applies rotation updates. - * - * This is useful if you need to synchronize with the latest player state (including inputs and movement), - * or if you must place your rotation requests as the tick is completed and just before the new rotation is processed. - * - * @param alwaysListen Whether to keep this listener active at all times. Defaults to false. - * @param block A callback providing a [SafeContext] where you can place rotation-related operations or other logic. - */ fun Any.onRotate( alwaysListen: Boolean = false, priority: Priority = 0, block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { + ) = this.listen(priority, alwaysListen) { block() } - fun Any.onRotatePost( - alwaysListen: Boolean = false, - priority: Priority = 0, - block: SafeContext.() -> Unit - ) = this.listen(priority, alwaysListen) { - block() - } init { + listen(priority = Int.MIN_VALUE) { + changedThisTick = false + } + listen { event -> val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listen @@ -93,25 +90,24 @@ object RotationManager : RequestHandler(), Loadable { } } - @JvmStatic - fun processRotations() = runSafe { - preEvent() - - // Update the request - val changed = updateRequest(true) { entry -> - // skip requests that have failed to build the rotation - // to free the request place for others - entry.value.target.targetRotation.value != null + override fun SafeContext.handleRequest(request: RotationRequest) { + if (activeRequest != null) return + if (request.target.targetRotation.value != null) { + activeRequest = request + changedThisTick = true } + } - if (currentRequest != null) activeThisTick = true + @JvmStatic + fun processRotations() = runSafe { + if (activeRequest != null) activeThisTick = true - if (!changed) { // rebuild the rotation if the same context gets used again - currentRequest?.target?.targetRotation?.update() + if (!changedThisTick) { // rebuild the rotation if the same context gets used again + activeRequest?.target?.targetRotation?.update() } // Calculate the target rotation - val targetRotation = currentRequest?.let { request -> + val targetRotation = activeRequest?.let { request -> val rotationTo = if (request.keepTicks >= 0) request.target.targetRotation.value ?: currentRotation // same context gets used again && the rotation is null this tick @@ -128,25 +124,23 @@ object RotationManager : RequestHandler(), Loadable { currentRotation = targetRotation/*.fixSensitivity(prevRotation)*/ // Handle LOCK mode - if (currentRequest?.mode == RotationMode.Lock) { + if (activeRequest?.mode == RotationMode.Lock) { player.yaw = currentRotation.yawF player.pitch = currentRotation.pitchF } // Tick and reset the context - currentRequest?.let { + activeRequest?.let { if (--it.keepTicks > 0) return@let if (--it.decayTicks >= 0) return@let - currentRequest = null + activeRequest = null } - - postEvent() } private fun reset(rotation: Rotation) { prevRotation = rotation currentRotation = rotation - currentRequest = null + activeRequest = null } private val smoothRotation @@ -156,45 +150,45 @@ object RotationManager : RequestHandler(), Loadable { @JvmStatic val lockRotation get() = - if (currentRequest?.mode == RotationMode.Lock) smoothRotation else null + if (activeRequest?.mode == RotationMode.Lock) smoothRotation else null @JvmStatic val renderYaw get() = - if (currentRequest == null) null else smoothRotation.yaw.toFloat() + if (activeRequest == null) null else smoothRotation.yaw.toFloat() @JvmStatic val renderPitch get() = - if (currentRequest == null) null else smoothRotation.pitch.toFloat() + if (activeRequest == null) null else smoothRotation.pitch.toFloat() @JvmStatic val handYaw get() = - if (currentRequest?.mode == RotationMode.Lock) currentRotation.yaw.toFloat() else null + if (activeRequest?.mode == RotationMode.Lock) currentRotation.yaw.toFloat() else null @JvmStatic val handPitch get() = - if (currentRequest?.mode == RotationMode.Lock) currentRotation.pitch.toFloat() else null + if (activeRequest?.mode == RotationMode.Lock) currentRotation.pitch.toFloat() else null @JvmStatic val movementYaw: Float? get() { - if (currentRequest?.mode == RotationMode.Silent) return null + if (activeRequest?.mode == RotationMode.Silent) return null return currentRotation.yaw.toFloat() } @JvmStatic val movementPitch: Float? get() { - if (currentRequest?.mode == RotationMode.Silent) return null + if (activeRequest?.mode == RotationMode.Silent) return null return currentRotation.pitch.toFloat() } @JvmStatic fun getRotationForVector(deltaTime: Double): Vec2d? { - if (currentRequest?.mode == RotationMode.Silent) return null + if (activeRequest?.mode == RotationMode.Silent) return null val rot = lerp(deltaTime, prevRotation, currentRotation) return Vec2d(rot.yaw, rot.pitch) @@ -242,7 +236,7 @@ object RotationManager : RequestHandler(), Loadable { // Actual yaw used by the physics engine var actualYaw = currentRotation.yaw - if (currentRequest?.mode == RotationMode.Silent) { + if (activeRequest?.mode == RotationMode.Silent) { actualYaw = player.yaw.toDouble() } @@ -278,6 +272,5 @@ object RotationManager : RequestHandler(), Loadable { } } - override fun preEvent() = UpdateManagerEvent.Rotation.Pre().post() - override fun postEvent() = UpdateManagerEvent.Rotation.Post().post() + override fun preEvent(): Event = UpdateManagerEvent.Rotation().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index ae4a5e7ee..974876f2a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -21,8 +21,10 @@ import com.lambda.context.SafeContext import com.lambda.event.events.ClientEvent import com.lambda.event.events.MovementEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.* +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationMode import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index e698972cf..ddbe9faa0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -47,7 +47,7 @@ object PacketMine : Module( private val page by setting("Page", Page.Build) private val build = BuildSettings(this) { page == Page.Build } - private val breakConfig = build.breakSettings + private val breakConfig = build.breaking private val rotation = RotationSettings(this) { page == Page.Rotation } private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } private val inventory = InventorySettings(this) { page == Page.Inventory } @@ -81,12 +81,11 @@ object PacketMine : Module( } val request = BreakRequest( - breakContexts(requestPositions), build, rotation, interact, inventory, hotbar, - pendingInteractionsList = pendingInteractionsList, - onAccept = { breakingPositions[0] = it }, + breakContexts(requestPositions), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, onAccept = { breakingPositions[0] = it }, onCancel = { nullifyBreakPos(it) }, - onBreak = { breaks++; nullifyBreakPos(it) } - ) { _ -> itemDrops++ } + onBreak = { breaks++; nullifyBreakPos(it) }, + { _ -> itemDrops++ } + ) breakConfig.request(request) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 7848760f3..980662201 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -161,13 +161,11 @@ class BuildTask @Ta5kBuilder constructor( val breakResults = resultsNotBlocked.filterIsInstance() val requestContexts = arrayListOf() - if (build.breakSettings.instantBreaksPerTick > 1) { - val takeCount = build.breakSettings - .instantBreaksPerTick - .coerceAtMost(emptyPendingInteractionSlots) + if (build.breaking.breaksPerTick > 1) { + val take = emptyPendingInteractionSlots.coerceAtLeast(0) breakResults .filter { it.context.instantBreak } - .take(takeCount) + .take(take) .let { instantBreakResults -> requestContexts.addAll(instantBreakResults.map { it.context }) } @@ -178,12 +176,12 @@ class BuildTask @Ta5kBuilder constructor( } val request = BreakRequest( - requestContexts, build, rotation, interact, inventory, hotbar, - pendingInteractionsList = pendingInteractions, + requestContexts, build, rotation, hotbar, + pendingInteractions = pendingInteractions, onBreak = { breaks++ }, onItemDrop = onItemDrop ) - build.breakSettings.request(request) + build.breaking.request(request) return@listen } is PlaceResult.Place -> { @@ -192,9 +190,9 @@ class BuildTask @Ta5kBuilder constructor( .distinctBy { it.blockPos } .take(emptyPendingInteractionSlots) - build.placeSettings.request( + build.placing.request( PlaceRequest( - placeResults.map { it.context }, build, rotation, hotbar, interact, pendingInteractions + placeResults.map { it.context }, build, rotation, hotbar, pendingInteractions ) { placements++ } ) } From d59d6e23bd5fa2b6f1bf8d47fb40f86a5c65ca44 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 01:25:45 +0100 Subject: [PATCH 124/364] cleanup --- .../request/breaking/BreakManager.kt | 2 +- .../interaction/request/hotbar/SlotInfo.kt | 31 ------------------- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 6854c3ff0..02ca3f019 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -422,7 +422,7 @@ object BreakManager : RequestHandler( * callback will be triggered. * * @see destroyBlock - * @see addPendingBreak + * @see startPending */ private fun SafeContext.onBlockBreak(info: BreakInfo) { when (info.breakConfig.breakConfirmation) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt deleted file mode 100644 index c83ac3a59..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/SlotInfo.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.interaction.request.hotbar - -data class SlotInfo( - val slot: Int, - var keepTicks: Int, - var swapPause: Int -) { - var activeRequestAge = 0 - var swapPauseAge = 0 - - val swapPaused get() = swapPauseAge < swapPause - val swappedThisTick get() = activeRequestAge <= 0 - val keeping get() = keepTicks > 0 -} \ No newline at end of file From b69a617ad28f8b61edba912d2f6b742470d07e2e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 01:29:10 +0100 Subject: [PATCH 125/364] fixed concurrent modification exception --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 02ca3f019..312db376d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -320,10 +320,12 @@ object BreakManager : RequestHandler( * @see atMaxBreakInfos */ private fun SafeContext.processNewBreaks(request: BreakRequest): Boolean { - breaks.forEach { ctx -> + val iterator = breaks.iterator() + while (iterator.hasNext()) { + val ctx = iterator.next() initNewBreak(ctx, request) ?: return false request.onAccept?.invoke(ctx.expectedPos) - breaks.remove(ctx) + iterator.remove() if (atMaxBreakInfos(request.build.breaking)) return false } return true From b94e115aa5101e9da9807775894440aedaf64d19 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 04:44:53 +0100 Subject: [PATCH 126/364] set fresh to false for queued requests in request handler and added age checks to rotation requests --- .../kotlin/com/lambda/interaction/request/RequestHandler.kt | 1 + .../lambda/interaction/request/rotation/RotationManager.kt | 5 ++++- .../lambda/interaction/request/rotation/RotationRequest.kt | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 13864b6f7..acee69db4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -81,6 +81,7 @@ abstract class RequestHandler( preOpen?.invoke(this) queuedRequest?.let { request -> handleRequest(request) + request.fresh = false queuedRequest = null } acceptingRequests = true diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 8834842a9..5f2954d3a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -73,6 +73,9 @@ object RotationManager : RequestHandler( init { listen(priority = Int.MIN_VALUE) { + activeRequest?.let { request -> + request.age++ + } changedThisTick = false } @@ -91,7 +94,7 @@ object RotationManager : RequestHandler( } override fun SafeContext.handleRequest(request: RotationRequest) { - if (activeRequest != null) return + activeRequest?.let { if (it.age <= 0) return } if (request.target.targetRotation.value != null) { activeRequest = request changedThisTick = true diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index 78bcc4310..b0516ef4a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -31,6 +31,7 @@ data class RotationRequest( val turnSpeed: () -> Double = { 180.0 }, val speedMultiplier: Double = 1.0 ) : Request(prio) { + var age = 0 constructor( target: RotationTarget, From 4bef75243a2d692ee02c9cce3ba5ddd9f8eb9cfd Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 05:09:23 +0100 Subject: [PATCH 127/364] add rotation stage mask, and open requests for all stages in break, place, and hotbar managers --- .../com/lambda/config/groups/RotationSettings.kt | 11 ++++++++++- .../com/lambda/interaction/request/RequestHandler.kt | 4 ++-- .../interaction/request/breaking/BreakManager.kt | 3 +-- .../interaction/request/hotbar/HotbarManager.kt | 3 +-- .../interaction/request/placing/PlaceManager.kt | 3 +-- .../interaction/request/rotation/RotationConfig.kt | 7 +++++++ .../interaction/request/rotation/RotationRequest.kt | 3 ++- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 129490058..1d17d24b9 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -21,7 +21,11 @@ import com.lambda.config.Configurable import com.lambda.interaction.request.Priority import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationMode -import kotlin.math.* +import kotlin.math.PI +import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.ln +import kotlin.math.sqrt import kotlin.random.Random class RotationSettings( @@ -37,6 +41,11 @@ class RotationSettings( /** How many ticks to wait before resetting the rotation */ override val decayTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } + /** + * At what sub-tick stages rotations can be performed + */ + override val rotationStageMask by c.setting("Rotation Stage Mask", setOf(*TickStage.entries.toTypedArray()), "The sub-tick stages at which rotations can be performed", vis) + /** Whether the rotation is instant */ var instant by c.setting("Instant Rotation", true, "Instantly rotate") { rotate && vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index acee69db4..64a41c900 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -60,8 +60,8 @@ abstract class RequestHandler( openStages.forEach { stage -> when(stage) { TickStage.TickStart -> openRequestsFor(TickStage.TickStart) - TickStage.PostHotbar -> TODO() - TickStage.PostInteract -> TODO() + TickStage.PostHotbar -> { /*ToDo*/ } + TickStage.PostInteract -> { /*ToDo*/ } TickStage.PreMovement -> openRequestsFor(TickStage.PreMovement) TickStage.PostMovement -> openRequestsFor(TickStage.PostMovement) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 312db376d..e5870e035 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -68,8 +68,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkSectionPos object BreakManager : RequestHandler( - TickStage.TickStart, - TickStage.PostMovement, + *TickStage.entries.toTypedArray(), preOpen = { activeRequest?.let { processRequest(it) } }, onOpen = { preEvent() } ), PositionBlocking { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 0f2f099ea..6ab901902 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -40,8 +40,7 @@ import com.lambda.threading.runSafe * @see InGameHudMixin.onTick */ object HotbarManager : RequestHandler( - TickStage.TickStart, - TickStage.PostMovement, + *TickStage.entries.toTypedArray(), postClose = { checkResetSwap() }, onOpen = { preEvent() } ), Loadable { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index b8be34a38..fe27ca008 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -68,8 +68,7 @@ import net.minecraft.util.math.Direction import net.minecraft.world.GameMode object PlaceManager : RequestHandler( - TickStage.TickStart, - TickStage.PostMovement, + *TickStage.entries.toTypedArray(), preOpen = { activeRequest?.let { processRequest(it) } }, onOpen = { preEvent() } ), PositionBlocking { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index c3314ad17..a7aacccec 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request.rotation +import com.lambda.config.groups.TickStage import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -49,6 +50,11 @@ abstract class RotationConfig(priority: Priority) : RequestConfig + val rotate: Boolean get() = rotationMode != RotationMode.None override fun requestInternal(request: RotationRequest) { @@ -59,6 +65,7 @@ abstract class RotationConfig(priority: Priority) : RequestConfig Double = { 180.0 }, @@ -37,7 +38,7 @@ data class RotationRequest( target: RotationTarget, config: RotationConfig, speedMultiplier: Double = 1.0 - ) : this(target, config.priority, config.rotationMode, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier) + ) : this(target, config.priority, config.rotationMode, config, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier) override val done: Boolean get() = mode == RotationMode.None || runSafe { From c37b72e6de3f3e5e32fdb87ce3d89b9f9546ee7a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 18:01:00 +0100 Subject: [PATCH 128/364] allow overriding the current queued request if the config matches and couple configs with requests --- .../main/kotlin/com/lambda/config/groups/TickStage.kt | 1 - .../kotlin/com/lambda/interaction/request/Request.kt | 3 ++- .../com/lambda/interaction/request/RequestHandler.kt | 4 ++-- .../interaction/request/breaking/BreakRequest.kt | 2 +- .../lambda/interaction/request/hotbar/HotbarManager.kt | 10 +++++----- .../lambda/interaction/request/hotbar/HotbarRequest.kt | 8 ++++---- .../lambda/interaction/request/placing/PlaceManager.kt | 2 +- .../lambda/interaction/request/placing/PlaceRequest.kt | 2 +- .../interaction/request/rotation/RotationManager.kt | 3 +-- .../interaction/request/rotation/RotationRequest.kt | 4 ++-- 10 files changed, 19 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt index 1e09b8873..c1160339b 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt @@ -21,6 +21,5 @@ enum class TickStage { TickStart, PostHotbar, PostInteract, - PreMovement, PostMovement, } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt index 9459c2111..74dd90f8d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt @@ -18,7 +18,8 @@ package com.lambda.interaction.request abstract class Request ( - val priority: Priority + val priority: Priority, + val config: RequestConfig<*> ) { var fresh = true diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 64a41c900..914c40d25 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -62,7 +62,6 @@ abstract class RequestHandler( TickStage.TickStart -> openRequestsFor(TickStage.TickStart) TickStage.PostHotbar -> { /*ToDo*/ } TickStage.PostInteract -> { /*ToDo*/ } - TickStage.PreMovement -> openRequestsFor(TickStage.PreMovement) TickStage.PostMovement -> openRequestsFor(TickStage.PostMovement) } } @@ -102,7 +101,8 @@ abstract class RequestHandler( */ fun request(request: R, queueIfClosed: Boolean = true): R { if (!acceptingRequests) { - if (queueIfClosed && queuedRequest == null) { + val canOverrideQueued = queuedRequest?.run { config === request.config } ?: true + if (queueIfClosed && canOverrideQueued) { queuedRequest = request } return request diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 1615368da..2a815fc5c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -40,7 +40,7 @@ data class BreakRequest( val onBreak: ((BlockPos) -> Unit)? = null, val onItemDrop: ((ItemEntity) -> Unit)? = null, private val prio: Priority = 0 -) : Request(prio) { +) : Request(prio, build.breaking) { override val done: Boolean get() = runSafe { contexts.all { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 6ab901902..a81edc91d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -77,11 +77,11 @@ object HotbarManager : RequestHandler( } override fun SafeContext.handleRequest(request: HotbarRequest) { - val hotbarConfig = request.hotbarConfig - maxSwapsThisTick = hotbarConfig.swapsPerTick - swapDelay = swapDelay.coerceAtMost(hotbarConfig.swapDelay) + val hotbar = request.hotbar + maxSwapsThisTick = hotbar.swapsPerTick + swapDelay = swapDelay.coerceAtMost(hotbar.swapDelay) - if (tickStage !in hotbarConfig.sequenceStageMask) return + if (tickStage !in hotbar.sequenceStageMask) return if (request.slot != activeRequest?.slot) { if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return @@ -91,7 +91,7 @@ object HotbarManager : RequestHandler( } swapsThisTick++ - swapDelay = hotbarConfig.swapDelay + swapDelay = hotbar.swapDelay } else activeRequest?.let { current -> request.swapPauseAge = current.swapPauseAge if (current.swappedThisTick && current.keeping) return diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index 8c0539735..e9cb0c806 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -22,11 +22,11 @@ import com.lambda.interaction.request.Request class HotbarRequest( val slot: Int, - val hotbarConfig: HotbarConfig, - var keepTicks: Int = hotbarConfig.keepTicks, - var swapPause: Int = hotbarConfig.swapPause, + val hotbar: HotbarConfig, + var keepTicks: Int = hotbar.keepTicks, + var swapPause: Int = hotbar.swapPause, priority: Priority = 0, -) : Request(priority) { +) : Request(priority, hotbar) { var activeRequestAge = 0 var swapPauseAge = 0 diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index fe27ca008..2cc6b920b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -173,7 +173,7 @@ object PlaceManager : RequestHandler( if (!request.rotation.rotate) return // In case you cant rotate and place within the same tick - potentialPlacements.getOrNull(maxPlacementsThisTick)?.let { nextPredictedPlacement -> + potentialPlacements.getOrNull(maxPlacementsThisTick - 1)?.let { nextPredictedPlacement -> request.rotation.request(nextPredictedPlacement.rotation) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index 4f91d09b7..5db12e617 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -35,7 +35,7 @@ data class PlaceRequest( val pendingInteractions: MutableCollection, val prio: Priority = 0, val onPlace: () -> Unit -) : Request(prio) { +) : Request(prio, build.placing) { override val done: Boolean get() = runSafe { contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 5f2954d3a..1b46a5dcb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -51,7 +51,7 @@ import kotlin.math.sign import kotlin.math.sin object RotationManager : RequestHandler( - TickStage.PreMovement, + TickStage.TickStart, preOpen = { preEvent() } ), Loadable { var currentRotation = Rotation.ZERO @@ -70,7 +70,6 @@ object RotationManager : RequestHandler( block() } - init { listen(priority = Int.MIN_VALUE) { activeRequest?.let { request -> diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index ccaadf672..6a897105d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -26,12 +26,12 @@ data class RotationRequest( val target: RotationTarget, val prio: Priority, val mode: RotationMode, - val rotationConfig: RotationConfig, + val rot: RotationConfig, var keepTicks: Int = 3, var decayTicks: Int = 0, val turnSpeed: () -> Double = { 180.0 }, val speedMultiplier: Double = 1.0 -) : Request(prio) { +) : Request(prio, rot) { var age = 0 constructor( From 770ec326b90a4cb37ea6fca5641c7e6d79692c82 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 14 Apr 2025 19:53:14 +0100 Subject: [PATCH 129/364] add pathing check --- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 980662201..c83eeacd4 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -113,7 +113,7 @@ class BuildTask @Ta5kBuilder constructor( .plus(pendingInteractions.toList()) .toMutableList() - if (bestPos != null) { + if (bestPos != null && build.pathing) { drawables.add(bestPos) } TaskFlowModule.drawables = drawables @@ -152,7 +152,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Contextual -> { bestPos?.let { - BaritoneUtils.setGoalAndPath(GoalNear(it.pos, 1)) + if (build.pathing) BaritoneUtils.setGoalAndPath(GoalNear(it.pos, 1)) } if (atMaxPendingInteractions) return@listen From 832a2a4a7a549f54a73c9002142559e6055facdb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 15 Apr 2025 14:47:56 +0100 Subject: [PATCH 130/364] BrokenBlockHandler and removed preOpen and postClose in favor of onOpen and onClose --- .../interaction/request/RequestHandler.kt | 9 +- .../request/breaking/BreakManager.kt | 183 ++++-------------- .../request/breaking/BrokenBlockHandler.kt | 154 +++++++++++++++ .../request/hotbar/HotbarManager.kt | 4 +- .../request/placing/PlaceManager.kt | 4 +- .../request/rotation/RotationManager.kt | 4 +- 6 files changed, 200 insertions(+), 158 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 914c40d25..520c60ebe 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -31,9 +31,8 @@ import com.lambda.threading.runSafe */ abstract class RequestHandler( vararg openStages: TickStage, - val preOpen: (SafeContext.() -> Unit)? = null, - val onOpen: (SafeContext.() -> Unit)? = null, - val postClose: (SafeContext.() -> Unit)? = null + private val onOpen: (SafeContext.() -> Unit)? = null, + private val onClose: (SafeContext.() -> Unit)? = null ) { /** * Represents if the handler is accepting requests at any given time @@ -77,7 +76,6 @@ abstract class RequestHandler( private inline fun openRequestsFor(stage: TickStage) { listen(priority = Int.MAX_VALUE) { tickStage = stage - preOpen?.invoke(this) queuedRequest?.let { request -> handleRequest(request) request.fresh = false @@ -85,10 +83,11 @@ abstract class RequestHandler( } acceptingRequests = true onOpen?.invoke(this) + preEvent() } listen(priority = Int.MIN_VALUE) { + onClose?.invoke(this) acceptingRequests = false - postClose?.invoke(this) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index e5870e035..a2a739dd1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.request.breaking -import com.lambda.Lambda.mc import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.event.Event @@ -37,26 +36,23 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode import com.lambda.interaction.request.breaking.BreakConfig.BreakMode import com.lambda.interaction.request.breaking.BreakManager.activeRequest -import com.lambda.interaction.request.breaking.BreakManager.preEvent import com.lambda.interaction.request.breaking.BreakManager.processRequest import com.lambda.interaction.request.breaking.BreakType.Primary +import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock +import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingBreaks +import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs +import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta -import com.lambda.util.BlockUtils.fluidState -import com.lambda.util.BlockUtils.matches -import com.lambda.util.Communication.info import com.lambda.util.Communication.warn -import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.gamemode import com.lambda.util.player.swingHand import net.minecraft.block.BlockState -import net.minecraft.block.OperatorBlock import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.entity.ItemEntity @@ -65,12 +61,10 @@ import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.ChunkSectionPos object BreakManager : RequestHandler( *TickStage.entries.toTypedArray(), - preOpen = { activeRequest?.let { processRequest(it) } }, - onOpen = { preEvent() } + onOpen = { activeRequest?.let { processRequest(it) } } ), PositionBlocking { private var primaryBreak: BreakInfo? get() = breakInfos[0] @@ -80,30 +74,12 @@ object BreakManager : RequestHandler( set(value) { breakInfos[1] = value } private val breakInfos = arrayOfNulls(2) - private val pendingBreaks = LimitedDecayQueue( - TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { info -> - mc.world?.let { world -> - val pos = info.context.expectedPos - val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)) - if (!loaded) return@let - - info("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") - - val awaitThenBreak = info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak - if (!info.broken && awaitThenBreak) { - world.setBlockState(info.context.expectedPos, info.context.checkedState) - } - } - info.pendingInteractions.remove(info.context) - } private val pendingBreakCount get() = breakInfos.count { it != null } + pendingBreaks.size - - private var activeRequest: BreakRequest? = null - override val blockedPositions get() = breakInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } + private var activeRequest: BreakRequest? = null + private var rotationRequest: RotationRequest? = null private val rotated get() = rotationRequest?.done != false @@ -140,29 +116,6 @@ object BreakManager : RequestHandler( } listen(priority = Int.MIN_VALUE + 1) { event -> - pendingBreaks - .firstOrNull { it.context.expectedPos == event.pos } - ?.let { pending -> - // return if the state hasn't changed - if (event.newState.matches(pending.context.checkedState)) - return@listen - - // return if the block's not broken - if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) { - pending.stopPending() - return@listen - } - - if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { - destroyBlock(pending) - } - pending.internalOnBreak() - if (pending.callbacksCompleted) { - pending.stopPending() - } - return@listen - } - breakInfos .filterNotNull() .firstOrNull { it.context.expectedPos == event.pos } @@ -185,15 +138,6 @@ object BreakManager : RequestHandler( // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work listen(priority = Int.MIN_VALUE + 1) { if (it.entity !is ItemEntity) return@listen - pendingBreaks - .firstOrNull { info -> matchesBlockItem(info, it.entity) } - ?.let { pending -> - pending.internalOnItemDrop(it.entity) - if (pending.callbacksCompleted) { - pending.stopPending() - } - return@listen - } breakInfos .filterNotNull() @@ -203,7 +147,6 @@ object BreakManager : RequestHandler( listenUnsafe(priority = Int.MIN_VALUE + 1) { breakInfos.forEach { it?.nullify() } - pendingBreaks.clear() breakCooldown = 0 } } @@ -286,6 +229,20 @@ object BreakManager : RequestHandler( maxBreaksThisTick = breakConfig.breaksPerTick.coerceAtMost(pendingLimit) } + /** + * @return if the break context can be accepted. + */ + private fun SafeContext.canAccept(ctx: BreakContext): Boolean { + if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false + + breakInfos.firstOrNull { it != null && !it.isRedundant } + ?.let { info -> + if ( ctx.hotbarIndex != info.context.hotbarIndex) return false + } + + return !blockState(ctx.expectedPos).isAir + } + /** * Attempts to break as many [BreakContext]'s as possible from the [instantBreaks] collection within this tick. * @@ -359,8 +316,7 @@ object BreakManager : RequestHandler( } primaryBreak = breakInfo - pendingBreaks.setSizeLimit(request.build.breaking.maxPendingBreaks) - pendingBreaks.setDecayTime(request.build.interactionTimeout * 50L) + setPendingConfigs(request) return primaryBreak } @@ -372,41 +328,6 @@ object BreakManager : RequestHandler( return breakInfos.take(possibleBreakingCount).all { it != null } } - /** - * @return if the [ItemEntity] matches the [BreakInfo]'s expected item drop. - */ - private fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { - val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) - val correctMaterial = info.context.checkedState.block == entity.stack.item.block - return inRange && correctMaterial - } - - /** - * @return if the [newState] matches the [targetState]. - * - * @see TargetState - */ - private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = - if (targetState.matches(newState, pos, world)) true - else { - this@BreakManager.warn("Break at ${pos.toShortString()} was rejected with $newState instead of $targetState") - false - } - - /** - * @return if the break context can be accepted. - */ - private fun SafeContext.canAccept(ctx: BreakContext): Boolean { - if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false - - breakInfos.firstOrNull { it != null && !it.isRedundant } - ?.let { info -> - if ( ctx.hotbarIndex != info.context.hotbarIndex) return false - } - - return !blockState(ctx.expectedPos).isAir - } - /** * Begins the post-break logic sequence for the given [info]. * @@ -446,22 +367,6 @@ object BreakManager : RequestHandler( info.nullify() } - /** - * Adds the [info] to the break manager, and requesters, pending interaction collections. - */ - private fun BreakInfo.startPending() { - pendingBreaks.add(this) - pendingInteractions.add(context) - } - - /** - * Removes the [info] from the break manager, and requesters, pending interation collections. - */ - private fun BreakInfo.stopPending() { - pendingBreaks.remove(this) - pendingInteractions.remove(context) - } - /** * Makes the [BreakInfo] a secondary if not already. */ @@ -686,35 +591,25 @@ object BreakManager : RequestHandler( } /** - * A modified version of the minecraft breakBlock method. - * - * Performs the actions required to display break particles, sounds, texture overlay, etc. - * based on the users settings. - * - * @return if the blocks state was set or not. - * - * @see net.minecraft.client.world.ClientWorld.breakBlock + * @return if the [ItemEntity] matches the [BreakInfo]'s expected item drop. */ - private fun SafeContext.destroyBlock(info: BreakInfo): Boolean { - val ctx = info.context - - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false - - if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) - return false - val block = ctx.checkedState.block - if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false - if (ctx.checkedState.isAir) return false - - block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) - val fluidState = fluidState(ctx.expectedPos) - val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) - if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) - - if (info.breakConfig.breakingTexture) info.setBreakingTextureStage(player, world, -1) - - return setState + fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { + val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) + val correctMaterial = info.context.checkedState.block == entity.stack.item.block + return inRange && correctMaterial } + /** + * @return if the [newState] matches the [targetState]. + * + * @see TargetState + */ + fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = + if (targetState.matches(newState, pos, world)) true + else { + this@BreakManager.warn("Break at ${pos.toShortString()} was rejected with $newState instead of $targetState") + false + } + override fun preEvent(): Event = UpdateManagerEvent.Break().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt new file mode 100644 index 000000000..471d8e0e9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +import com.lambda.Lambda.mc +import com.lambda.context.SafeContext +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.EntityEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe +import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode +import com.lambda.interaction.request.breaking.BreakManager.matchesBlockItem +import com.lambda.interaction.request.breaking.BreakManager.matchesTargetState +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.fluidState +import com.lambda.util.BlockUtils.matches +import com.lambda.util.Communication.info +import com.lambda.util.collections.LimitedDecayQueue +import com.lambda.util.player.gamemode +import net.minecraft.block.OperatorBlock +import net.minecraft.entity.ItemEntity +import net.minecraft.util.math.ChunkSectionPos + +object BrokenBlockHandler { + val pendingBreaks = LimitedDecayQueue( + TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L + ) { info -> + mc.world?.let { world -> + val pos = info.context.expectedPos + val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)) + if (!loaded) return@let + + info("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") + + val awaitThenBreak = info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak + if (!info.broken && awaitThenBreak) { + world.setBlockState(info.context.expectedPos, info.context.checkedState) + } + } + info.pendingInteractions.remove(info.context) + } + + init { + listen(priority = Int.MIN_VALUE + 1) { event -> + pendingBreaks + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { pending -> + // return if the state hasn't changed + if (event.newState.matches(pending.context.checkedState)) + return@listen + + // return if the block's not broken + if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) { + pending.stopPending() + return@listen + } + + if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { + destroyBlock(pending) + } + pending.internalOnBreak() + if (pending.callbacksCompleted) { + pending.stopPending() + } + return@listen + } + } + + listen(priority = Int.MIN_VALUE + 1) { + if (it.entity !is ItemEntity) return@listen + pendingBreaks + .firstOrNull { info -> matchesBlockItem(info, it.entity) } + ?.let { pending -> + pending.internalOnItemDrop(it.entity) + if (pending.callbacksCompleted) { + pending.stopPending() + } + return@listen + } + } + + listenUnsafe(priority = Int.MIN_VALUE + 1) { + pendingBreaks.clear() + } + } + + /** + * Adds the [info] to the break manager, and requesters, pending interaction collections. + */ + fun BreakInfo.startPending() { + pendingBreaks.add(this) + pendingInteractions.add(context) + } + + /** + * Removes the [info] from the break manager, and requesters, pending interation collections. + */ + private fun BreakInfo.stopPending() { + pendingBreaks.remove(this) + pendingInteractions.remove(context) + } + + fun setPendingConfigs(request: BreakRequest) { + pendingBreaks.setSizeLimit(request.build.breaking.maxPendingBreaks) + pendingBreaks.setDecayTime(request.build.interactionTimeout * 50L) + } + + /** + * A modified version of the minecraft breakBlock method. + * + * Performs the actions required to display break particles, sounds, texture overlay, etc. + * based on the users settings. + * + * @return if the blocks state was set or not. + * + * @see net.minecraft.client.world.ClientWorld.breakBlock + */ + fun SafeContext.destroyBlock(info: BreakInfo): Boolean { + val ctx = info.context + + if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false + + if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) + return false + val block = ctx.checkedState.block + if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false + if (ctx.checkedState.isAir) return false + + block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) + val fluidState = fluidState(ctx.expectedPos) + val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) + if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) + + if (info.breakConfig.breakingTexture) info.setBreakingTextureStage(player, world, -1) + + return setState + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index a81edc91d..38537719a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -28,7 +28,6 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap -import com.lambda.interaction.request.hotbar.HotbarManager.preEvent import com.lambda.mixin.entity.PlayerInventoryMixin import com.lambda.mixin.render.InGameHudMixin import com.lambda.threading.runSafe @@ -41,8 +40,7 @@ import com.lambda.threading.runSafe */ object HotbarManager : RequestHandler( *TickStage.entries.toTypedArray(), - postClose = { checkResetSwap() }, - onOpen = { preEvent() } + onClose = { checkResetSwap() } ), Loadable { val serverSlot get() = runSafe { interaction.lastSelectedSlot diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 2cc6b920b..107084fb8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -38,7 +38,6 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.placing.PlaceManager.activeRequest -import com.lambda.interaction.request.placing.PlaceManager.preEvent import com.lambda.interaction.request.placing.PlaceManager.processRequest import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.blockState @@ -69,8 +68,7 @@ import net.minecraft.world.GameMode object PlaceManager : RequestHandler( *TickStage.entries.toTypedArray(), - preOpen = { activeRequest?.let { processRequest(it) } }, - onOpen = { preEvent() } + onOpen = { activeRequest?.let { processRequest(it) } } ), PositionBlocking { private val pendingPlacements = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 1b46a5dcb..d985e258c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -33,7 +33,6 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotation.Rotation.Companion.slerp -import com.lambda.interaction.request.rotation.RotationManager.preEvent import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.modules.client.Baritone import com.lambda.threading.runGameScheduled @@ -51,8 +50,7 @@ import kotlin.math.sign import kotlin.math.sin object RotationManager : RequestHandler( - TickStage.TickStart, - preOpen = { preEvent() } + TickStage.TickStart ), Loadable { var currentRotation = Rotation.ZERO private var prevRotation = Rotation.ZERO From 9f6c8dd25feaf9dddef00216a9e392bcfdc19ad8 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 15 Apr 2025 15:17:13 +0100 Subject: [PATCH 131/364] more BreakManager documentation --- .../request/breaking/BreakManager.kt | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index a2a739dd1..43ee2d456 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -151,6 +151,12 @@ object BreakManager : RequestHandler( } } + /** + * Attempts to accept and process the request, if there is not already an [activeRequest]. + * If the request is processed and all breaks completed, the [activeRequest] is cleared. + * + * @see processRequest + */ override fun SafeContext.handleRequest(request: BreakRequest) { if (activeRequest != null || PlaceManager.activeThisTick) return @@ -164,6 +170,16 @@ object BreakManager : RequestHandler( } } + /** + * If the request is fresh, local variables are populated through the [processRequest] method. + * It then attempts to perform as many breaks within this tick as possible from the [instantBreaks] collection. + * The [breakInfos] are then updated if the dependencies are present, E.G. if the user has rotations enabled, + * or the player needs to swap to a different hotbar slot. + * + * @see performInstantBreaks + * @see processNewBreaks + * @see updateBreakProgress + */ private fun SafeContext.processRequest(request: BreakRequest) { pendingBreaks.cleanUp() @@ -194,6 +210,15 @@ object BreakManager : RequestHandler( } } + /** + * Filters and sorts the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches + * in positions. If a match is found, the [BreakInfo] is updated with the new context. Otherwise, the break is cancelled. + * The [instantBreaks] and [breaks] collections are then populated with the new appropriate contexts, and the [maxBreaksThisTick] + * value is set. + * + * @see canAccept + * @see cancelBreak + */ private fun SafeContext.populateFrom(request: BreakRequest) { // Sanitize and sort the new breaks val newBreaks = request.contexts @@ -404,7 +429,7 @@ object BreakManager : RequestHandler( } /** - * Nullifies the break. If the block is not broken, the [BreakInfo.internalOnBreak] callback gets triggered + * Nullifies the break. If the block is not broken, the [BreakInfo.internalOnCancel] callback gets triggered */ private fun BreakInfo.nullify() { type.nullify() From 064b78ff08ac7656f07d5eb8a00196bbced0fb5e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 15 Apr 2025 15:21:25 +0100 Subject: [PATCH 132/364] BrokenBlockHandler documentation --- .../request/breaking/BrokenBlockHandler.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 471d8e0e9..f7739ec9c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -37,6 +37,12 @@ import net.minecraft.block.OperatorBlock import net.minecraft.entity.ItemEntity import net.minecraft.util.math.ChunkSectionPos +/** + * This object is designed to handle blocks that have been broken client side, yet are awaiting + * confirmation from the server, and / or an item drop. + * + * @see BreakManager + */ object BrokenBlockHandler { val pendingBreaks = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L @@ -109,13 +115,17 @@ object BrokenBlockHandler { } /** - * Removes the [info] from the break manager, and requesters, pending interation collections. + * Removes the [info] from the break manager, and requesters, pending interaction collections. */ private fun BreakInfo.stopPending() { pendingBreaks.remove(this) pendingInteractions.remove(context) } + /** + * Sets the size limit and decay time for the [pendingBreaks] [LimitedDecayQueue] + * using the [request]'s configs + */ fun setPendingConfigs(request: BreakRequest) { pendingBreaks.setSizeLimit(request.build.breaking.maxPendingBreaks) pendingBreaks.setDecayTime(request.build.interactionTimeout * 50L) From 3b9c161424b11a2cfe25855135f303e6bf4a8bc4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 15 Apr 2025 20:10:53 +0100 Subject: [PATCH 133/364] PlacedBlockHandler --- .../interaction/request/placing/PlaceInfo.kt | 28 ++++++ .../request/placing/PlaceManager.kt | 72 +-------------- .../request/placing/PlacedBlockHandler.kt | 87 +++++++++++++++++++ 3 files changed, 119 insertions(+), 68 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt new file mode 100644 index 000000000..78daf953b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.placing + +import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.construction.context.PlaceContext + +data class PlaceInfo( + val context: PlaceContext, + val onPlace: () -> Unit, + val pendingInteractionsList: MutableCollection, + val placeConfig: PlaceConfig +) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 107084fb8..243f28239 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -17,21 +17,15 @@ package com.lambda.interaction.request.placing -import com.lambda.Lambda.mc import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.event.Event import com.lambda.event.EventFlow.post -import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent -import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe -import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler @@ -39,12 +33,10 @@ import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.placing.PlaceManager.activeRequest import com.lambda.interaction.request.placing.PlaceManager.processRequest -import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.interaction.request.placing.PlacedBlockHandler.addPendingPlace +import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements import com.lambda.util.BlockUtils.blockState -import com.lambda.util.BlockUtils.item -import com.lambda.util.Communication.info import com.lambda.util.Communication.warn -import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.player.gamemode import com.lambda.util.player.isItemOnCooldown import com.lambda.util.player.swingHand @@ -70,21 +62,12 @@ object PlaceManager : RequestHandler( *TickStage.entries.toTypedArray(), onOpen = { activeRequest?.let { processRequest(it) } } ), PositionBlocking { - private val pendingPlacements = LimitedDecayQueue( - TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L - ) { - info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") - mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) - it.pendingInteractionsList.remove(it.context) - } - private var activeRequest: PlaceRequest? = null + private var potentialPlacements = mutableListOf() private var placementsThisTick = 0 private var maxPlacementsThisTick = 0 - private var potentialPlacements = mutableListOf() - private var shouldSneak = false private val validSneak: (player: ClientPlayerEntity) -> Boolean = { player -> shouldSneak == player.isSneaking } @@ -112,29 +95,6 @@ object PlaceManager : RequestHandler( it.input.sneaking = true } } - - listen(priority = Int.MIN_VALUE) { event -> - pendingPlacements - .firstOrNull { it.context.expectedPos == event.pos } - ?.let { info -> - removePendingPlace(info) - - // return if the block wasn't placed - if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) - return@listen - - if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) - with (info.context) { - placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos) - } - info.onPlace() - return@listen - } - } - - listenUnsafe { - pendingPlacements.clear() - } } override fun SafeContext.handleRequest(request: PlaceRequest) { @@ -198,13 +158,6 @@ object PlaceManager : RequestHandler( pending.context.expectedPos == placeContext.expectedPos } - private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = - if (targetState.matches(newState, pos, world)) true - else { - this@PlaceManager.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState") - false - } - private fun SafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() val hitResult = placeContext.result @@ -342,7 +295,7 @@ object PlaceManager : RequestHandler( PlayerInteractBlockC2SPacket(hand, hitResult, sequence) } - private fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) { + fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) { val blockSoundGroup = state.soundGroup world.playSound( player, @@ -364,22 +317,5 @@ object PlaceManager : RequestHandler( ) } - private fun addPendingPlace(info: PlaceInfo) { - pendingPlacements.add(info) - info.pendingInteractionsList.add(info.context) - } - - private fun removePendingPlace(info: PlaceInfo) { - pendingPlacements.remove(info) - info.pendingInteractionsList.remove(info.context) - } - - private data class PlaceInfo( - val context: PlaceContext, - val onPlace: () -> Unit, - val pendingInteractionsList: MutableCollection, - val placeConfig: PlaceConfig - ) - override fun preEvent(): Event = UpdateManagerEvent.Place().post() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt new file mode 100644 index 000000000..5e8f1b871 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.placing + +import com.lambda.Lambda.mc +import com.lambda.context.SafeContext +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.placing.PlaceManager.placeSound +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.item +import com.lambda.util.Communication.info +import com.lambda.util.Communication.warn +import com.lambda.util.collections.LimitedDecayQueue +import net.minecraft.block.BlockState +import net.minecraft.item.BlockItem +import net.minecraft.util.math.BlockPos + +object PlacedBlockHandler { + val pendingPlacements = LimitedDecayQueue( + TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L + ) { + info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") + mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + it.pendingInteractionsList.remove(it.context) + } + + init { + listen(priority = Int.MIN_VALUE) { event -> + pendingPlacements + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { info -> + removePendingPlace(info) + + // return if the block wasn't placed + if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) + return@listen + + if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) + with (info.context) { + placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos) + } + info.onPlace() + return@listen + } + } + + listenUnsafe { + pendingPlacements.clear() + } + } + + fun addPendingPlace(info: PlaceInfo) { + pendingPlacements.add(info) + info.pendingInteractionsList.add(info.context) + } + + private fun removePendingPlace(info: PlaceInfo) { + pendingPlacements.remove(info) + info.pendingInteractionsList.remove(info.context) + } + + private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = + if (targetState.matches(newState, pos, world)) true + else { + this@PlacedBlockHandler.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState") + false + } +} \ No newline at end of file From ac690c238c1637090d14f94632df58603e79fd18 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 15 Apr 2025 20:59:41 +0100 Subject: [PATCH 134/364] place manager documentation --- .../request/breaking/BrokenBlockHandler.kt | 4 +- .../request/placing/PlaceManager.kt | 59 ++++++++++++++++++- .../request/placing/PlacedBlockHandler.kt | 19 ++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index f7739ec9c..92a465246 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -107,7 +107,7 @@ object BrokenBlockHandler { } /** - * Adds the [info] to the break manager, and requesters, pending interaction collections. + * Adds the [info] to the [BrokenBlockHandler], and requesters, pending interaction collections. */ fun BreakInfo.startPending() { pendingBreaks.add(this) @@ -115,7 +115,7 @@ object BrokenBlockHandler { } /** - * Removes the [info] from the break manager, and requesters, pending interaction collections. + * Removes the [info] from the [BrokenBlockHandler], and requesters, pending interaction collections. */ private fun BreakInfo.stopPending() { pendingBreaks.remove(this) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 243f28239..b6b9341b6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -35,6 +35,7 @@ import com.lambda.interaction.request.placing.PlaceManager.activeRequest import com.lambda.interaction.request.placing.PlaceManager.processRequest import com.lambda.interaction.request.placing.PlacedBlockHandler.addPendingPlace import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements +import com.lambda.interaction.request.placing.PlacedBlockHandler.setPendingConfigs import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.warn import com.lambda.util.player.gamemode @@ -97,6 +98,12 @@ object PlaceManager : RequestHandler( } } + /** + * accepts, and processes the request, as long as the current [activeRequest] is null, and the [BreakManager] has not + * been active this tick. + * + * @see processRequest + */ override fun SafeContext.handleRequest(request: PlaceRequest) { if (activeRequest != null || BreakManager.activeThisTick) return @@ -105,6 +112,16 @@ object PlaceManager : RequestHandler( if (placementsThisTick > 0) activeThisTick = true } + /** + * If the request is fresh, local variables are populated through the [processRequest] method. + * It then attempts to perform as many placements within this tick as possible from the [potentialPlacements] collection. + * + * If all the [maxPlacementsThisTick] limit is reached and the user has rotations enabled, it will start rotating to + * the next predicted placement in the list for optimal speed. + * + * @see populateFrom + * @see placeBlock + */ fun SafeContext.processRequest(request: PlaceRequest) { pendingPlacements.cleanUp() @@ -136,12 +153,16 @@ object PlaceManager : RequestHandler( } } + /** + * Filters and sorts the [request]'s [PlaceContext]s, placing them into the [potentialPlacements] collection, and + * setting the maxPlacementsThisTick value. + * + * @see canPlace + */ private fun SafeContext.populateFrom(request: PlaceRequest) { val place = request.build.placing - pendingPlacements.setSizeLimit(place.maxPendingPlacements) - pendingPlacements.setDecayTime(request.build.interactionTimeout * 50L) - + setPendingConfigs(request) potentialPlacements = request.contexts .filter { canPlace(it) } .sortedWith( @@ -153,11 +174,19 @@ object PlaceManager : RequestHandler( maxPlacementsThisTick = (place.placementsPerTick.coerceAtMost(pendingLimit)) } + /** + * @return if none of the [pendingPlacements] match positions with the [placeContext] + */ private fun canPlace(placeContext: PlaceContext) = pendingPlacements.none { pending -> pending.context.expectedPos == placeContext.expectedPos } + /** + * A modified version of the minecraft interactBlock method, renamed to better suit its usage. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.interactBlock + */ private fun SafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() val hitResult = placeContext.result @@ -166,6 +195,11 @@ object PlaceManager : RequestHandler( return interactBlockInternal(placeContext, request, request.build.placing, hand, hitResult) } + /** + * A modified version of the minecraft interactBlockInternal method. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.interactBlockInternal + */ private fun SafeContext.interactBlockInternal( placeContext: PlaceContext, request: PlaceRequest, @@ -203,6 +237,11 @@ object PlaceManager : RequestHandler( return ActionResult.PASS } + /** + * A modified version of the minecraft useOnBlock method. + * + * @see net.minecraft.item.Item.useOnBlock + */ private fun SafeContext.useOnBlock( placeContext: PlaceContext, request: PlaceRequest, @@ -223,6 +262,11 @@ object PlaceManager : RequestHandler( return place(placeContext, request, hand, hitResult, placeConfig, item, ItemPlacementContext(context)) } + /** + * A modified version of the minecraft place method. + * + * @see net.minecraft.item.BlockItem.place + */ private fun SafeContext.place( placeContext: PlaceContext, request: PlaceRequest, @@ -290,11 +334,17 @@ object PlaceManager : RequestHandler( return ActionResult.success(world.isClient) } + /** + * sends the block placement packet using the given [hand] and [hitResult]. + */ private fun SafeContext.sendPlacePacket(hand: Hand, hitResult: BlockHitResult) = interaction.sendSequencedPacket(world) { sequence: Int -> PlayerInteractBlockC2SPacket(hand, hitResult, sequence) } + /** + * Plays the block placement sound at a given position. + */ fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) { val blockSoundGroup = state.soundGroup world.playSound( @@ -307,6 +357,9 @@ object PlaceManager : RequestHandler( ) } + /** + * Must be called before and after placing a block to bypass grim's air place checks. + */ private fun SafeContext.airPlaceOffhandSwap() { connection.sendPacket( PlayerActionC2SPacket( diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt index 5e8f1b871..1111cd27f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -68,16 +68,35 @@ object PlacedBlockHandler { } } + /** + * Adds the info to the [PlacedBlockHandler], and requesters, pending interaction collections. + */ fun addPendingPlace(info: PlaceInfo) { pendingPlacements.add(info) info.pendingInteractionsList.add(info.context) } + /** + * Removes the info from the [PlacedBlockHandler], and requesters, pending interaction collections. + */ private fun removePendingPlace(info: PlaceInfo) { pendingPlacements.remove(info) info.pendingInteractionsList.remove(info.context) } + /** + * Sets the size limit and decay time for the [pendingPlacements] using the [request]'s configs + */ + fun setPendingConfigs(request: PlaceRequest) { + pendingPlacements.setSizeLimit(request.build.placing.maxPendingPlacements) + pendingPlacements.setDecayTime(request.build.interactionTimeout * 50L) + } + + /** + * @return if the [targetState] matches the [newState] + * + * @see TargetState + */ private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = if (targetState.matches(newState, pos, world)) true else { From f2773ef375f34d59d8802808afb92f1a008187a3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 17 Apr 2025 14:59:29 +0100 Subject: [PATCH 135/364] inlined needless shortened break config reference --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 43ee2d456..04a44f82d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -183,10 +183,9 @@ object BreakManager : RequestHandler( private fun SafeContext.processRequest(request: BreakRequest) { pendingBreaks.cleanUp() - val breakConfig = request.build.breaking if (request.fresh) populateFrom(request) - if (!atMaxBreakInfos(breakConfig)) run processNewBreaks@ { + if (!atMaxBreakInfos(request.build.breaking)) run processNewBreaks@ { if (!performInstantBreaks(request)) return@processNewBreaks processNewBreaks(request) } From e5ac7867f135341b8d126c54c1d22476b3eb0b88 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 18 Apr 2025 00:17:05 +0200 Subject: [PATCH 136/364] Fix merge issues --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- .../lambda/interaction/request/breaking/BrokenBlockHandler.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 04a44f82d..d38c27ff4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -136,7 +136,7 @@ object BreakManager : RequestHandler( } // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work - listen(priority = Int.MIN_VALUE + 1) { + listen(priority = Int.MIN_VALUE + 1) { if (it.entity !is ItemEntity) return@listen breakInfos diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 92a465246..389e6ca48 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -88,7 +88,7 @@ object BrokenBlockHandler { } } - listen(priority = Int.MIN_VALUE + 1) { + listen(priority = Int.MIN_VALUE + 1) { if (it.entity !is ItemEntity) return@listen pendingBreaks .firstOrNull { info -> matchesBlockItem(info, it.entity) } From 7b99509d38c66c8923ca78a896b99a406314199a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 18 Apr 2025 00:02:24 +0100 Subject: [PATCH 137/364] rotations fix for placements --- .../com/lambda/interaction/request/rotation/RotationManager.kt | 1 + .../interaction/request/rotation/visibilty/RotationTargets.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index d985e258c..c42054b33 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -53,6 +53,7 @@ object RotationManager : RequestHandler( TickStage.TickStart ), Loadable { var currentRotation = Rotation.ZERO + val nextRotation get() = activeRequest?.target?.targetRotation?.value ?: currentRotation private var prevRotation = Rotation.ZERO private var changedThisTick = false diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index 01c7eaaba..477646f45 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -47,7 +47,7 @@ annotation class RotationDsl @RotationDsl fun lookAt(angle: Rotation, maxAngleDistance: Double = 10.0) = RotationTarget(null, { - RotationManager.currentRotation dist angle < maxAngleDistance + RotationManager.nextRotation dist angle < maxAngleDistance }) { angle } /** From aa57f9bcefae08386e1469850671896d668c70bd Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 18 Apr 2025 01:37:53 +0200 Subject: [PATCH 138/364] Remove "smart" pathing for now --- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index c83eeacd4..35a562772 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -98,26 +98,12 @@ class BuildTask @Ta5kBuilder constructor( if (collectDrops()) return@listen val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) - val resultPostions = results.map { it.blockPos } - val sim = blueprint.simulation(interact, rotation, inventory, build) - BlockPos.iterateOutwards(player.blockPos, 2, 2, 2).forEach { - sim.simulate(it.toFastVec()) - } - val bestPos = sim.goodPositions() - .filter { it.pos !in resultPostions } - .maxByOrNull { it.interactions } - - val drawables = results + TaskFlowModule.drawables = results .filterIsInstance() .plus(pendingInteractions.toList()) .toMutableList() - if (bestPos != null && build.pathing) { - drawables.add(bestPos) - } - TaskFlowModule.drawables = drawables - val resultsNotBlocked = results .filter { result -> pendingInteractions.none { it.expectedPos == result.blockPos } } .sorted() @@ -142,6 +128,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { if (!build.pathing) return@listen + val sim = blueprint.simulation(interact, rotation, inventory, build) val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) } @@ -151,10 +138,6 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { - bestPos?.let { - if (build.pathing) BaritoneUtils.setGoalAndPath(GoalNear(it.pos, 1)) - } - if (atMaxPendingInteractions) return@listen when (bestResult) { is BreakResult.Break -> { From ada6fa28996a3a91011a311cdc4203dac2b99279 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 18 Apr 2025 01:09:30 +0100 Subject: [PATCH 139/364] Revert "rotations fix for placements" This reverts commit 7b99509d38c66c8923ca78a896b99a406314199a. --- .../com/lambda/interaction/request/rotation/RotationManager.kt | 1 - .../interaction/request/rotation/visibilty/RotationTargets.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index c42054b33..d985e258c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -53,7 +53,6 @@ object RotationManager : RequestHandler( TickStage.TickStart ), Loadable { var currentRotation = Rotation.ZERO - val nextRotation get() = activeRequest?.target?.targetRotation?.value ?: currentRotation private var prevRotation = Rotation.ZERO private var changedThisTick = false diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index 477646f45..01c7eaaba 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -47,7 +47,7 @@ annotation class RotationDsl @RotationDsl fun lookAt(angle: Rotation, maxAngleDistance: Double = 10.0) = RotationTarget(null, { - RotationManager.nextRotation dist angle < maxAngleDistance + RotationManager.currentRotation dist angle < maxAngleDistance }) { angle } /** From d7cd202046a5e4abdb6994876c020ee9402946d0 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 18 Apr 2025 14:22:59 +0100 Subject: [PATCH 140/364] use receive instead of send for PlayerPositionLookS2CPacket reset --- .../com/lambda/interaction/request/rotation/RotationManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index d985e258c..1b6443e16 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -76,7 +76,7 @@ object RotationManager : RequestHandler( changedThisTick = false } - listen { event -> + listen { event -> val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listen From c59b824d52caca6727b994d473a6480bd64ec292 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 18 Apr 2025 17:34:08 +0100 Subject: [PATCH 141/364] fix small BreakManager oversight --- .../request/breaking/BreakManager.kt | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d38c27ff4..2f2257751 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -158,16 +158,10 @@ object BreakManager : RequestHandler( * @see processRequest */ override fun SafeContext.handleRequest(request: BreakRequest) { - if (activeRequest != null || PlaceManager.activeThisTick) return + if (activeRequest != null || PlaceManager.activeThisTick || request.contexts.isEmpty()) return activeRequest = request processRequest(request) - if (instantBreaks.isNotEmpty() || breaks.isNotEmpty()) { - activeRequest = null - } - if (breaksThisTick > 0 || breakInfos.any { it != null && !it.isRedundant }) { - activeThisTick = true - } } /** @@ -192,21 +186,30 @@ object BreakManager : RequestHandler( // Reversed so that the breaking order feels natural to the user as the primary break is always the // last break to be started - breakInfos - .filterNotNull() - .filter { !it.isRedundant } - .also { infos -> - infos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> - info.request.rotation.request(info.context.rotation) + run breakInfos@ { + breakInfos + .filterNotNull() + .filter { !it.isRedundant } + .also { infos -> + infos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> + info.request.rotation.request(info.context.rotation) + } } - } - .reversed() - .forEach { info -> - if (info.updatedProgressThisTick) return@forEach - if (!info.context.requestDependencies(request)) return - if (!rotated || tickStage !in info.breakConfig.breakStageMask) return - updateBreakProgress(info) - } + .reversed() + .forEach { info -> + if (info.updatedProgressThisTick) return@forEach + if (!info.context.requestDependencies(request)) return@breakInfos + if (!rotated || tickStage !in info.breakConfig.breakStageMask) return@breakInfos + updateBreakProgress(info) + } + } + + if (instantBreaks.isEmpty() && breaks.isEmpty()) { + activeRequest = null + } + if (breaksThisTick > 0 || breakInfos.any { it != null && !it.isRedundant }) { + activeThisTick = true + } } /** From b267a79d0c10edcea843425a1157211a55675fc6 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 18 Apr 2025 20:13:33 +0100 Subject: [PATCH 142/364] rotations fix!!! --- .../baritone/MixinBaritonePlayerContext.java | 2 +- .../lambda/interaction/PlayerPacketManager.kt | 7 ++- .../construction/simulation/BuildSimulator.kt | 2 +- .../request/placing/PlaceManager.kt | 11 +--- .../request/rotation/RotationManager.kt | 63 ++++++++++--------- .../rotation/visibilty/PointSelection.kt | 2 +- .../rotation/visibilty/RequestedHit.kt | 4 +- .../rotation/visibilty/RotationTarget.kt | 2 +- .../rotation/visibilty/RotationTargets.kt | 2 +- .../rotation/visibilty/VisibilityChecker.kt | 5 +- .../module/modules/combat/CrystalAura.kt | 14 ++++- .../lambda/module/modules/combat/KillAura.kt | 6 +- .../lambda/module/modules/player/Scaffold.kt | 14 ++--- 13 files changed, 71 insertions(+), 63 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java index 0c81c8144..0190fe979 100644 --- a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java +++ b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java @@ -42,7 +42,7 @@ void syncRotationWithBaritone(CallbackInfoReturnable cir) { RotationManager rm = RotationManager.INSTANCE; cir.setReturnValue(new Rotation( - (float) rm.getCurrentRotation().getYaw(), (float) rm.getCurrentRotation().getPitch()) + (float) rm.getActiveRotation().getYaw(), (float) rm.getActiveRotation().getPitch()) ); } } diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index 11ccdbed8..d4736b1e4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -32,7 +32,10 @@ import com.lambda.util.math.component3 import com.lambda.util.player.MovementUtils.motionX import com.lambda.util.player.MovementUtils.motionZ import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket -import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.* +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.Full +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.LookAndOnGround +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.OnGroundOnly +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.PositionAndOnGround import net.minecraft.util.math.Vec3d object PlayerPacketManager { @@ -51,7 +54,7 @@ object PlayerPacketManager { runSafe { PlayerPacketEvent.Pre( player.pos, - RotationManager.currentRotation, + RotationManager.activeRotation, player.isOnGround, player.isSprinting, player.isSneaking diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index a2ea5f22a..3f97f3ffc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -434,7 +434,7 @@ object BuildSimulator { return acc } - val currentRotation = RotationManager.currentRotation + val currentRotation = RotationManager.activeRotation val currentCast = currentRotation.rayCast(interact.interactReach, eye) val voxelShape = state.getOutlineShape(world, pos) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index b6b9341b6..85df2368b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -141,16 +141,7 @@ object PlaceManager : RequestHandler( placementsThisTick++ iterator.remove() } - if (potentialPlacements.isEmpty()) { - activeRequest = null - return - } - if (!request.rotation.rotate) return - - // In case you cant rotate and place within the same tick - potentialPlacements.getOrNull(maxPlacementsThisTick - 1)?.let { nextPredictedPlacement -> - request.rotation.request(nextPredictedPlacement.rotation) - } + if (potentialPlacements.isEmpty()) activeRequest = null } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 1b6443e16..820bb90d2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -52,8 +52,9 @@ import kotlin.math.sin object RotationManager : RequestHandler( TickStage.TickStart ), Loadable { - var currentRotation = Rotation.ZERO - private var prevRotation = Rotation.ZERO + var activeRotation = Rotation.ZERO; private set + var serverRotation = Rotation.ZERO; private set + private var prevServerRotation = Rotation.ZERO private var changedThisTick = false private var activeRequest: RotationRequest? = null @@ -94,6 +95,7 @@ object RotationManager : RequestHandler( activeRequest?.let { if (it.age <= 0) return } if (request.target.targetRotation.value != null) { activeRequest = request + updateActiveRotation() changedThisTick = true } } @@ -104,29 +106,17 @@ object RotationManager : RequestHandler( if (!changedThisTick) { // rebuild the rotation if the same context gets used again activeRequest?.target?.targetRotation?.update() + updateActiveRotation() } - // Calculate the target rotation - val targetRotation = activeRequest?.let { request -> - val rotationTo = if (request.keepTicks >= 0) - request.target.targetRotation.value - ?: currentRotation // same context gets used again && the rotation is null this tick - else player.rotation - - val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier - val turnSpeed = request.turnSpeed() * speedMultiplier - - currentRotation.slerp(rotationTo, turnSpeed) - } ?: player.rotation - // Update the current rotation - prevRotation = currentRotation - currentRotation = targetRotation/*.fixSensitivity(prevRotation)*/ + prevServerRotation = serverRotation + serverRotation = activeRotation/*.fixSensitivity(prevServerRotation)*/ // Handle LOCK mode if (activeRequest?.mode == RotationMode.Lock) { - player.yaw = currentRotation.yawF - player.pitch = currentRotation.pitchF + player.yaw = serverRotation.yawF + player.pitch = serverRotation.pitchF } // Tick and reset the context @@ -137,15 +127,30 @@ object RotationManager : RequestHandler( } } + private fun SafeContext.updateActiveRotation() { + activeRotation = activeRequest?.let { request -> + val rotationTo = if (request.keepTicks >= 0) + request.target.targetRotation.value + ?: activeRotation // same context gets used again && the rotation is null this tick + else player.rotation + + val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier + val turnSpeed = request.turnSpeed() * speedMultiplier + + serverRotation.slerp(rotationTo, turnSpeed) + } ?: player.rotation + } + private fun reset(rotation: Rotation) { - prevRotation = rotation - currentRotation = rotation + prevServerRotation = rotation + serverRotation = rotation + activeRotation = rotation activeRequest = null } private val smoothRotation get() = - lerp(Lambda.mc.partialTicks, prevRotation, currentRotation) + lerp(Lambda.mc.partialTicks, prevServerRotation, serverRotation) @JvmStatic val lockRotation @@ -165,32 +170,32 @@ object RotationManager : RequestHandler( @JvmStatic val handYaw get() = - if (activeRequest?.mode == RotationMode.Lock) currentRotation.yaw.toFloat() else null + if (activeRequest?.mode == RotationMode.Lock) serverRotation.yaw.toFloat() else null @JvmStatic val handPitch get() = - if (activeRequest?.mode == RotationMode.Lock) currentRotation.pitch.toFloat() else null + if (activeRequest?.mode == RotationMode.Lock) serverRotation.pitch.toFloat() else null @JvmStatic val movementYaw: Float? get() { if (activeRequest?.mode == RotationMode.Silent) return null - return currentRotation.yaw.toFloat() + return serverRotation.yaw.toFloat() } @JvmStatic val movementPitch: Float? get() { if (activeRequest?.mode == RotationMode.Silent) return null - return currentRotation.pitch.toFloat() + return serverRotation.pitch.toFloat() } @JvmStatic fun getRotationForVector(deltaTime: Double): Vec2d? { if (activeRequest?.mode == RotationMode.Silent) return null - val rot = lerp(deltaTime, prevRotation, currentRotation) + val rot = lerp(deltaTime, prevServerRotation, serverRotation) return Vec2d(rot.yaw, rot.pitch) } @@ -234,7 +239,7 @@ object RotationManager : RequestHandler( if (signForward == 0f && signStrafe == 0f) return@runSafe // Actual yaw used by the physics engine - var actualYaw = currentRotation.yaw + var actualYaw = serverRotation.yaw if (activeRequest?.mode == RotationMode.Silent) { actualYaw = player.yaw.toDouble() @@ -262,7 +267,7 @@ object RotationManager : RequestHandler( // Makes baritone movement safe // when yaw difference is too big to compensate it by modifying keyboard input val minYawDist = movementYawList - .map { currentRotation.yaw + it } // all possible movement directions (including diagonals) + .map { serverRotation.yaw + it } // all possible movement directions (including diagonals) .minOf { Rotation.angleDifference(it, baritoneYaw) } if (minYawDist > 5.0) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt index 761d7fda4..5abd3d302 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt @@ -24,7 +24,7 @@ import com.lambda.util.math.distSq enum class PointSelection(val select: (MutableList) -> VisibilityChecker.CheckedHit?) { ByRotation({ hits -> hits.minByOrNull { - RotationManager.currentRotation dist it.targetRotation + RotationManager.activeRotation dist it.targetRotation } }), Optimum( optimum@ { hits -> diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt index a0158d23b..8cf300e29 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt @@ -60,7 +60,7 @@ abstract class RequestedHit { * * @return True if the hit is valid, false otherwise. */ - fun verifyRotation(rotation: Rotation = RotationManager.currentRotation) = + fun verifyRotation(rotation: Rotation = RotationManager.activeRotation) = rotation.rayCast(reach)?.let { verifyHit(it) } ?: false /** @@ -69,7 +69,7 @@ abstract class RequestedHit { * @return [HitResult] if passed, null otherwise. */ fun hitIfValid() = - RotationManager.currentRotation.rayCast(reach)?.let { + RotationManager.activeRotation.rayCast(reach)?.let { if (!verifyHit(it)) null else it } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index 37a82c170..42772bc9a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -43,7 +43,7 @@ data class RotationTarget( } val angleDistance get() = runSafe { - targetRotation.value?.dist(RotationManager.currentRotation) + targetRotation.value?.dist(RotationManager.activeRotation) } ?: 1000.0 /** diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index 01c7eaaba..e21fc7ae7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -47,7 +47,7 @@ annotation class RotationDsl @RotationDsl fun lookAt(angle: Rotation, maxAngleDistance: Double = 10.0) = RotationTarget(null, { - RotationManager.currentRotation dist angle < maxAngleDistance + RotationManager.activeRotation dist angle < maxAngleDistance }) { angle } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index 37e258f04..6c3ed439a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -21,8 +21,9 @@ import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan -import com.lambda.interaction.request.rotation.* +import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.RotationManager import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.extension.component6 import com.lambda.util.math.distSq @@ -63,7 +64,7 @@ object VisibilityChecker { interaction: InteractionConfig, verify: CheckedHit.() -> Boolean ): CheckedHit? { - val currentRotation = RotationManager.currentRotation + val currentRotation = RotationManager.activeRotation if (boxes.any { it.contains(eye) }) { currentRotation.rayCast(reach, eye)?.let { hit -> diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 01c626dcc..a39158be7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -44,9 +44,14 @@ import com.lambda.util.PacketUtils.sendPacket import com.lambda.util.Timer import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.combat.CombatUtils.crystalDamage -import com.lambda.util.math.* import com.lambda.util.math.MathUtils.ceilToInt import com.lambda.util.math.MathUtils.roundToStep +import com.lambda.util.math.Vec2d +import com.lambda.util.math.distSq +import com.lambda.util.math.flooredBlockPos +import com.lambda.util.math.getHitVec +import com.lambda.util.math.minus +import com.lambda.util.math.plus import com.lambda.util.world.fastEntitySearch import net.minecraft.block.Blocks import net.minecraft.entity.Entity @@ -56,7 +61,10 @@ import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.math.* +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d import kotlin.concurrent.fixedRateTimer import kotlin.math.max import kotlin.time.Duration.Companion.milliseconds @@ -456,7 +464,7 @@ object CrystalAura : Module( if (side.axis != Direction.Axis.Y) vec += Vec3d(0.0, 0.45, 0.0) player.eyePos.rotationTo(vec) - } ?: RotationManager.currentRotation + } ?: RotationManager.activeRotation } /** diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index f8beb58ed..27b5ae32f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -133,7 +133,7 @@ object KillAura : Module( } /*private fun SafeContext.buildRotation(target: LivingEntity) { - val currentRotation = RotationManager.currentRotation + val serverRotation = RotationManager.serverRotation val prediction = buildPlayerPrediction() @@ -160,7 +160,7 @@ object KillAura : Module( // Rotation stabilizer speedMultiplier = if (stabilize && !rotation.instant) { - val slowDown = currentRotation.castBox(box, reach, eye) != null + val slowDown = serverRotation.castBox(box, reach, eye) != null with(rotation) { val targetSpeed = if (slowDown) 0.0 else 1.0 @@ -257,7 +257,7 @@ object KillAura : Module( // Rotation check run { if (!rotate) return@run - val angle = RotationManager.currentRotation + val angle = RotationManager.activeRotation if (interactionSettings.strictRayCast) { val cast = angle.rayCast(interactionSettings.attackReach) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index fc0dfb7f2..2d1a4d2b0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -27,8 +27,6 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh import com.lambda.graphics.renderer.esp.builders.ofBox -import com.lambda.interaction.request.rotation.RotationManager.currentRotation -import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.blockplace.PlaceFinder.Companion.buildPlaceInfo import com.lambda.interaction.blockplace.PlaceInfo import com.lambda.interaction.blockplace.PlaceInteraction.placeBlock @@ -37,6 +35,8 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifferenc import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.Rotation.Companion.wrap +import com.lambda.interaction.request.rotation.RotationManager.activeRotation +import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces @@ -216,9 +216,9 @@ object Scaffold : Module( val assumedYaw = assumeYawByDirection(moveYaw) // No need to rotate, already looking correctly - val lookingCorrectly = castRotation(currentRotation, info) != null - val isYawStable = angleDifference(currentRotation.yaw, assumedYaw) < YAW_THRESHOLD - if (lookingCorrectly && isYawStable) return currentRotation + val lookingCorrectly = castRotation(activeRotation, info) != null + val isYawStable = angleDifference(activeRotation.yaw, assumedYaw) < YAW_THRESHOLD + if (lookingCorrectly && isYawStable) return activeRotation // Dividing the surface by segments and iterating through them val pointScan = mutableSetOf().apply { @@ -265,7 +265,7 @@ object Scaffold : Module( val optimalRotation = when { // Placing supporting block - info.placeSteps > 0 && !isDiagonal -> currentRotation + info.placeSteps > 0 && !isDiagonal -> activeRotation // Placing base block else -> assumedRotation @@ -316,7 +316,7 @@ object Scaffold : Module( val isNearLedge = world.isBlockSpaceEmpty(player, predictedBox)*/ val sneak = lastRotation?.let { - currentRotation dist it > YAW_THRESHOLD && player.isOnGround + activeRotation dist it > YAW_THRESHOLD && player.isOnGround } ?: (sneakTicks > 0 && placeInfoAge < 4) if (sneak) sneakTicks = 3 From 38a5fc0d15d5038fb013ef2a3e5075afa2f19650 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 19 Apr 2025 02:42:06 +0100 Subject: [PATCH 143/364] previous rotation direction checks in build sim --- .../construction/context/PlaceContext.kt | 7 ++-- .../construction/simulation/BuildSimulator.kt | 32 ++++++++++++------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index a88658a0a..40b6c9f93 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -30,7 +30,6 @@ import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import java.awt.Color @@ -46,7 +45,7 @@ data class PlaceContext( override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, - val primeDirection: Direction? + val previousDirWasInvalid: Boolean = false ) : BuildContext { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) @@ -79,7 +78,9 @@ data class PlaceContext( fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) - val validRotation = if (request.build.placing.rotate) request.rotation.request(rotation).done else true + val validRotation = if (request.build.placing.rotate) { + request.rotation.request(rotation).done && !previousDirWasInvalid + } else true return hotbarRequest.done && validRotation } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 3f97f3ffc..368b0e6d8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -69,7 +69,6 @@ import net.minecraft.item.BlockItem import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys -import net.minecraft.state.property.Properties import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos @@ -77,7 +76,6 @@ import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import net.minecraft.util.shape.VoxelShapes -import kotlin.jvm.optionals.getOrNull import kotlin.math.pow object BuildSimulator { @@ -243,7 +241,7 @@ object BuildSimulator { // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { setPos(eye.x, eye.y - standingEyeHeight, eye.z) - if (place.rotateForPlace) this.rotation = checkedHit.targetRotation + this.rotation = RotationManager.serverRotation } val checkedResult = checkedHit.hit @@ -311,11 +309,26 @@ object BuildSimulator { } } - simulatePlaceState()?.let simulatePlaceState@ { basePlaceResult -> - if (!place.axisRotate) { + var currentDirIsInvalid = false + simulatePlaceState()?.let { basePlaceResult -> + if (!place.rotate) { acc.add(basePlaceResult) return@forEach } + + currentDirIsInvalid = true + } + + if (place.rotateForPlace && !place.axisRotate) { + fakePlayer.rotation = checkedHit.targetRotation + simulatePlaceState()?.let { rotatedPlaceResult -> + acc.add(rotatedPlaceResult) + return@forEach + } + rot = checkedHit.targetRotation + } + + if (place.axisRotate) run axisRotations@ { placementRotations.forEachIndexed direction@ { index, angle -> fakePlayer.rotation = angle @@ -332,8 +345,8 @@ object BuildSimulator { } else -> { - rot = fakePlayer.rotation - return@simulatePlaceState + rot = angle + return@axisRotations } } } @@ -343,9 +356,6 @@ object BuildSimulator { val hitBlock = blockState(blockHit.blockPos).block val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks - val primeDirection = - (target as? TargetState.State)?.blockState?.getOrEmpty(Properties.HORIZONTAL_FACING)?.getOrNull() - val placeContext = PlaceContext( eye, blockHit, @@ -358,7 +368,7 @@ object BuildSimulator { target, shouldSneak, false, - primeDirection + currentDirIsInvalid ) val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) From 67c6f26e2609c8dd3273ec53dabb8a8d30c1b22e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 19 Apr 2025 04:23:35 +0100 Subject: [PATCH 144/364] add todo in PlaceContext --- .../com/lambda/interaction/construction/context/PlaceContext.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 40b6c9f93..85fec9e0a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -79,6 +79,8 @@ data class PlaceContext( fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) val validRotation = if (request.build.placing.rotate) { + //ToDo: add a rotation compare to the !previousDirWasInvalid check in case the player + // was able to perform actions after updating the server rotation in the same tick request.rotation.request(rotation).done && !previousDirWasInvalid } else true return hotbarRequest.done && validRotation From e7c79907d70988b3a052cf4fd467b58e23b8f170 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 20 Apr 2025 19:25:55 +0100 Subject: [PATCH 145/364] rotations for instant breakable blocks --- .../com/lambda/interaction/request/RequestConfig.kt | 6 +++--- .../lambda/interaction/request/breaking/BreakConfig.kt | 4 ++-- .../interaction/request/breaking/BreakManager.kt | 10 ++++++---- .../lambda/interaction/request/hotbar/HotbarConfig.kt | 4 ++-- .../lambda/interaction/request/placing/PlaceConfig.kt | 4 ++-- .../interaction/request/rotation/RotationConfig.kt | 4 ++-- .../interaction/request/rotation/RotationManager.kt | 4 +++- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt index ee906c748..8472a9b4b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt @@ -20,8 +20,8 @@ package com.lambda.interaction.request abstract class RequestConfig ( val priority: Priority ) { - protected abstract fun requestInternal(request: R) + protected abstract fun requestInternal(request: R, queueIfClosed: Boolean = true) - fun request(request: R): R = - request.apply(::requestInternal) + fun request(request: R, queueIfClosed: Boolean = true): R = + request.apply { requestInternal(this, queueIfClosed) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index c06ec99c4..1bee4db28 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -47,8 +47,8 @@ abstract class BreakConfig( abstract val minFortuneLevel: Int abstract val ignoredBlocks: Set - override fun requestInternal(request: BreakRequest) { - BreakManager.request(request) + override fun requestInternal(request: BreakRequest, queueIfClosed: Boolean) { + BreakManager.request(request, queueIfClosed) } enum class BreakMode { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 2f2257751..4c7de9502 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -191,15 +191,16 @@ object BreakManager : RequestHandler( .filterNotNull() .filter { !it.isRedundant } .also { infos -> - infos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> - info.request.rotation.request(info.context.rotation) + rotationRequest = infos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> + val rotation = info.context.rotation + if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation } } .reversed() .forEach { info -> if (info.updatedProgressThisTick) return@forEach if (!info.context.requestDependencies(request)) return@breakInfos - if (!rotated || tickStage !in info.breakConfig.breakStageMask) return@breakInfos + if ((!rotated && info.isPrimary) || tickStage !in info.breakConfig.breakStageMask) return@breakInfos updateBreakProgress(info) } } @@ -283,7 +284,8 @@ object BreakManager : RequestHandler( val ctx = iterator.next() if (!ctx.requestDependencies(request)) return false - if (tickStage !in request.build.breaking.breakStageMask) return false + rotationRequest = if (request.build.breaking.rotateForBreak) request.rotation.request(ctx.rotation, false) else null + if (!rotated || tickStage !in request.build.breaking.breakStageMask) return false val breakInfo = initNewBreak(ctx, request) ?: return false request.onAccept?.invoke(ctx.expectedPos) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt index 5f1ae8bcf..72f338f3e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -64,7 +64,7 @@ abstract class HotbarConfig( * * @param request The hotbar request to register. */ - override fun requestInternal(request: HotbarRequest) { - HotbarManager.request(request) + override fun requestInternal(request: HotbarRequest, queueIfClosed: Boolean) { + HotbarManager.request(request, queueIfClosed) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index fad02fe90..f90ad5765 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -40,8 +40,8 @@ abstract class PlaceConfig( abstract val swingType: BuildConfig.SwingType abstract val sounds: Boolean - override fun requestInternal(request: PlaceRequest) { - PlaceManager.request(request) + override fun requestInternal(request: PlaceRequest, queueIfClosed: Boolean) { + PlaceManager.request(request, queueIfClosed) } enum class AirPlaceMode { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index a7aacccec..10d80f022 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -57,8 +57,8 @@ abstract class RotationConfig(priority: Priority) : RequestConfig( - TickStage.TickStart + TickStage.TickStart, + TickStage.PostHotbar, + TickStage.PostInteract, ), Loadable { var activeRotation = Rotation.ZERO; private set var serverRotation = Rotation.ZERO; private set From 8ebfb1714a4047a7e2942a1461fe918acde70d98 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 20 Apr 2025 19:30:59 +0100 Subject: [PATCH 146/364] currentDirIsInvalid check before calculating axis rotation --- .../interaction/construction/simulation/BuildSimulator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 368b0e6d8..7a10ce012 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -328,7 +328,7 @@ object BuildSimulator { rot = checkedHit.targetRotation } - if (place.axisRotate) run axisRotations@ { + if (place.axisRotate && currentDirIsInvalid) run axisRotations@ { placementRotations.forEachIndexed direction@ { index, angle -> fakePlayer.rotation = angle From d3eda36a57c84f565722210f5d3d74e3573b11ef Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 20 Apr 2025 19:37:21 +0100 Subject: [PATCH 147/364] use players rotation first rather than current rotation --- .../lambda/interaction/construction/simulation/BuildSimulator.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 7a10ce012..b968c4878 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -241,7 +241,6 @@ object BuildSimulator { // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { setPos(eye.x, eye.y - standingEyeHeight, eye.z) - this.rotation = RotationManager.serverRotation } val checkedResult = checkedHit.hit From 2ec256d9efd3c06fbaaa71e40de20185bb25aa2f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 21 Apr 2025 00:48:19 +0100 Subject: [PATCH 148/364] use current rotation rather than player rotation when checking the validity of the previous rotation for direction --- .../lambda/interaction/construction/context/PlaceContext.kt | 2 +- .../interaction/construction/simulation/BuildSimulator.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 85fec9e0a..df5a88967 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -81,7 +81,7 @@ data class PlaceContext( val validRotation = if (request.build.placing.rotate) { //ToDo: add a rotation compare to the !previousDirWasInvalid check in case the player // was able to perform actions after updating the server rotation in the same tick - request.rotation.request(rotation).done && !previousDirWasInvalid + request.rotation.request(rotation, false).done && !previousDirWasInvalid } else true return hotbarRequest.done && validRotation } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index b968c4878..5ccf1dc7d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -241,6 +241,7 @@ object BuildSimulator { // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { setPos(eye.x, eye.y - standingEyeHeight, eye.z) + this.rotation = RotationManager.serverRotation } val checkedResult = checkedHit.hit @@ -293,7 +294,7 @@ object BuildSimulator { } lateinit var resultState: BlockState - var rot = fakePlayer.rotation + var rot = player.rotation val simulatePlaceState = placeState@ { resultState = blockItem.getPlacementState(context) @@ -330,7 +331,6 @@ object BuildSimulator { if (place.axisRotate && currentDirIsInvalid) run axisRotations@ { placementRotations.forEachIndexed direction@ { index, angle -> fakePlayer.rotation = angle - when (val placeResult = simulatePlaceState()) { is PlaceResult.BlockedByEntity -> { acc.add(placeResult) From ad9b456e24f0c4383e8a959fa26845ab964ab20b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 22 Apr 2025 23:29:43 +0100 Subject: [PATCH 149/364] fix rotations being reset causing incorrect placements --- .../com/lambda/config/groups/TickStage.kt | 2 +- .../lambda/interaction/PlayerPacketManager.kt | 9 +++++++ .../construction/context/PlaceContext.kt | 6 ++--- .../interaction/request/RequestHandler.kt | 3 +-- .../request/rotation/RotationManager.kt | 24 ++++--------------- .../request/rotation/RotationRequest.kt | 1 + 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt index c1160339b..060287782 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt @@ -21,5 +21,5 @@ enum class TickStage { TickStart, PostHotbar, PostInteract, - PostMovement, + PlayerTickPost } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index d4736b1e4..fc1dbe52d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -134,6 +134,15 @@ object PlayerPacketManager { } } + // Update the server rotation in RotationManager + with (RotationManager) { + prevServerRotation = serverRotation + serverRotation = new.rotation/*.fixSensitivity(prevServerRotation)*/ + activeRequest?.let { request -> + request.matchesServerRot = request.done + } + } + PlayerPacketEvent.Post().post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index df5a88967..f3a1f95ef 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -45,7 +45,7 @@ data class PlaceContext( override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, - val previousDirWasInvalid: Boolean = false + val currentDirIsInvalid: Boolean = false ) : BuildContext { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) @@ -79,9 +79,7 @@ data class PlaceContext( fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) val validRotation = if (request.build.placing.rotate) { - //ToDo: add a rotation compare to the !previousDirWasInvalid check in case the player - // was able to perform actions after updating the server rotation in the same tick - request.rotation.request(rotation, false).done && !previousDirWasInvalid + request.rotation.request(rotation, false).done && (!currentDirIsInvalid || rotation.matchesServerRot) } else true return hotbarRequest.done && validRotation } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 520c60ebe..839d8be93 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -20,7 +20,6 @@ package com.lambda.interaction.request import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.event.Event -import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.threading.runSafe @@ -61,7 +60,7 @@ abstract class RequestHandler( TickStage.TickStart -> openRequestsFor(TickStage.TickStart) TickStage.PostHotbar -> { /*ToDo*/ } TickStage.PostInteract -> { /*ToDo*/ } - TickStage.PostMovement -> openRequestsFor(TickStage.PostMovement) + TickStage.PlayerTickPost -> openRequestsFor(TickStage.PlayerTickPost) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index f7bb77307..028cc943f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -24,7 +24,6 @@ import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent -import com.lambda.event.events.PacketEvent import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent @@ -35,7 +34,6 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotation.Rotation.Companion.slerp import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.modules.client.Baritone -import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe import com.lambda.util.extension.partialTicks import com.lambda.util.extension.rotation @@ -43,7 +41,6 @@ import com.lambda.util.math.MathUtils.toRadian import com.lambda.util.math.Vec2d import com.lambda.util.math.lerp import net.minecraft.client.input.Input -import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket import kotlin.math.cos import kotlin.math.round import kotlin.math.sign @@ -54,12 +51,12 @@ object RotationManager : RequestHandler( TickStage.PostHotbar, TickStage.PostInteract, ), Loadable { - var activeRotation = Rotation.ZERO; private set - var serverRotation = Rotation.ZERO; private set - private var prevServerRotation = Rotation.ZERO + var activeRotation = Rotation.ZERO + var serverRotation = Rotation.ZERO + var prevServerRotation = Rotation.ZERO + var activeRequest: RotationRequest? = null private var changedThisTick = false - private var activeRequest: RotationRequest? = null override fun load() = "Loaded Rotation Manager" @@ -79,15 +76,6 @@ object RotationManager : RequestHandler( changedThisTick = false } - listen { event -> - val packet = event.packet - if (packet !is PlayerPositionLookS2CPacket) return@listen - - runGameScheduled { - reset(Rotation(packet.yaw, packet.pitch)) - } - } - listenUnsafe { reset(Rotation.ZERO) } @@ -111,10 +99,6 @@ object RotationManager : RequestHandler( updateActiveRotation() } - // Update the current rotation - prevServerRotation = serverRotation - serverRotation = activeRotation/*.fixSensitivity(prevServerRotation)*/ - // Handle LOCK mode if (activeRequest?.mode == RotationMode.Lock) { player.yaw = serverRotation.yawF diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index 6a897105d..68e19e6aa 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -33,6 +33,7 @@ data class RotationRequest( val speedMultiplier: Double = 1.0 ) : Request(prio, rot) { var age = 0 + var matchesServerRot = false constructor( target: RotationTarget, From 3df34854997d131bb5ee08c1a06e99ccb5c61ca7 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:57:33 -0400 Subject: [PATCH 150/364] cleaned up some code --- .../request/breaking/BreakManager.kt | 29 ++++++++++--------- .../request/breaking/BreakRequest.kt | 6 ++-- .../request/hotbar/HotbarConfig.kt | 4 +-- .../request/rotation/RotationConfig.kt | 4 +-- .../rotation/visibilty/PointSelection.kt | 16 +++++----- .../module/modules/player/PacketMine.kt | 4 +-- 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 4c7de9502..f2345ead0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -186,21 +186,23 @@ object BreakManager : RequestHandler( // Reversed so that the breaking order feels natural to the user as the primary break is always the // last break to be started - run breakInfos@ { + run { breakInfos .filterNotNull() .filter { !it.isRedundant } - .also { infos -> - rotationRequest = infos.firstOrNull { it.breakConfig.rotateForBreak }?.let { info -> - val rotation = info.context.rotation - if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation - } + .also { + rotationRequest = it.firstOrNull { it.breakConfig.rotateForBreak } + ?.let { info -> + val rotation = info.context.rotation + if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation + } } - .reversed() + .asReversed() .forEach { info -> - if (info.updatedProgressThisTick) return@forEach - if (!info.context.requestDependencies(request)) return@breakInfos - if ((!rotated && info.isPrimary) || tickStage !in info.breakConfig.breakStageMask) return@breakInfos + if (info.updatedProgressThisTick) return@run + if (!info.context.requestDependencies(request)) return@run + if ((!rotated && info.isPrimary) || tickStage !in info.breakConfig.breakStageMask) return@run + updateBreakProgress(info) } } @@ -263,9 +265,10 @@ object BreakManager : RequestHandler( private fun SafeContext.canAccept(ctx: BreakContext): Boolean { if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false - breakInfos.firstOrNull { it != null && !it.isRedundant } + breakInfos + .firstOrNull { it != null && !it.isRedundant } ?.let { info -> - if ( ctx.hotbarIndex != info.context.hotbarIndex) return false + if (ctx.hotbarIndex != info.context.hotbarIndex) return false } return !blockState(ctx.expectedPos).isAir @@ -641,4 +644,4 @@ object BreakManager : RequestHandler( } override fun preEvent(): Event = UpdateManagerEvent.Break().post() -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 2a815fc5c..421a747ff 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -43,8 +43,6 @@ data class BreakRequest( ) : Request(prio, build.breaking) { override val done: Boolean get() = runSafe { - contexts.all { - ctx -> ctx.targetState.matches(blockState(ctx.expectedPos), ctx.expectedPos, world) - } + contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt index 72f338f3e..556666d9d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -21,7 +21,7 @@ import com.lambda.config.groups.TickStage import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig -/* +/** * Abstract base class for configuring hotbar slot switch behavior. * * @param priority The priority of this configuration. @@ -67,4 +67,4 @@ abstract class HotbarConfig( override fun requestInternal(request: HotbarRequest, queueIfClosed: Boolean) { HotbarManager.request(request, queueIfClosed) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index 10d80f022..9759071f2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -21,7 +21,7 @@ import com.lambda.config.groups.TickStage import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig -/* +/** * Abstract base class for configuring rotation behavior. * * @param priority The priority of this configuration. @@ -68,4 +68,4 @@ abstract class RotationConfig(priority: Priority) : RequestConfig) -> VisibilityChecker.CheckedHit?) { ByRotation({ hits -> @@ -27,15 +28,14 @@ enum class PointSelection(val select: (MutableList RotationManager.activeRotation dist it.targetRotation } }), - Optimum( optimum@ { hits -> + Optimum({ hits -> val optimum = hits - .map { it.hit.pos }.reduceOrNull { acc, vec3d -> - acc?.let { it.add(vec3d) } ?: acc - } - ?.multiply(1.0 / hits.size.toDouble()) ?: return@optimum null + .map { it.hit.pos } + .reduceOrNull { acc, pos -> acc.add(pos) } + ?.times(1 / hits.size) - hits.minByOrNull { - it.hit.pos distSq optimum + optimum?.let { + hits.minByOrNull { it.hit.pos distSq optimum } } }) -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index ddbe9faa0..a0e895596 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -40,7 +40,7 @@ import net.minecraft.util.math.BlockPos import java.util.concurrent.ConcurrentLinkedQueue object PacketMine : Module( - "Packet Mine", + "PacketMine", "automatically breaks blocks, and does it faster", setOf(ModuleTag.PLAYER) ) { @@ -116,4 +116,4 @@ object PacketMine : Module( enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } -} \ No newline at end of file +} From bc883f8a53fa6cd1a2edf0147c0fe9c9e18bf027 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 26 Apr 2025 00:13:48 +0100 Subject: [PATCH 151/364] return foreach instead of run and remove context sorting from the managers --- .../interaction/construction/context/BreakContext.kt | 5 +++++ .../interaction/construction/context/PlaceContext.kt | 8 ++++++-- .../interaction/request/breaking/BreakManager.kt | 12 ++++-------- .../interaction/request/placing/PlaceManager.kt | 12 ++++-------- .../main/kotlin/com/lambda/task/tasks/BuildTask.kt | 2 -- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 6d81ba7df..4abff698d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -23,6 +23,7 @@ import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.breaking.BreakRequest +import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo @@ -64,8 +65,12 @@ data class BreakContext( return when (other) { is BreakContext -> compareByDescending { if (it.checkedState.block is FallingBlock) it.expectedPos.y else 0 + }.thenBy { + it.instantBreak }.thenBy { it.rotation.target.angleDistance + }.thenBy { + it.hotbarIndex == HotbarManager.serverSlot }.compare(this, other) else -> 1 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index f3a1f95ef..14c020d7f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -17,11 +17,13 @@ package com.lambda.interaction.construction.context +import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotation.RotationRequest @@ -57,9 +59,11 @@ data class PlaceContext( }.thenByDescending { it.checkedState.fluidState.level }.thenBy { - it.sneak + it.sneak == mc.player?.isSneaking }.thenBy { it.rotation.target.angleDistance + }.thenBy { + it.hotbarIndex == HotbarManager.serverSlot }.thenBy { it.distance }.thenBy { @@ -79,7 +83,7 @@ data class PlaceContext( fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) val validRotation = if (request.build.placing.rotate) { - request.rotation.request(rotation, false).done && (!currentDirIsInvalid || rotation.matchesServerRot) + request.rotation.request(rotation, false).done && !currentDirIsInvalid } else true return hotbarRequest.done && validRotation } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index f2345ead0..1d6c836c8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -42,7 +42,6 @@ import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingBreaks import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending -import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe @@ -199,7 +198,7 @@ object BreakManager : RequestHandler( } .asReversed() .forEach { info -> - if (info.updatedProgressThisTick) return@run + if (info.updatedProgressThisTick) return@forEach if (!info.context.requestDependencies(request)) return@run if ((!rotated && info.isPrimary) || tickStage !in info.breakConfig.breakStageMask) return@run @@ -216,7 +215,7 @@ object BreakManager : RequestHandler( } /** - * Filters and sorts the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches + * Filters the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches * in positions. If a match is found, the [BreakInfo] is updated with the new context. Otherwise, the break is cancelled. * The [instantBreaks] and [breaks] collections are then populated with the new appropriate contexts, and the [maxBreaksThisTick] * value is set. @@ -225,13 +224,10 @@ object BreakManager : RequestHandler( * @see cancelBreak */ private fun SafeContext.populateFrom(request: BreakRequest) { - // Sanitize and sort the new breaks + // Sanitize the new breaks val newBreaks = request.contexts .filter { ctx -> canAccept(ctx) } - .sortedWith( - compareByDescending { it.instantBreak } - .thenByDescending { it.hotbarIndex == HotbarManager.serverSlot } - ).toMutableList() + .toMutableList() // Update the current break infos or cancel if abandoned breakInfos diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 85df2368b..cc6b470a7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -30,7 +30,6 @@ import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager -import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.placing.PlaceManager.activeRequest import com.lambda.interaction.request.placing.PlaceManager.processRequest import com.lambda.interaction.request.placing.PlacedBlockHandler.addPendingPlace @@ -134,7 +133,7 @@ object PlaceManager : RequestHandler( if (ctx.sneak) shouldSneak = true if (!ctx.requestDependencies(request) || !validSneak(player)) return - if (tickStage !in request.build.placing.placeStageMask) return +// if (tickStage !in request.build.placing.placeStageMask) return val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) if (!actionResult.isAccepted) warn("Placement interaction failed with $actionResult") @@ -145,21 +144,18 @@ object PlaceManager : RequestHandler( } /** - * Filters and sorts the [request]'s [PlaceContext]s, placing them into the [potentialPlacements] collection, and + * Filters the [request]'s [PlaceContext]s, placing them into the [potentialPlacements] collection, and * setting the maxPlacementsThisTick value. * * @see canPlace */ - private fun SafeContext.populateFrom(request: PlaceRequest) { + private fun populateFrom(request: PlaceRequest) { val place = request.build.placing setPendingConfigs(request) potentialPlacements = request.contexts .filter { canPlace(it) } - .sortedWith( - compareByDescending { it.hotbarIndex == HotbarManager.serverSlot } - .thenByDescending { it.sneak == player.isSneaking } - ).toMutableList() + .toMutableList() val pendingLimit = (place.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) maxPlacementsThisTick = (place.placementsPerTick.coerceAtMost(pendingLimit)) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 35a562772..94ebd445a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -18,7 +18,6 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.GoalBlock -import baritone.api.pathing.goals.GoalNear import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig @@ -56,7 +55,6 @@ import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.SlotUtils.hotbarAndStorage -import com.lambda.util.world.toFastVec import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos import java.util.concurrent.ConcurrentLinkedQueue From f398a32ba0036a644364843c3f671fa754e24f59 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 26 Apr 2025 13:33:55 -0400 Subject: [PATCH 152/364] Use event tick stages --- .../lambda/mixin/MinecraftClientMixin.java | 53 ++++++-- .../mixin/entity/ClientPlayerEntityMixin.java | 15 ++- .../com/lambda/config/groups/BreakSettings.kt | 5 +- .../lambda/config/groups/HotbarSettings.kt | 5 +- .../com/lambda/config/groups/PlaceSettings.kt | 5 +- .../com/lambda/event/events/TickEvent.kt | 116 ++++++++++++------ .../com/lambda/interaction/request/Request.kt | 2 +- .../interaction/request/RequestHandler.kt | 32 +++-- .../request/breaking/BreakConfig.kt | 6 +- .../interaction/request/breaking/BreakInfo.kt | 22 ++-- .../request/breaking/BreakManager.kt | 6 +- .../request/breaking/BreakRequest.kt | 5 +- .../request/hotbar/HotbarConfig.kt | 4 +- .../request/hotbar/HotbarManager.kt | 8 +- .../request/hotbar/HotbarRequest.kt | 2 +- .../request/placing/PlaceConfig.kt | 5 +- .../request/placing/PlaceManager.kt | 8 +- .../request/placing/PlaceRequest.kt | 2 +- .../request/rotation/RotationManager.kt | 9 +- .../request/rotation/RotationRequest.kt | 4 +- 20 files changed, 201 insertions(+), 113 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 9223e7703..2e3b7bbe0 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -24,11 +24,16 @@ import com.lambda.event.events.TickEvent; import com.lambda.module.modules.player.Interact; import com.lambda.module.modules.player.PacketMine; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.sound.SoundManager; import net.minecraft.util.thread.ThreadExecutor; import net.minecraft.util.Hand; import net.minecraft.util.hit.HitResult; @@ -40,7 +45,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(MinecraftClient.class) +@Mixin(value = MinecraftClient.class, priority = Integer.MAX_VALUE) public class MinecraftClientMixin { @Shadow @Nullable @@ -49,24 +54,46 @@ public class MinecraftClientMixin { @Nullable public HitResult crosshairTarget; - @Inject(method = "tick", at = @At("HEAD")) - void onTickPre(CallbackInfo ci) { - EventFlow.post(new TickEvent.Pre()); + @WrapMethod(method = "render") + void onLoopTick(boolean tick, Operation original) { + EventFlow.post(TickEvent.Render.Pre.INSTANCE); + original.call(tick); + EventFlow.post(TickEvent.Render.Post.INSTANCE); } - @Inject(method = "tick", at = @At("RETURN")) - void onTickPost(CallbackInfo ci) { - EventFlow.post(new TickEvent.Post()); + @WrapMethod(method = "tick") + void onTick(Operation original) { + EventFlow.post(TickEvent.Pre.INSTANCE); + original.call(); + EventFlow.post(TickEvent.Post.INSTANCE); } - @Inject(method = "render", at = @At("HEAD")) - void onLoopTickPre(CallbackInfo ci) { - EventFlow.post(new TickEvent.Render.Pre()); + @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;tick()V")) + void onNetwork(ClientPlayerInteractionManager instance, Operation original) { + EventFlow.post(TickEvent.Network.Pre.INSTANCE); + original.call(instance); + EventFlow.post(TickEvent.Network.Post.INSTANCE); } - @Inject(method = "render", at = @At("RETURN")) - void onLoopTickPost(CallbackInfo ci) { - EventFlow.post(new TickEvent.Render.Post()); + @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;handleInputEvents()V")) + void onInput(MinecraftClient instance, Operation original) { + EventFlow.post(TickEvent.Input.Pre.INSTANCE); + original.call(instance); + EventFlow.post(TickEvent.Input.Post.INSTANCE); + } + + @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;tick()V")) + void onWorldRenderer(WorldRenderer instance, Operation original) { + EventFlow.post(TickEvent.WorldRender.Pre.INSTANCE); + original.call(instance); + EventFlow.post(TickEvent.WorldRender.Post.INSTANCE); + } + + @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/sound/SoundManager;tick(Z)V")) + void onSound(SoundManager instance, boolean paused, Operation original) { + EventFlow.post(TickEvent.Sound.Pre.INSTANCE); + original.call(instance, paused); + EventFlow.post(TickEvent.Sound.Post.INSTANCE); } @Inject(at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;info(Ljava/lang/String;)V", shift = At.Shift.AFTER, remap = false), method = "stop") diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index c01507154..3375d4097 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -25,6 +25,8 @@ import com.lambda.interaction.PlayerPacketManager; import com.lambda.interaction.request.rotation.RotationManager; import com.lambda.module.modules.player.PortalGui; +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.DeathScreen; import net.minecraft.client.gui.screen.Screen; @@ -129,14 +131,11 @@ void sendBegin(CallbackInfo ci) { autoJumpEnabled = Lambda.getMc().options.getAutoJump().getValue(); } - @Inject(method = "tick", at = @At(value = "HEAD")) - void onTickPre(CallbackInfo ci) { - EventFlow.post(new TickEvent.Player.Pre()); - } - - @Inject(method = "tick", at = @At(value = "RETURN")) - void onTickPost(CallbackInfo ci) { - EventFlow.post(new TickEvent.Player.Post()); + @WrapMethod(method = "tick") + void onTick(Operation original) { + EventFlow.post(TickEvent.Player.Pre.INSTANCE); + original.call(); + EventFlow.post(TickEvent.Player.Post.INSTANCE); } @Redirect(method = "tickNewAi", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getYaw()F")) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 7f27479dc..a24e13e4f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -18,6 +18,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.breaking.BreakConfig import com.lambda.util.BlockUtils.allSigns @@ -32,7 +33,7 @@ class BreakSettings( override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 0, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } - override val breakStageMask by c.setting("Break Stage Mask", setOf(*TickStage.entries.toTypedArray()), "The sub-tick timing at which break actions can be performed", vis) + override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } @@ -47,4 +48,4 @@ class BreakSettings( override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() } override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt index adab14c43..ca8ed7aef 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt @@ -18,6 +18,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.hotbar.HotbarConfig @@ -30,5 +31,5 @@ class HotbarSettings( override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", vis) override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() } override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) - override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(*TickStage.entries.toTypedArray()), "The sub-tick timing at which hotbar actions are performed", vis) -} \ No newline at end of file + override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which hotbar actions are performed", vis) +} diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index e3fad9e38..1fce32319 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -18,6 +18,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.placing.PlaceConfig @@ -29,11 +30,11 @@ class PlaceSettings( override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } - override val placeStageMask by c.setting("Place Sequence Mode", setOf(*TickStage.entries.toTypedArray()), "The sub-tick timing at which break actions are performed") { vis() } + override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions are performed") { vis() } override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() } override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing } override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt b/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt index 26fcad5d9..629f5564c 100644 --- a/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt @@ -19,51 +19,97 @@ package com.lambda.event.events import com.lambda.event.Event +/** + * Phases: + * + * 1. **Pre-Tick**: Increments uptime, steps world tick manager, decrement item use cooldown. + * 2. **GUI Update**: Processes delayed messages, updates HUD. + * 3. **Game Mode Update**: Updates targeted entity, ticks tutorial, and interaction managers. + * 4. **Texture Update**: Ticks texture manager. + * 5. **Screen Handling**: Manages screen logic, ticks current screen. + * 6. **Debug HUD Update**: Resets debug HUD chunk. + * 7. **Input Handling**: Handles input events, decrements attack cooldown. + * 8. **World Update**: Ticks game and world renderers, world entities. + * 9. **Music and Sound Update**: Ticks music tracker and sound manager. + * 10. **Tutorial and Social Interactions**: Handles tutorial and social interactions, ticks world. + * 11. **Pending Connection**: Ticks integrated server connection. + * 12. **Keyboard Handling**: Polls for debug crash key presses. + * + * @see net.minecraft.client.MinecraftClient.tick + */ sealed class TickEvent { /** * Triggered before each iteration of the game loop. * * Phases: * - * 1. **Pre-Tick**: Increments uptime, steps world tick manager, decrement item use cooldown. - * 2. **GUI Update**: Processes delayed messages, updates HUD. - * 3. **Game Mode Update**: Updates targeted entity, ticks tutorial, and interaction managers. - * 4. **Texture Update**: Ticks texture manager. - * 5. **Screen Handling**: Manages screen logic, ticks current screen. - * 6. **Debug HUD Update**: Resets debug HUD chunk. - * 7. **Input Handling**: Handles input events, decrements attack cooldown. - * 8. **World Update**: Ticks game and world renderers, world entities. - * 9. **Music and Sound Update**: Ticks music tracker and sound manager. - * 10. **Tutorial and Social Interactions**: Handles tutorial and social interactions, ticks world. - * 11. **Pending Connection**: Ticks integrated server connection. - * 12. **Keyboard Handling**: Polls for debug crash key presses. - * - * @see net.minecraft.client.MinecraftClient.tick + * 1. Increments uptime + * 2. Steps world tick manager + * 3. Decrements item use cooldown */ - class Pre : Event + data object Pre : Event /** * Triggered after each iteration of the game loop. - * Targeted at 20 ticks per second. + */ + data object Post : Event + + /** + * Triggered during the network tick stage only + * + * Phases: + * + * 1. Synchronizes player inventory slot changes + * 2. Clears the outgoing packet queue + * 3. Ticks packet listeners + * 4. Flushes the connection channel + * 5. Updates network statistics + * 6. Updates the packet logger + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.tick + */ + sealed class Network { + data object Pre : Event + data object Post : Event + } + + /** + * Triggered during the input tick stage * * Phases: * - * 1. **Pre-Tick**: Increments uptime, steps world tick manager, decrement item use cooldown. - * 2. **GUI Update**: Processes delayed messages, updates HUD. - * 3. **Game Mode Update**: Updates targeted entity, ticks tutorial, and interaction managers. - * 4. **Texture Update**: Ticks texture manager. - * 5. **Screen Handling**: Manages screen logic, ticks current screen. - * 6. **Debug HUD Update**: Resets debug HUD chunk. - * 7. **Input Handling**: Handles input events, decrements attack cooldown. - * 8. **World Update**: Ticks game and world renderers, world entities (such as [TickEvent.Player]). - * 9. **Music and Sound Update**: Ticks music tracker and sound manager. - * 10. **Tutorial**: Handles tutorials, ticks world. - * 11. **Pending Connection**: Ticks integrated server connection. - * 12. **Keyboard Handling**: Polls for debug crash key presses. - * - * @see net.minecraft.client.MinecraftClient.tick + * 1. Handles various game specific keys + * 2. Handles block breaking + * 3. Adds block breaking particles + * 4. Swings the player arm + * 5. Decrements attack cooldown + * + * @see net.minecraft.client.MinecraftClient.handleInputEvents + */ + sealed class Input { + data object Pre : Event + data object Post : Event + } + + /** + * Triggered during the world render tick stage + * + * @see net.minecraft.client.render.WorldRenderer.tick */ - class Post : Event + sealed class WorldRender { + data object Pre : Event + data object Post : Event + } + + /** + * Triggered during the sound update tick stage + * + * @see net.minecraft.client.sound.SoundManager.tick + */ + sealed class Sound { + data object Pre : Event + data object Post : Event + } /** * Triggered before ([Pre]) and after ([Post]) each render tick. @@ -82,12 +128,12 @@ sealed class TickEvent { /** * Triggered before each render tick ([TickEvent.Render]) of the game loop. */ - class Pre : Event + data object Pre : Event /** * Triggered after each render tick ([TickEvent.Render]) of the game loop. */ - class Post : Event + data object Post : Event } /** @@ -106,11 +152,11 @@ sealed class TickEvent { /** * Triggered before each player tick ([TickEvent.Player]). */ - class Pre : Event + data object Pre : Event /** * Triggered after each player tick ([TickEvent.Player]). */ - class Post : Event + data object Post : Event } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt index 74dd90f8d..b7115cd77 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt @@ -24,4 +24,4 @@ abstract class Request ( var fresh = true abstract val done: Boolean -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 839d8be93..eab896c21 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.request -import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.event.Event import com.lambda.event.events.TickEvent @@ -29,7 +28,7 @@ import com.lambda.threading.runSafe * next opening if closed */ abstract class RequestHandler( - vararg openStages: TickStage, + vararg openStages: Event, private val onOpen: (SafeContext.() -> Unit)? = null, private val onClose: (SafeContext.() -> Unit)? = null ) { @@ -41,7 +40,7 @@ abstract class RequestHandler( /** * Represents the sequence stage the current tick is at */ - var tickStage = TickStage.TickStart; private set + var tickStage: Event? = null; private set /** * If a request is made while the handler isn't accepting requests, it is placed into [queuedRequest] and run @@ -55,12 +54,23 @@ abstract class RequestHandler( var activeThisTick = false; protected set init { - openStages.forEach { stage -> - when(stage) { - TickStage.TickStart -> openRequestsFor(TickStage.TickStart) - TickStage.PostHotbar -> { /*ToDo*/ } - TickStage.PostInteract -> { /*ToDo*/ } - TickStage.PlayerTickPost -> openRequestsFor(TickStage.PlayerTickPost) + openStages.forEach { + when (it) { + is TickEvent.Pre -> openRequestsFor(it) + is TickEvent.Post -> openRequestsFor(it) + is TickEvent.Network.Pre -> openRequestsFor(it) + is TickEvent.Network.Post -> openRequestsFor(it) + is TickEvent.Input.Pre -> openRequestsFor(it) + is TickEvent.Input.Post -> openRequestsFor(it) + is TickEvent.WorldRender.Pre -> openRequestsFor(it) + is TickEvent.WorldRender.Post -> openRequestsFor(it) + is TickEvent.Sound.Pre -> openRequestsFor(it) + is TickEvent.Sound.Post -> openRequestsFor(it) + is TickEvent.Render.Pre -> openRequestsFor(it) + is TickEvent.Render.Post -> openRequestsFor(it) + is TickEvent.Player.Pre -> openRequestsFor(it) + is TickEvent.Player.Post -> openRequestsFor(it) + else -> throw IllegalArgumentException("Event '$it' is not allowed for requests") } } @@ -72,7 +82,7 @@ abstract class RequestHandler( /** * opens the handler for requests for the duration of the given event */ - private inline fun openRequestsFor(stage: TickStage) { + private inline fun openRequestsFor(stage: T) { listen(priority = Int.MAX_VALUE) { tickStage = stage queuedRequest?.let { request -> @@ -119,4 +129,4 @@ abstract class RequestHandler( abstract fun SafeContext.handleRequest(request: R) protected abstract fun preEvent(): Event -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 1bee4db28..31d304271 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -18,7 +18,7 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.TickStage +import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig import net.minecraft.block.Block @@ -31,7 +31,7 @@ abstract class BreakConfig( abstract val breakThreshold: Float abstract val doubleBreak: Boolean abstract val breakDelay: Int - abstract val breakStageMask: Set + abstract val breakStageMask: Set abstract val swing: SwingMode abstract val swingType: BuildConfig.SwingType abstract val sounds: Boolean @@ -71,4 +71,4 @@ abstract class BreakConfig( BreakThenAwait, AwaitThenBreak } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 7eacbb803..b6c0a5978 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -54,22 +54,20 @@ data class BreakInfo( val callbacksCompleted @Synchronized get() = broken && (request.onItemDrop == null || item != null) + @Synchronized fun internalOnBreak() { - synchronized(this) { - broken = true - request.onBreak?.invoke(context.expectedPos) - item?.let { item -> - request.onItemDrop?.invoke(item) - } + broken = true + request.onBreak?.invoke(context.expectedPos) + item?.let { item -> + request.onItemDrop?.invoke(item) } } + @Synchronized fun internalOnItemDrop(item: ItemEntity) { - synchronized(this) { - this.item = item - if (broken) { - request.onItemDrop?.invoke(item) - } + this.item = item + if (broken) { + request.onItemDrop?.invoke(item) } } @@ -132,4 +130,4 @@ enum class BreakType(val index: Int) { Primary -> breakConfig.breakThreshold else -> 1.0f } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 1d6c836c8..67f547586 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.request.breaking -import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.event.Event import com.lambda.event.EventFlow.post @@ -62,7 +61,10 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos object BreakManager : RequestHandler( - *TickStage.entries.toTypedArray(), + TickEvent.Pre, + TickEvent.Input.Pre, + TickEvent.Player.Post, + // ToDo: Post interact onOpen = { activeRequest?.let { processRequest(it) } } ), PositionBlocking { private var primaryBreak: BreakInfo? diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 421a747ff..57d248b9d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig +import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.Priority @@ -42,7 +43,5 @@ data class BreakRequest( private val prio: Priority = 0 ) : Request(prio, build.breaking) { override val done: Boolean - get() = runSafe { - contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } - } == true + get() = runSafe { contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt index 556666d9d..c987e2e61 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -17,7 +17,7 @@ package com.lambda.interaction.request.hotbar -import com.lambda.config.groups.TickStage +import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -57,7 +57,7 @@ abstract class HotbarConfig( /** * The sub-tick timings at which hotbar actions can be performed */ - abstract val sequenceStageMask: Set + abstract val sequenceStageMask: Set /** * Registers a hotbar request with the HotbarManager. diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 38537719a..341a57a76 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.request.hotbar -import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.Event @@ -39,7 +38,10 @@ import com.lambda.threading.runSafe * @see InGameHudMixin.onTick */ object HotbarManager : RequestHandler( - *TickStage.entries.toTypedArray(), + TickEvent.Pre, + TickEvent.Input.Pre, + TickEvent.Player.Post, + // ToDo: Post interact onClose = { checkResetSwap() } ), Loadable { val serverSlot get() = runSafe { @@ -110,4 +112,4 @@ object HotbarManager : RequestHandler( } override fun preEvent(): Event = UpdateManagerEvent.Hotbar().post() -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index e9cb0c806..3ef363521 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -36,4 +36,4 @@ class HotbarRequest( override val done: Boolean get() = slot == HotbarManager.serverSlot && !swapPaused -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index f90ad5765..2a7c028ac 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.TickStage +import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -32,7 +33,7 @@ abstract class PlaceConfig( protected abstract val axisRotateSetting: Boolean val axisRotate get() = airPlace.isEnabled() && axisRotateSetting - abstract val placeStageMask: Set + abstract val placeStageMask: Set abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int abstract val placementsPerTick: Int @@ -57,4 +58,4 @@ abstract class PlaceConfig( PlaceThenAwait, AwaitThenPlace } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index cc6b470a7..55df956a8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.request.placing -import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.event.Event import com.lambda.event.EventFlow.post @@ -59,7 +58,10 @@ import net.minecraft.util.math.Direction import net.minecraft.world.GameMode object PlaceManager : RequestHandler( - *TickStage.entries.toTypedArray(), + TickEvent.Pre, + TickEvent.Input.Pre, + TickEvent.Player.Post, + // ToDo: Post interact onOpen = { activeRequest?.let { processRequest(it) } } ), PositionBlocking { private var activeRequest: PlaceRequest? = null @@ -358,4 +360,4 @@ object PlaceManager : RequestHandler( } override fun preEvent(): Event = UpdateManagerEvent.Place().post() -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index 5db12e617..7a133d821 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -40,4 +40,4 @@ data class PlaceRequest( get() = runSafe { contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 028cc943f..db9c4ea44 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -47,9 +47,10 @@ import kotlin.math.sign import kotlin.math.sin object RotationManager : RequestHandler( - TickStage.TickStart, - TickStage.PostHotbar, - TickStage.PostInteract, + TickEvent.Pre, + TickEvent.Input.Pre, + TickEvent.Player.Post, + // ToDo: Post interact ), Loadable { var activeRotation = Rotation.ZERO var serverRotation = Rotation.ZERO @@ -264,4 +265,4 @@ object RotationManager : RequestHandler( } override fun preEvent(): Event = UpdateManagerEvent.Rotation().post() -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index 68e19e6aa..0b9862a9e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -42,7 +42,5 @@ data class RotationRequest( ) : this(target, config.priority, config.rotationMode, config, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier) override val done: Boolean get() = - mode == RotationMode.None || runSafe { - target.verify() - } == true + mode == RotationMode.None || runSafe { target.verify() } == true } From 8a0cb5b0439ae39d217427f6724225871b26162d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 28 Apr 2025 19:26:51 +0100 Subject: [PATCH 153/364] got something working ish but i hate it in many, many ways --- .../lambda/interaction/PlayerPacketManager.kt | 8 +- .../construction/context/BreakContext.kt | 2 +- .../construction/context/PlaceContext.kt | 2 +- .../construction/simulation/BuildSimulator.kt | 74 ++++++----- .../interaction/request/rotation/Rotation.kt | 9 +- .../request/rotation/RotationManager.kt | 33 +++-- .../request/rotation/RotationRequest.kt | 1 - .../rotation/visibilty/PlaceDirection.kt | 124 ++++++++++++++++++ .../rotation/visibilty/PointSelection.kt | 2 +- .../rotation/visibilty/RotationTarget.kt | 2 +- .../rotation/visibilty/RotationTargets.kt | 13 ++ .../main/kotlin/com/lambda/util/BlockUtils.kt | 6 +- .../com/lambda/util/player/PlayerUtils.kt | 14 -- .../src/main/resources/lambda.accesswidener | 1 + 14 files changed, 220 insertions(+), 71 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index fc1dbe52d..a6657e5cf 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -135,13 +135,7 @@ object PlayerPacketManager { } // Update the server rotation in RotationManager - with (RotationManager) { - prevServerRotation = serverRotation - serverRotation = new.rotation/*.fixSensitivity(prevServerRotation)*/ - activeRequest?.let { request -> - request.matchesServerRot = request.done - } - } + RotationManager.onRotationSend() PlayerPacketEvent.Post().post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 4abff698d..20056ecb5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -85,7 +85,7 @@ data class BreakContext( } fun requestDependencies(request: BreakRequest): Boolean { - val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) + val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) return hotbarRequest.done } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 14c020d7f..9e9eb8423 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -81,7 +81,7 @@ data class PlaceContext( override fun shouldRotate(config: BuildConfig) = config.placing.rotate fun requestDependencies(request: PlaceRequest): Boolean { - val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar)) + val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) val validRotation = if (request.build.placing.rotate) { request.rotation.request(rotation, false).done && !currentDirIsInvalid } else true diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 5ccf1dc7d..4864e2483 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -40,11 +40,13 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.PlaceDirection import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.interaction.request.rotation.visibilty.lookAtBlock +import com.lambda.interaction.request.rotation.visibilty.lookInDirection import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -59,7 +61,6 @@ import com.lambda.util.math.distSq import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.copyPlayer import com.lambda.util.player.gamemode -import com.lambda.util.player.placementRotations import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock @@ -294,7 +295,7 @@ object BuildSimulator { } lateinit var resultState: BlockState - var rot = player.rotation + var rot = RotationManager.serverRotation val simulatePlaceState = placeState@ { resultState = blockItem.getPlacementState(context) @@ -309,43 +310,44 @@ object BuildSimulator { } } - var currentDirIsInvalid = false - simulatePlaceState()?.let { basePlaceResult -> + val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> if (!place.rotate) { acc.add(basePlaceResult) return@forEach } - - currentDirIsInvalid = true - } - - if (place.rotateForPlace && !place.axisRotate) { - fakePlayer.rotation = checkedHit.targetRotation - simulatePlaceState()?.let { rotatedPlaceResult -> - acc.add(rotatedPlaceResult) - return@forEach + true + } ?: false + + if (place.rotateForPlace) run rotate@ { + if (!place.axisRotate) { + fakePlayer.rotation = checkedHit.targetRotation + simulatePlaceState()?.let { rotatedPlaceResult -> + acc.add(rotatedPlaceResult) + return@forEach + } + rot = fakePlayer.rotation + return@rotate } - rot = checkedHit.targetRotation - } - if (place.axisRotate && currentDirIsInvalid) run axisRotations@ { - placementRotations.forEachIndexed direction@ { index, angle -> - fakePlayer.rotation = angle - when (val placeResult = simulatePlaceState()) { - is PlaceResult.BlockedByEntity -> { - acc.add(placeResult) - return@forEach - } - - is PlaceResult.NoIntegrity -> { - if (index != placementRotations.lastIndex) return@direction - acc.add(placeResult) - return@forEach - } - - else -> { - rot = angle - return@axisRotations + if (currentDirIsInvalid) { + PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> + fakePlayer.rotation = direction.rotation + when (val placeResult = simulatePlaceState()) { + is PlaceResult.BlockedByEntity -> { + acc.add(placeResult) + return@forEach + } + + is PlaceResult.NoIntegrity -> { + if (index != PlaceDirection.entries.lastIndex) return@direction + acc.add(placeResult) + return@forEach + } + + else -> { + rot = fakePlayer.rotation + return@rotate + } } } } @@ -355,10 +357,14 @@ object BuildSimulator { val hitBlock = blockState(blockHit.blockPos).block val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks + val rotationRequest = if (place.axisRotate) { + lookInDirection(PlaceDirection.fromRotation(rot)) + } else lookAt(rot, 0.001) + val placeContext = PlaceContext( eye, blockHit, - RotationRequest(lookAt(rot, 0.001), rotation), + RotationRequest(rotationRequest, rotation), eye.distanceTo(blockHit.pos), resultState, blockState(blockHit.blockPos.offset(blockHit.side)), diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt index 44dba9df5..f1d28c2c3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt @@ -30,8 +30,13 @@ import net.minecraft.entity.Entity import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.MathHelper.wrapDegrees import net.minecraft.util.math.Vec3d -import kotlin.math.* +import kotlin.math.abs +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.hypot +import kotlin.math.sin data class Rotation(val yaw: Double, val pitch: Double) { constructor(yaw: Float, pitch: Float) : this(yaw.toDouble(), pitch.toDouble()) @@ -91,7 +96,7 @@ data class Rotation(val yaw: Double, val pitch: Double) { val UP = Rotation(0.0, -90.0) val Direction.rotation get() = Rotation(yaw.toDouble(), 0.0) var Entity.rotation - get() = Rotation(yaw, pitch) + get() = Rotation(wrapDegrees(yaw), wrapDegrees(pitch)) set(value) { yaw = value.yawF pitch = value.pitchF diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 028cc943f..eda9b8de1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -17,13 +17,14 @@ package com.lambda.interaction.request.rotation -import com.lambda.Lambda +import com.lambda.Lambda.mc import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.PacketEvent import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent @@ -34,6 +35,7 @@ import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotation.Rotation.Companion.slerp import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.modules.client.Baritone +import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe import com.lambda.util.extension.partialTicks import com.lambda.util.extension.rotation @@ -41,6 +43,7 @@ import com.lambda.util.math.MathUtils.toRadian import com.lambda.util.math.Vec2d import com.lambda.util.math.lerp import net.minecraft.client.input.Input +import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket import kotlin.math.cos import kotlin.math.round import kotlin.math.sign @@ -76,6 +79,15 @@ object RotationManager : RequestHandler( changedThisTick = false } + listen { event -> + val packet = event.packet + if (packet !is PlayerPositionLookS2CPacket) return@listen + + runGameScheduled { + reset(Rotation(packet.yaw, packet.pitch)) + } + } + listenUnsafe { reset(Rotation.ZERO) } @@ -99,12 +111,6 @@ object RotationManager : RequestHandler( updateActiveRotation() } - // Handle LOCK mode - if (activeRequest?.mode == RotationMode.Lock) { - player.yaw = serverRotation.yawF - player.pitch = serverRotation.pitchF - } - // Tick and reset the context activeRequest?.let { if (--it.keepTicks > 0) return@let @@ -113,6 +119,17 @@ object RotationManager : RequestHandler( } } + fun onRotationSend() { + prevServerRotation = serverRotation + serverRotation = activeRotation/*.fixSensitivity(prevServerRotation)*/ + + // Handle LOCK mode + if (activeRequest?.mode == RotationMode.Lock) { + mc.player?.yaw = serverRotation.yawF + mc.player?.pitch = serverRotation.pitchF + } + } + private fun SafeContext.updateActiveRotation() { activeRotation = activeRequest?.let { request -> val rotationTo = if (request.keepTicks >= 0) @@ -136,7 +153,7 @@ object RotationManager : RequestHandler( private val smoothRotation get() = - lerp(Lambda.mc.partialTicks, prevServerRotation, serverRotation) + lerp(mc.partialTicks, prevServerRotation, serverRotation) @JvmStatic val lockRotation diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index 68e19e6aa..6a897105d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -33,7 +33,6 @@ data class RotationRequest( val speedMultiplier: Double = 1.0 ) : Request(prio, rot) { var age = 0 - var matchesServerRot = false constructor( target: RotationTarget, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt new file mode 100644 index 000000000..ee4f42c2c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.rotation.visibilty + +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.util.Communication.info +import net.minecraft.entity.Entity +import net.minecraft.util.math.Direction +import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.Vec3i + +enum class PlaceDirection( + val rotation: Rotation, + val vector: Vec3i, + private val yawRanges: List> +) { + Up ( 0.0, -90.0, 0, 1, 0, listOf(Double.MIN_VALUE..Double.MAX_VALUE)), + Down ( 0.0, 90.0, 0, -1, 0, listOf(Double.MIN_VALUE..Double.MAX_VALUE)), + + UpNorth ( -180.0, -90.0, 0, 1, -1, northYawRanges), + UpSouth ( 0.0, -90.0, 0, 1, 1, listOf(southYawRange)), + UpWest ( 90.0, -90.0, 1, 1, 0, listOf(westYawRange)), + UpEast ( -90.0, -90.0, -1, 1, 0, listOf(eastYawRange)), + + DownNorth( -180.0, 90.0, 0, -1, -1, northYawRanges), + DownSouth( 0.0, 90.0, 0, -1, 1, listOf(southYawRange)), + DownWest ( 90.0, 90.0, 1, -1, 0, listOf(westYawRange)), + DownEast ( -90.0, 90.0, -1, -1, 0, listOf(eastYawRange)), + + North ( -180.0, 0.0, 0, 0, -1, northYawRanges), + South ( 0.0, 0.0, 0, 0, 1, listOf(southYawRange)), + West ( 90.0, 0.0, 1, 0, 0, listOf(westYawRange)), + East ( -90.0, 0.0, -1, 0, 0, listOf(eastYawRange)); + + constructor(yaw: Double, pitch: Double, x: Int, y: Int, z: Int, yawRanges: List>) + : this(Rotation(yaw, pitch), Vec3i(x, y, z), yawRanges) + + fun snapToArea(rot: Rotation): Rotation { +// if (isInArea(rot)) return rot + + //ToDo: fix snapping to a given directions area to speed up rotations in the case they are not instant +// val normalizedYaw = wrapDegrees(rot.yaw) +// val clampedYaw = when { +// this.rotation.yaw != 180.0 -> normalizedYaw.coerceIn(yawRanges[0]) +// normalizedYaw < 0 -> normalizedYaw.coerceIn(yawRanges[0]) +// else -> normalizedYaw.coerceIn(yawRanges[1]) +// } + + info("$rotation") + + return Rotation(rotation.yaw, rotation.pitch) + } + + fun isInArea(rot: Rotation) = fromRotation(rot) == this + + companion object { + /** + * A modified version of the minecraft getEntityFacingOrder method. This version takes a + * [Rotation] instead of an [Entity] + * + * @see Direction.getEntityFacingOrder + */ + fun fromRotation(rotation: Rotation): PlaceDirection { + val pitchRad = rotation.pitchF * (Math.PI.toFloat() / 180f) + val yawRad = -rotation.yawF * (Math.PI.toFloat() / 180f) + + val sinPitch = MathHelper.sin(pitchRad) + val cosPitch = MathHelper.cos(pitchRad) + val sinYaw = MathHelper.sin(yawRad) + val cosYaw = MathHelper.cos(yawRad) + + val isFacingEast = sinYaw > 0.0f + val isFacingUp = sinPitch < 0.0f + val isFacingSouth = cosYaw > 0.0f + + val eastWestStrength = if (isFacingEast) sinYaw else -sinYaw + val upDownStrength = if (isFacingUp) -sinPitch else sinPitch + val northSouthStrength = if (isFacingSouth) cosYaw else -cosYaw + + val adjustedEastWestStrength = eastWestStrength * cosPitch + val adjustedNorthSouthStrength = northSouthStrength * cosPitch + + return when { + eastWestStrength > northSouthStrength -> when { + upDownStrength > adjustedEastWestStrength -> when { + isFacingUp && isFacingEast -> UpEast + isFacingUp -> UpWest + isFacingEast -> DownEast + else -> DownWest + } + else -> if (isFacingEast) East else West + } + upDownStrength > adjustedNorthSouthStrength -> when { + isFacingUp && isFacingSouth -> UpSouth + isFacingUp -> UpNorth + isFacingSouth -> DownSouth + else -> DownNorth + } + else -> if (isFacingSouth) South else North + } + } + } +} + +// North and south take priority at borders. Same rule applies with up and down over horizontal directions +val northYawRanges = listOf(-180.0..-135.0, 135.0..180.0) +val southYawRange = -45.0..45.0 +val eastYawRange = -134.99..-45.01 +val westYawRange = 45.01..134.99 \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt index cfc563963..9a84ea68f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt @@ -31,7 +31,7 @@ enum class PointSelection(val select: (MutableList Optimum({ hits -> val optimum = hits .map { it.hit.pos } - .reduceOrNull { acc, pos -> acc.add(pos) } + .reduceOrNull { acc, pos -> acc?.add(pos) } ?.times(1 / hits.size) optimum?.let { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index 42772bc9a..5b68132fe 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -43,7 +43,7 @@ data class RotationTarget( } val angleDistance get() = runSafe { - targetRotation.value?.dist(RotationManager.activeRotation) + targetRotation.value?.dist(RotationManager.serverRotation) } ?: 1000.0 /** diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index e21fc7ae7..241010b3c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -26,6 +26,7 @@ import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.ALL_SIDES import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.findRotation import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.extension.rotation import com.lambda.util.world.raycast.InteractionMask import net.minecraft.entity.LivingEntity import net.minecraft.util.hit.BlockHitResult @@ -50,6 +51,18 @@ fun lookAt(angle: Rotation, maxAngleDistance: Double = 10.0) = RotationManager.activeRotation dist angle < maxAngleDistance }) { angle } +@RotationDsl +fun lookInDirection(direction: PlaceDirection) = + RotationTarget(null, { + PlaceDirection.fromRotation(RotationManager.activeRotation) == direction + }) { + if (!direction.isInArea(RotationManager.activeRotation) || !direction.isInArea(player.rotation)) { + direction.snapToArea(RotationManager.activeRotation) + } else { + player.rotation + } + } + /** * Creates a [RotationTarget] based on a requested hit, but doesn't build the rotation. * diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 96b6d5556..6f09a1a62 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -94,7 +94,11 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.item.SwordItem import net.minecraft.registry.tag.FluidTags -import net.minecraft.util.math.* +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.EightWayDirection +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i import net.minecraft.world.BlockView object BlockUtils { diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index b07dfed41..2d6056e92 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -2,8 +2,6 @@ package com.lambda.util.player import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.yaw import com.mojang.authlib.GameProfile import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.network.OtherClientPlayerEntity @@ -12,7 +10,6 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket import net.minecraft.util.Hand -import net.minecraft.util.math.Direction val SafeContext.gamemode get() = interaction.currentGameMode @@ -68,14 +65,3 @@ fun SafeContext.swingHandClient(hand: Hand) { } fun SafeContext.isItemOnCooldown(item: Item) = player.itemCooldownManager.isCoolingDown(item) - -val placementRotations = Direction.entries - .filter { it.axis.isHorizontal } - .flatMap { dir -> - val yaw = dir.yaw - listOf( - Rotation(yaw, 0f), - Rotation(yaw, -90f), - Rotation(yaw, 90f) - ) - } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 7029f6c64..8950d4a23 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -36,6 +36,7 @@ accessible method net/minecraft/entity/LivingEntity modifyAppliedDamage (Lnet/mi accessible method net/minecraft/entity/LivingEntity applyArmorToDamage (Lnet/minecraft/entity/damage/DamageSource;F)F accessible method net/minecraft/entity/LivingEntity getHandSwingDuration ()I accessible method net/minecraft/client/network/ClientPlayerInteractionManager syncSelectedSlot ()V +accessible method net/minecraft/util/math/Direction listClosest (Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/Direction;)[Lnet/minecraft/util/math/Direction; # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V From e9db1aa5d96b6c40bc6aef1859ccba1789cd96be Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 29 Apr 2025 19:38:03 +0100 Subject: [PATCH 154/364] locks to yaw boundaries however pitch is still hard coded to 90, 0, -90 --- .../interaction/request/rotation/Rotation.kt | 13 +++-- .../rotation/visibilty/PlaceDirection.kt | 51 +++++++++---------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt index f1d28c2c3..cebf10397 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt @@ -29,7 +29,6 @@ import com.lambda.util.world.raycast.RayCastUtils.rayCast import net.minecraft.entity.Entity import net.minecraft.util.math.Box import net.minecraft.util.math.Direction -import net.minecraft.util.math.MathHelper import net.minecraft.util.math.MathHelper.wrapDegrees import net.minecraft.util.math.Vec3d import kotlin.math.abs @@ -96,17 +95,17 @@ data class Rotation(val yaw: Double, val pitch: Double) { val UP = Rotation(0.0, -90.0) val Direction.rotation get() = Rotation(yaw.toDouble(), 0.0) var Entity.rotation - get() = Rotation(wrapDegrees(yaw), wrapDegrees(pitch)) + get() = Rotation(yaw, pitch) set(value) { yaw = value.yawF pitch = value.pitchF } - fun wrap(deg: Double) = MathHelper.wrapDegrees(deg) + fun wrap(deg: Double) = wrapDegrees(deg) fun Rotation.lerp(other: Rotation, delta: Double): Rotation { - val yaw = this.yaw + delta * (other.yaw - this.yaw) - val pitch = this.pitch + delta * (other.pitch - this.pitch) + val yaw = wrap(this.yaw + delta * (other.yaw - this.yaw)) + val pitch = wrap(this.pitch + delta * (other.pitch - this.pitch)) return Rotation(yaw, pitch) } @@ -119,8 +118,8 @@ data class Rotation(val yaw: Double, val pitch: Double) { val yawSpeed = abs(yawDiff / diff) * speed val pitchSpeed = abs(pitchDiff / diff) * speed - val yaw = yaw + yawDiff.coerceIn(-yawSpeed, yawSpeed) - val pitch = pitch + pitchDiff.coerceIn(-pitchSpeed, pitchSpeed) + val yaw = wrap(yaw + yawDiff.coerceIn(-yawSpeed, yawSpeed)) + val pitch = wrap(pitch + pitchDiff.coerceIn(-pitchSpeed, pitchSpeed)) return Rotation(yaw, pitch) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index ee4f42c2c..f5b470c8f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -18,10 +18,10 @@ package com.lambda.interaction.request.rotation.visibilty import com.lambda.interaction.request.rotation.Rotation -import com.lambda.util.Communication.info import net.minecraft.entity.Entity import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.MathHelper.wrapDegrees import net.minecraft.util.math.Vec3i enum class PlaceDirection( @@ -33,37 +33,35 @@ enum class PlaceDirection( Down ( 0.0, 90.0, 0, -1, 0, listOf(Double.MIN_VALUE..Double.MAX_VALUE)), UpNorth ( -180.0, -90.0, 0, 1, -1, northYawRanges), - UpSouth ( 0.0, -90.0, 0, 1, 1, listOf(southYawRange)), - UpWest ( 90.0, -90.0, 1, 1, 0, listOf(westYawRange)), - UpEast ( -90.0, -90.0, -1, 1, 0, listOf(eastYawRange)), + UpSouth ( 0.0, -90.0, 0, 1, 1, listOf(southYawRange)), + UpWest ( 90.0, -90.0, 1, 1, 0, listOf(westYawRange)), + UpEast ( -90.0, -90.0, -1, 1, 0, listOf(eastYawRange)), DownNorth( -180.0, 90.0, 0, -1, -1, northYawRanges), - DownSouth( 0.0, 90.0, 0, -1, 1, listOf(southYawRange)), - DownWest ( 90.0, 90.0, 1, -1, 0, listOf(westYawRange)), - DownEast ( -90.0, 90.0, -1, -1, 0, listOf(eastYawRange)), + DownSouth( 0.0, 90.0, 0, -1, 1, listOf(southYawRange)), + DownWest ( 90.0, 90.0, 1, -1, 0, listOf(westYawRange)), + DownEast ( -90.0, 90.0, -1, -1, 0, listOf(eastYawRange)), North ( -180.0, 0.0, 0, 0, -1, northYawRanges), - South ( 0.0, 0.0, 0, 0, 1, listOf(southYawRange)), - West ( 90.0, 0.0, 1, 0, 0, listOf(westYawRange)), - East ( -90.0, 0.0, -1, 0, 0, listOf(eastYawRange)); + South ( 0.0, 0.0, 0, 0, 1, listOf(southYawRange)), + West ( 90.0, 0.0, 1, 0, 0, listOf(westYawRange)), + East ( -90.0, 0.0, -1, 0, 0, listOf(eastYawRange)); constructor(yaw: Double, pitch: Double, x: Int, y: Int, z: Int, yawRanges: List>) : this(Rotation(yaw, pitch), Vec3i(x, y, z), yawRanges) + //ToDo: Add dynamic pitch border calculations to avoid excess rotation distance fun snapToArea(rot: Rotation): Rotation { -// if (isInArea(rot)) return rot + if (isInArea(rot)) return rot - //ToDo: fix snapping to a given directions area to speed up rotations in the case they are not instant -// val normalizedYaw = wrapDegrees(rot.yaw) -// val clampedYaw = when { -// this.rotation.yaw != 180.0 -> normalizedYaw.coerceIn(yawRanges[0]) -// normalizedYaw < 0 -> normalizedYaw.coerceIn(yawRanges[0]) -// else -> normalizedYaw.coerceIn(yawRanges[1]) -// } - - info("$rotation") + val normalizedYaw = wrapDegrees(rot.yaw) + val clampedYaw = when { + this.rotation.yaw != -180.0 -> normalizedYaw.coerceIn(yawRanges[0]) + normalizedYaw < 0 -> normalizedYaw.coerceIn(yawRanges[0]) + else -> normalizedYaw.coerceIn(yawRanges[1]) + } - return Rotation(rotation.yaw, rotation.pitch) + return Rotation(clampedYaw, this.rotation.pitch) } fun isInArea(rot: Rotation) = fromRotation(rot) == this @@ -117,8 +115,9 @@ enum class PlaceDirection( } } -// North and south take priority at borders. Same rule applies with up and down over horizontal directions -val northYawRanges = listOf(-180.0..-135.0, 135.0..180.0) -val southYawRange = -45.0..45.0 -val eastYawRange = -134.99..-45.01 -val westYawRange = 45.01..134.99 \ No newline at end of file +const val FUDGE_FACTOR = 0.01 + +val northYawRanges = listOf(-180.0..(-135.0 - FUDGE_FACTOR), (135.0 + FUDGE_FACTOR)..180.0) +val southYawRange = ( -45.0 + FUDGE_FACTOR)..( 45.0 - FUDGE_FACTOR) +val eastYawRange = (-135.0 + FUDGE_FACTOR)..(-45.0 - FUDGE_FACTOR) +val westYawRange = ( 45.0 + FUDGE_FACTOR)..(135.0 - FUDGE_FACTOR) \ No newline at end of file From 93c58319d8d5fa75e5c321ac418367bfb08b55e2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 1 May 2025 13:57:01 +0100 Subject: [PATCH 155/364] test players rotation before simulating axis rotations --- .../interaction/construction/context/PlaceContext.kt | 4 ++-- .../construction/simulation/BuildSimulator.kt | 10 ++++++++-- .../lambda/interaction/request/placing/PlaceConfig.kt | 5 +---- .../src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 +--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 9e9eb8423..cd87f247c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -78,11 +78,11 @@ data class PlaceContext( withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) } - override fun shouldRotate(config: BuildConfig) = config.placing.rotate + override fun shouldRotate(config: BuildConfig) = config.placing.rotateForPlace fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) - val validRotation = if (request.build.placing.rotate) { + val validRotation = if (request.build.placing.rotateForPlace) { request.rotation.request(rotation, false).done && !currentDirIsInvalid } else true return hotbarRequest.done && validRotation diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 4864e2483..cde372b92 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -295,7 +295,7 @@ object BuildSimulator { } lateinit var resultState: BlockState - var rot = RotationManager.serverRotation + var rot = fakePlayer.rotation val simulatePlaceState = placeState@ { resultState = blockItem.getPlacementState(context) @@ -311,7 +311,7 @@ object BuildSimulator { } val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> - if (!place.rotate) { + if (!place.rotateForPlace) { acc.add(basePlaceResult) return@forEach } @@ -329,6 +329,12 @@ object BuildSimulator { return@rotate } + fakePlayer.rotation = player.rotation + simulatePlaceState() ?: run { + rot = fakePlayer.rotation + return@rotate + } + if (currentDirIsInvalid) { PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> fakePlayer.rotation = direction.rotation diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 2a7c028ac..66571b7db 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.TickStage import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -27,12 +26,10 @@ abstract class PlaceConfig( priority: Priority ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean - val rotate - get() = rotateForPlace || axisRotate abstract val airPlace: AirPlaceMode protected abstract val axisRotateSetting: Boolean val axisRotate - get() = airPlace.isEnabled() && axisRotateSetting + get() = rotateForPlace && airPlace.isEnabled() && axisRotateSetting abstract val placeStageMask: Set abstract val placeConfirmationMode: PlaceConfirmationMode abstract val maxPendingPlacements: Int diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 94ebd445a..abd3f5ce2 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -100,7 +100,6 @@ class BuildTask @Ta5kBuilder constructor( TaskFlowModule.drawables = results .filterIsInstance() .plus(pendingInteractions.toList()) - .toMutableList() val resultsNotBlocked = results .filter { result -> pendingInteractions.none { it.expectedPos == result.blockPos } } @@ -143,10 +142,9 @@ class BuildTask @Ta5kBuilder constructor( val requestContexts = arrayListOf() if (build.breaking.breaksPerTick > 1) { - val take = emptyPendingInteractionSlots.coerceAtLeast(0) breakResults .filter { it.context.instantBreak } - .take(take) + .take(emptyPendingInteractionSlots) .let { instantBreakResults -> requestContexts.addAll(instantBreakResults.map { it.context }) } From 240533e72c9afdf95d5ebdd3a67aadb25e0f6bf0 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 1 May 2025 17:36:46 +0100 Subject: [PATCH 156/364] cancel on tick post if not updated --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 67f547586..63ceb8a01 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -107,6 +107,10 @@ object BreakManager : RequestHandler( breakInfos.forEach { info -> info?.apply { if (isRedundant) updateBreakProgress(this) + else if (!updatedThisTick) { + this.cancelBreak() + return@apply + } activeAge++ updatedThisTick = false updatedProgressThisTick = false From d1d65688e1c74ce6aeb8fa03fd0e607b0b08ac6b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 1 May 2025 17:46:04 +0100 Subject: [PATCH 157/364] fixed tick delay on break manager requests in PacketMine --- .../module/modules/player/PacketMine.kt | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index a0e895596..7e2b1bd20 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -59,9 +59,14 @@ object PacketMine : Module( private var itemDrops = 0 private val breakingPositions = arrayOfNulls(2) - private var hitPos: BlockPos? = null + + private var requestedThisTick = false init { + listen { + requestedThisTick = false + } + listen { it.cancel() } listen { event -> event.cancel() @@ -70,24 +75,29 @@ object PacketMine : Module( breakingPositions[1] = breakingPositions[0] } breakingPositions[0] = null - hitPos = event.pos + sendBreakRequest(event.pos) } - listen { - val requestPositions = arrayListOf().apply { addAll(breakingPositions.filterNotNull()) } - hitPos?.let { pos -> - requestPositions.add(pos) - hitPos = null - } + listen { + if (!requestedThisTick) sendBreakRequest() + } + } - val request = BreakRequest( - breakContexts(requestPositions), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, onAccept = { breakingPositions[0] = it }, - onCancel = { nullifyBreakPos(it) }, - onBreak = { breaks++; nullifyBreakPos(it) }, - { _ -> itemDrops++ } - ) - breakConfig.request(request) + private fun SafeContext.sendBreakRequest(hitPos: BlockPos? = null) { + val requestPositions = arrayListOf(*breakingPositions.filterNotNull().toTypedArray()) + hitPos?.let { pos -> + requestPositions.add(pos) } + + val request = BreakRequest( + breakContexts(requestPositions), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, + onAccept = { breakingPositions[0] = it }, + onCancel = { nullifyBreakPos(it) }, + onBreak = { breaks++; nullifyBreakPos(it) }, + { _ -> itemDrops++ } + ) + breakConfig.request(request) + requestedThisTick = true } private fun nullifyBreakPos(pos: BlockPos) { From 7c8f3f361984b5003f54a53031db17aa6068b70a Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 2 May 2025 21:13:20 +0200 Subject: [PATCH 158/364] Added inversed rotation method with unit tests --- .../rotation/visibilty/PlaceDirection.kt | 55 ++++- common/src/test/kotlin/PlaceDirectionTest.kt | 119 +++++++++++ common/src/test/kotlin/RotationTest.kt | 194 ++++++++++++++++++ 3 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 common/src/test/kotlin/PlaceDirectionTest.kt create mode 100644 common/src/test/kotlin/RotationTest.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index f5b470c8f..9492dd0bf 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -23,6 +23,10 @@ import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper import net.minecraft.util.math.MathHelper.wrapDegrees import net.minecraft.util.math.Vec3i +import kotlin.math.abs +import kotlin.math.atan +import kotlin.math.cos +import kotlin.math.sin enum class PlaceDirection( val rotation: Rotation, @@ -50,20 +54,63 @@ enum class PlaceDirection( constructor(yaw: Double, pitch: Double, x: Int, y: Int, z: Int, yawRanges: List>) : this(Rotation(yaw, pitch), Vec3i(x, y, z), yawRanges) - //ToDo: Add dynamic pitch border calculations to avoid excess rotation distance fun snapToArea(rot: Rotation): Rotation { if (isInArea(rot)) return rot val normalizedYaw = wrapDegrees(rot.yaw) val clampedYaw = when { - this.rotation.yaw != -180.0 -> normalizedYaw.coerceIn(yawRanges[0]) + rotation.yaw != -180.0 -> normalizedYaw.coerceIn(yawRanges[0]) normalizedYaw < 0 -> normalizedYaw.coerceIn(yawRanges[0]) else -> normalizedYaw.coerceIn(yawRanges[1]) } - return Rotation(clampedYaw, this.rotation.pitch) + // Calculate pitch boundaries based on the snapped yaw + val snappedYawRad = Math.toRadians(clampedYaw) + val pitchBoundaryEW = Math.toDegrees(atan(abs(sin(snappedYawRad)))) + val pitchBoundaryNS = Math.toDegrees(atan(abs(cos(snappedYawRad)))) + + // Determine the correct pitch boundary and snap pitch + val snappedPitch = when { + // Primary E/W Directions + isEast() || isWest() -> { + when { + isUp() -> pitchBoundaryEW // Snap to lower edge of UP_E/UP_W area + isDown() -> -pitchBoundaryEW // Snap to upper edge of DOWN_E/DOWN_W area + else -> { // Horizontal E/W + val isPitchWithinBounds = abs(rot.pitch - pitchBoundaryEW) < abs(rot.pitch - (-pitchBoundaryEW)) + if (isPitchWithinBounds) pitchBoundaryEW else -pitchBoundaryEW + } + } + } + // Primary N/S Directions + isNorth() || isSouth() -> { + when { + isUp() -> pitchBoundaryNS // Snap to lower edge of UP_N/UP_S area + isDown() -> -pitchBoundaryNS // Snap to upper edge of DOWN_N/DOWN_S area + else -> { // Horizontal N/S + val isWithinNorthernBoundary = abs(rot.pitch - pitchBoundaryNS) < abs(rot.pitch - (-pitchBoundaryNS)) + if (isWithinNorthernBoundary) pitchBoundaryNS else -pitchBoundaryNS + } + } + } + // Handle purely UP/DOWN directions + else -> rotation.pitch + } + + // Clamp pitch to valid range + val clampedPitch = snappedPitch.coerceIn(-90.0, 90.0) + + return Rotation(clampedYaw, clampedPitch) } + // Helper functions to determine direction type + private fun isEast(): Boolean = this == East || this == UpEast || this == DownEast + private fun isWest(): Boolean = this == West || this == UpWest || this == DownWest + private fun isNorth(): Boolean = this == North || this == UpNorth || this == DownNorth + private fun isSouth(): Boolean = this == South || this == UpSouth || this == DownSouth + private fun isUp(): Boolean = this == UpEast || this == UpWest || this == UpNorth || this == UpSouth || this == Up + private fun isDown(): Boolean = this == DownEast || this == DownWest || this == DownNorth || this == DownSouth || this == Down + fun isInArea(rot: Rotation) = fromRotation(rot) == this companion object { @@ -120,4 +167,4 @@ const val FUDGE_FACTOR = 0.01 val northYawRanges = listOf(-180.0..(-135.0 - FUDGE_FACTOR), (135.0 + FUDGE_FACTOR)..180.0) val southYawRange = ( -45.0 + FUDGE_FACTOR)..( 45.0 - FUDGE_FACTOR) val eastYawRange = (-135.0 + FUDGE_FACTOR)..(-45.0 - FUDGE_FACTOR) -val westYawRange = ( 45.0 + FUDGE_FACTOR)..(135.0 - FUDGE_FACTOR) \ No newline at end of file +val westYawRange = ( 45.0 + FUDGE_FACTOR)..(135.0 - FUDGE_FACTOR) diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt new file mode 100644 index 000000000..e10a45dcf --- /dev/null +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.visibilty.PlaceDirection +import kotlin.math.abs +import kotlin.math.atan +import kotlin.math.cos +import kotlin.math.sin +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PlaceDirectionTest { + + @Test + fun `test pitch snapping for East direction`() { + val direction = PlaceDirection.East + + // Calculate expected pitch boundary for East at yaw -90.0 + val yawRad = Math.toRadians(-90.0) + val expectedBoundary = Math.toDegrees(atan(abs(sin(yawRad)))) + + // Test rotation outside the area (pitch too high) + val rotationOutsideHigh = Rotation(-90.0, expectedBoundary + 10.0) + val snappedHigh = direction.snapToArea(rotationOutsideHigh) + + // Test rotation outside the area (pitch too low) + val rotationOutsideLow = Rotation(-90.0, -expectedBoundary - 10.0) + val snappedLow = direction.snapToArea(rotationOutsideLow) + + // Verify that the pitch is snapped to the boundary + assertEquals(expectedBoundary, snappedHigh.pitch, 0.001, "Pitch should be snapped to the upper boundary") + assertEquals(-expectedBoundary, snappedLow.pitch, 0.001, "Pitch should be snapped to the lower boundary") + } + + @Test + fun `test pitch snapping for North direction`() { + val direction = PlaceDirection.North + + // Calculate expected pitch boundary for North at yaw -180.0 + val yawRad = Math.toRadians(-180.0) + val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad)))) + + // Test rotation outside the area (pitch too high) + val rotationOutsideHigh = Rotation(-180.0, expectedBoundary + 10.0) + val snappedHigh = direction.snapToArea(rotationOutsideHigh) + + // Test rotation outside the area (pitch too low) + val rotationOutsideLow = Rotation(-180.0, -expectedBoundary - 10.0) + val snappedLow = direction.snapToArea(rotationOutsideLow) + + // Verify that the pitch is snapped to the boundary + assertEquals(expectedBoundary, snappedHigh.pitch, 0.001, "Pitch should be snapped to the upper boundary") + assertEquals(-expectedBoundary, snappedLow.pitch, 0.001, "Pitch should be snapped to the lower boundary") + } + + @Test + fun `test pitch snapping for UpEast direction`() { + val direction = PlaceDirection.UpEast + + // Calculate expected pitch boundary for UpEast at yaw -90.0 + val yawRad = Math.toRadians(-90.0) + val expectedBoundary = Math.toDegrees(atan(abs(sin(yawRad)))) + + // Test rotation outside the area (pitch too low) + val rotationOutside = Rotation(-90.0, expectedBoundary - 10.0) + val snapped = direction.snapToArea(rotationOutside) + + // Verify that the pitch is snapped to the boundary + assertEquals(expectedBoundary, snapped.pitch, 0.001, "Pitch should be snapped to the boundary") + } + + @Test + fun `test pitch snapping for DownNorth direction`() { + val direction = PlaceDirection.DownNorth + + // Calculate expected pitch boundary for DownNorth at yaw -180.0 + val yawRad = Math.toRadians(-180.0) + val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad)))) + + // Test rotation outside the area (pitch too high) + val rotationOutside = Rotation(-180.0, -expectedBoundary + 10.0) + val snapped = direction.snapToArea(rotationOutside) + + // Verify that the pitch is snapped to the boundary + assertEquals(-expectedBoundary, snapped.pitch, 0.001, "Pitch should be snapped to the boundary") + } + + @Test + fun `test no snapping when rotation is already in area`() { + val direction = PlaceDirection.East + + // Create a rotation that should be in the East area + val rotation = Rotation(-90.0, 0.0) + + // Verify that the rotation is in the area + assertTrue(direction.isInArea(rotation), "Rotation should be in the East area") + + // Verify that snapToArea returns the same rotation + val snapped = direction.snapToArea(rotation) + assertEquals(rotation.yaw, snapped.yaw, 0.001, "Yaw should not change") + assertEquals(rotation.pitch, snapped.pitch, 0.001, "Pitch should not change") + } +} \ No newline at end of file diff --git a/common/src/test/kotlin/RotationTest.kt b/common/src/test/kotlin/RotationTest.kt new file mode 100644 index 000000000..6e48b552b --- /dev/null +++ b/common/src/test/kotlin/RotationTest.kt @@ -0,0 +1,194 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.rotation + +import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifference +import com.lambda.interaction.request.rotation.Rotation.Companion.dist +import com.lambda.interaction.request.rotation.Rotation.Companion.lerp +import com.lambda.interaction.request.rotation.Rotation.Companion.slerp +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.assertFalse +import kotlin.math.abs +import kotlin.math.hypot +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.MathHelper + +/** + * Tests for the Rotation class + * Note: Tests only cover methods that don't require SafeContext or Minecraft classes that can't be mocked easily + */ +class RotationTest { + + @Test + fun `test constructors and basic properties`() { + // Test double constructor + val rotation1 = Rotation(45.0, 30.0) + assertEquals(45.0, rotation1.yaw) + assertEquals(30.0, rotation1.pitch) + + // Test float constructor + val rotation2 = Rotation(45f, 30f) + assertEquals(45.0, rotation2.yaw) + assertEquals(30.0, rotation2.pitch) + + // Test yawF and pitchF + assertEquals(45f, rotation1.yawF) + assertEquals(30f, rotation1.pitchF) + + // Test float property + val floatArray = rotation1.float + assertEquals(2, floatArray.size) + assertEquals(45f, floatArray[0]) + assertEquals(30f, floatArray[1]) + } + + @Test + fun `test equalFloat method`() { + val rotation1 = Rotation(45.0, 30.0) + val rotation2 = Rotation(45.0, 30.0) + val rotation3 = Rotation(45.0, 31.0) + val rotation4 = Rotation(46.0, 30.0) + + assertTrue(rotation1.equalFloat(rotation2)) + assertFalse(rotation1.equalFloat(rotation3)) + assertFalse(rotation1.equalFloat(rotation4)) + } + + @Test + fun `test withDelta method`() { + val rotation = Rotation(45.0, 30.0) + + // Test with both yaw and pitch delta + val result1 = rotation.withDelta(10.0, 20.0) + assertEquals(55.0, result1.yaw) + assertEquals(50.0, result1.pitch) + + // Test with only yaw delta + val result2 = rotation.withDelta(10.0) + assertEquals(55.0, result2.yaw) + assertEquals(30.0, result2.pitch) + + // Test with only pitch delta + val result3 = rotation.withDelta(pitch = 20.0) + assertEquals(45.0, result3.yaw) + assertEquals(50.0, result3.pitch) + + // Test pitch clamping (upper bound) + val result4 = rotation.withDelta(pitch = 70.0) + assertEquals(45.0, result4.yaw) + assertEquals(90.0, result4.pitch) // Clamped to 90.0 + + // Test pitch clamping (lower bound) + val result5 = rotation.withDelta(pitch = -130.0) + assertEquals(45.0, result5.yaw) + assertEquals(-90.0, result5.pitch) // Clamped to -90.0 + } + + @Test + fun `test companion object constants`() { + assertEquals(0.0, Rotation.ZERO.yaw) + assertEquals(0.0, Rotation.ZERO.pitch) + + assertEquals(0.0, Rotation.DOWN.yaw) + assertEquals(90.0, Rotation.DOWN.pitch) + + assertEquals(0.0, Rotation.UP.yaw) + assertEquals(-90.0, Rotation.UP.pitch) + } + + @Test + fun `test lerp method`() { + val rotation1 = Rotation(0.0, 0.0) + val rotation2 = Rotation(90.0, 45.0) + + // Test with delta = 0 (should return rotation1) + val result1 = rotation1.lerp(rotation2, 0.0) + assertEquals(rotation1.yaw, result1.yaw, 0.001) + assertEquals(rotation1.pitch, result1.pitch, 0.001) + + // Test with delta = 1 (should return rotation2) + val result2 = rotation1.lerp(rotation2, 1.0) + assertEquals(rotation2.yaw, result2.yaw, 0.001) + assertEquals(rotation2.pitch, result2.pitch, 0.001) + + // Test with delta = 0.5 (should return midpoint) + val result3 = rotation1.lerp(rotation2, 0.5) + assertEquals(45.0, result3.yaw, 0.001) + assertEquals(22.5, result3.pitch, 0.001) + } + + @Test + fun `test slerp method`() { + val rotation1 = Rotation(0.0, 0.0) + val rotation2 = Rotation(90.0, 45.0) + + // Test with speed = 0 (should return rotation1) + val result1 = rotation1.slerp(rotation2, 0.0) + assertEquals(rotation1.yaw, result1.yaw, 0.001) + assertEquals(rotation1.pitch, result1.pitch, 0.001) + + // Test with very high speed (should return rotation2) + val result2 = rotation1.slerp(rotation2, 1000.0) + assertEquals(rotation2.yaw, result2.yaw, 0.001) + assertEquals(rotation2.pitch, result2.pitch, 0.001) + + // Test with limited speed + val result3 = rotation1.slerp(rotation2, 10.0) + assertTrue(result3.yaw > rotation1.yaw) + assertTrue(result3.pitch > rotation1.pitch) + assertTrue(result3.yaw < rotation2.yaw) + assertTrue(result3.pitch < rotation2.pitch) + } + + @Test + fun `test dist method`() { + val rotation1 = Rotation(0.0, 0.0) + val rotation2 = Rotation(90.0, 0.0) + val rotation3 = Rotation(0.0, 90.0) + val rotation4 = Rotation(90.0, 90.0) + + // Test distance between same rotations + assertEquals(0.0, rotation1 dist rotation1, 0.001) + + // Test distance with only yaw difference + assertEquals(90.0, rotation1 dist rotation2, 0.001) + + // Test distance with only pitch difference + assertEquals(90.0, rotation1 dist rotation3, 0.001) + + // Test distance with both yaw and pitch difference + assertEquals(hypot(90.0, 90.0), rotation1 dist rotation4, 0.001) + } + + @Test + fun `test angleDifference method`() { + // Test with angles in the same direction + assertEquals(10.0, angleDifference(10.0, 0.0), 0.001) + assertEquals(10.0, angleDifference(0.0, 10.0), 0.001) + + // Test with angles in opposite directions + assertEquals(20.0, angleDifference(-10.0, 10.0), 0.001) + assertEquals(20.0, angleDifference(10.0, -10.0), 0.001) + + // Test with angles that wrap around + assertEquals(20.0, angleDifference(170.0, -170.0), 0.001) + assertEquals(20.0, angleDifference(-170.0, 170.0), 0.001) + } +} From 4492a04d08a2afa51d1284ec04d2a491a2221c2b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 2 May 2025 20:17:47 +0100 Subject: [PATCH 159/364] start to re break implementation. Need to fix issue with managers calling other managers in onOpen before they have opened --- .../com/lambda/config/groups/BreakSettings.kt | 1 + .../lambda/config/groups/ReBreakSettings.kt | 36 +++++++++ .../com/lambda/event/events/TickEvent.kt | 30 +++---- .../request/breaking/BreakConfig.kt | 2 + .../interaction/request/breaking/BreakInfo.kt | 9 ++- .../request/breaking/BreakManager.kt | 53 +++++++----- .../request/breaking/BrokenBlockHandler.kt | 81 ++++++++++++------- .../request/breaking/ReBreakManager.kt | 81 +++++++++++++++++++ .../request/breaking/ReBreakResult.kt | 28 +++++++ 9 files changed, 258 insertions(+), 63 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakResult.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index a24e13e4f..5532f12af 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -29,6 +29,7 @@ class BreakSettings( vis: () -> Boolean = { true } ) : BreakConfig(priority) { override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() } + override val reBreak = ReBreakSettings(c, vis) override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt new file mode 100644 index 000000000..c5474f39f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.config.groups + +import com.lambda.config.Configurable + +class ReBreakSettings( + c: Configurable, + vis: () -> Boolean = { true } +) { + val mode by c.setting("ReBreak Mode", Mode.Manual, "The method used to re-break blocks after they've been broken once", vis) + + enum class Mode { + None, + Manual, + Auto, + AutoConstant; + + fun isEnabled() = this != None + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt b/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt index 629f5564c..473645894 100644 --- a/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt @@ -37,7 +37,7 @@ import com.lambda.event.Event * * @see net.minecraft.client.MinecraftClient.tick */ -sealed class TickEvent { +sealed class TickEvent : Event { /** * Triggered before each iteration of the game loop. * @@ -47,12 +47,12 @@ sealed class TickEvent { * 2. Steps world tick manager * 3. Decrements item use cooldown */ - data object Pre : Event + data object Pre : TickEvent() /** * Triggered after each iteration of the game loop. */ - data object Post : Event + data object Post : TickEvent() /** * Triggered during the network tick stage only @@ -69,8 +69,8 @@ sealed class TickEvent { * @see net.minecraft.client.network.ClientPlayerInteractionManager.tick */ sealed class Network { - data object Pre : Event - data object Post : Event + data object Pre : TickEvent() + data object Post : TickEvent() } /** @@ -87,8 +87,8 @@ sealed class TickEvent { * @see net.minecraft.client.MinecraftClient.handleInputEvents */ sealed class Input { - data object Pre : Event - data object Post : Event + data object Pre : TickEvent() + data object Post : TickEvent() } /** @@ -97,8 +97,8 @@ sealed class TickEvent { * @see net.minecraft.client.render.WorldRenderer.tick */ sealed class WorldRender { - data object Pre : Event - data object Post : Event + data object Pre : TickEvent() + data object Post : TickEvent() } /** @@ -107,8 +107,8 @@ sealed class TickEvent { * @see net.minecraft.client.sound.SoundManager.tick */ sealed class Sound { - data object Pre : Event - data object Post : Event + data object Pre : TickEvent() + data object Post : TickEvent() } /** @@ -128,12 +128,12 @@ sealed class TickEvent { /** * Triggered before each render tick ([TickEvent.Render]) of the game loop. */ - data object Pre : Event + data object Pre : TickEvent() /** * Triggered after each render tick ([TickEvent.Render]) of the game loop. */ - data object Post : Event + data object Post : TickEvent() } /** @@ -152,11 +152,11 @@ sealed class TickEvent { /** * Triggered before each player tick ([TickEvent.Player]). */ - data object Pre : Event + data object Pre : TickEvent() /** * Triggered after each player tick ([TickEvent.Player]). */ - data object Post : Event + data object Post : TickEvent() } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 31d304271..61bb62a99 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.ReBreakSettings import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -27,6 +28,7 @@ abstract class BreakConfig( priority: Priority = 0 ) : RequestConfig(priority) { abstract val breakMode: BreakMode + abstract val reBreak: ReBreakSettings abstract val unsafeCancels: Boolean abstract val breakThreshold: Float abstract val doubleBreak: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index b6c0a5978..85967a568 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -46,6 +46,7 @@ data class BreakInfo( val isPrimary get() = type == BreakType.Primary val isSecondary get() = type == BreakType.Secondary val isRedundant get() = type == BreakType.RedundantSecondary + val isReBreaking get() = type == BreakType.ReBreak @Volatile var broken = false; private set @@ -84,6 +85,11 @@ data class BreakInfo( } } + fun resetCallbacks() { + broken = false + item = null + } + fun setBreakingTextureStage( player: ClientPlayerEntity, world: ClientWorld, @@ -123,7 +129,8 @@ data class BreakInfo( enum class BreakType(val index: Int) { Primary(0), Secondary(1), - RedundantSecondary(2); + RedundantSecondary(2), + ReBreak(2); fun getBreakThreshold(breakConfig: BreakConfig) = when (this) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 63ceb8a01..2c9195df3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -28,7 +28,6 @@ import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler @@ -37,7 +36,10 @@ import com.lambda.interaction.request.breaking.BreakConfig.BreakMode import com.lambda.interaction.request.breaking.BreakManager.activeRequest import com.lambda.interaction.request.breaking.BreakManager.processRequest import com.lambda.interaction.request.breaking.BreakType.Primary +import com.lambda.interaction.request.breaking.BreakType.ReBreak +import com.lambda.interaction.request.breaking.BrokenBlockHandler.brokenState import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock +import com.lambda.interaction.request.breaking.BrokenBlockHandler.isBroken import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingBreaks import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending @@ -50,7 +52,6 @@ import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.gamemode import com.lambda.util.player.swingHand -import net.minecraft.block.BlockState import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.entity.ItemEntity @@ -85,12 +86,14 @@ object BreakManager : RequestHandler( private val rotated get() = rotationRequest?.done != false private var breakCooldown = 0 - private var breaksThisTick = 0 + var breaksThisTick = 0 private var maxBreaksThisTick = 0 private var breaks = mutableListOf() private var instantBreaks = mutableListOf() + var lastPosStarted: BlockPos? = null + fun Any.onBreak( alwaysListen: Boolean = false, priority: Priority = 0, @@ -126,7 +129,8 @@ object BreakManager : RequestHandler( .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> // if not broken - if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) { + if (!isBroken(info.context.checkedState, event.newState)) { + this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.checkedState.brokenState}") // update the checked state info.context.checkedState = event.newState return@listen @@ -135,6 +139,8 @@ object BreakManager : RequestHandler( info.internalOnBreak() if (!info.callbacksCompleted) { info.startPending() + } else if (info.isPrimary) { + ReBreakManager.startReBreak(info) } info.nullify() } @@ -387,6 +393,8 @@ object BreakManager : RequestHandler( info.internalOnBreak() if (!info.callbacksCompleted) { info.startPending() + } else if (info.isPrimary) { + ReBreakManager.startReBreak(info) } } BreakConfirmationMode.BreakThenAwait -> { @@ -442,7 +450,7 @@ object BreakManager : RequestHandler( */ private fun BreakInfo.nullify() { type.nullify() - if (!broken) internalOnCancel() + if (!broken && !isReBreaking && !isRedundant) internalOnCancel() } /** @@ -458,7 +466,8 @@ object BreakManager : RequestHandler( */ private fun BreakType.nullify() = when (this) { - Primary -> primaryBreak = null + Primary, + ReBreak -> primaryBreak = null else -> secondaryBreak = null } @@ -492,10 +501,29 @@ object BreakManager : RequestHandler( } if (!info.breaking) { + when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) { + is ReBreakResult.StillBreaking -> { + primaryBreak = reBreakResult.breakInfo.apply { + type = Primary + ReBreakManager.clearReBreak() + } + + primaryBreak?.let { primary -> + updateBreakProgress(primary) + } + return true + } + is ReBreakResult.ReBroke -> { + info.nullify() + return true + } + else -> {} + } if (!startBreaking(info)) { info.nullify() return false } + ReBreakManager.clearReBreak() val swing = info.breakConfig.swing if (swing.isEnabled() && swing != BreakConfig.SwingMode.End) { swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) @@ -616,6 +644,7 @@ object BreakManager : RequestHandler( if (info.breakConfig.breakMode == BreakMode.Packet) { info.stopBreakPacket(world, interaction) } + lastPosStarted = ctx.expectedPos info.startBreakPacket(world, interaction) if (info.isSecondary || (breakDelta < 1 && breakDelta >= info.breakConfig.breakThreshold)) { info.stopBreakPacket(world, interaction) @@ -633,17 +662,5 @@ object BreakManager : RequestHandler( return inRange && correctMaterial } - /** - * @return if the [newState] matches the [targetState]. - * - * @see TargetState - */ - fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = - if (targetState.matches(newState, pos, world)) true - else { - this@BreakManager.warn("Break at ${pos.toShortString()} was rejected with $newState instead of $targetState") - false - } - override fun preEvent(): Event = UpdateManagerEvent.Break().post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 389e6ca48..f845030a5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -25,14 +25,17 @@ import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode +import com.lambda.interaction.request.breaking.BreakManager.lastPosStarted import com.lambda.interaction.request.breaking.BreakManager.matchesBlockItem -import com.lambda.interaction.request.breaking.BreakManager.matchesTargetState +import com.lambda.interaction.request.breaking.ReBreakManager.reBreak import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.fluidState import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info +import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.player.gamemode +import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.entity.ItemEntity import net.minecraft.util.math.ChunkSectionPos @@ -64,41 +67,53 @@ object BrokenBlockHandler { init { listen(priority = Int.MIN_VALUE + 1) { event -> - pendingBreaks - .firstOrNull { it.context.expectedPos == event.pos } - ?.let { pending -> - // return if the state hasn't changed - if (event.newState.matches(pending.context.checkedState)) - return@listen - - // return if the block's not broken - if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) { - pending.stopPending() - return@listen - } + run { + pendingBreaks.firstOrNull { it.context.expectedPos == event.pos } + ?: if (reBreak?.context?.expectedPos == event.pos) reBreak + else null + }?.let { pending -> + // return if the state hasn't changed + if (event.newState.matches(pending.context.checkedState)) + return@listen - if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { - destroyBlock(pending) - } - pending.internalOnBreak() - if (pending.callbacksCompleted) { - pending.stopPending() + // return if the block's not broken + if (!isBroken(pending.context.checkedState, event.newState)) { + if (!pending.isReBreaking) { + this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.checkedState.brokenState}") } + pending.stopPending() return@listen } + + if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { + destroyBlock(pending) + } + pending.internalOnBreak() + if (pending.callbacksCompleted) { + pending.stopPending() + if (lastPosStarted == pending.context.expectedPos) { + ReBreakManager.startReBreak(pending) + } + } + return@listen + } } listen(priority = Int.MIN_VALUE + 1) { if (it.entity !is ItemEntity) return@listen - pendingBreaks - .firstOrNull { info -> matchesBlockItem(info, it.entity) } - ?.let { pending -> - pending.internalOnItemDrop(it.entity) - if (pending.callbacksCompleted) { - pending.stopPending() + run { + pendingBreaks.firstOrNull { info -> matchesBlockItem(info, it.entity) } + ?: reBreak?.let { info -> + return@run if (matchesBlockItem(info, it.entity)) info + else null } - return@listen + }?.let { pending -> + pending.internalOnItemDrop(it.entity) + if (pending.callbacksCompleted) { + pending.stopPending() } + return@listen + } } listenUnsafe(priority = Int.MIN_VALUE + 1) { @@ -117,9 +132,13 @@ object BrokenBlockHandler { /** * Removes the [info] from the [BrokenBlockHandler], and requesters, pending interaction collections. */ - private fun BreakInfo.stopPending() { - pendingBreaks.remove(this) - pendingInteractions.remove(context) + fun BreakInfo.stopPending() { + if (!isReBreaking) { + pendingBreaks.remove(this) + pendingInteractions.remove(context) + } else { + resetCallbacks() + } } /** @@ -161,4 +180,8 @@ object BrokenBlockHandler { return setState } + + val BlockState.isEmpty get() = matches(fluidState.blockState) + val BlockState.brokenState: BlockState get() = fluidState.blockState + fun isBroken(oldState: BlockState, newState: BlockState) = !oldState.isEmpty && oldState.brokenState.matches(newState) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt new file mode 100644 index 000000000..1fb60a921 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +import com.lambda.config.groups.ReBreakSettings +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock +import com.lambda.interaction.request.breaking.BrokenBlockHandler.isEmpty +import com.lambda.threading.runSafe +import com.lambda.util.player.swingHand +import net.minecraft.util.Hand + +object ReBreakManager { + var reBreak: BreakInfo? = null + + init { + listen(priority = Int.MIN_VALUE) { + reBreak?.apply { + breakingTicks++ + activeAge++ + } + } + } + + fun startReBreak(info: BreakInfo?) { + reBreak = info?.apply { + type = BreakType.ReBreak + } + } + + fun clearReBreak() { + reBreak = null + } + + fun handleUpdate(ctx: BreakContext, breakRequest: BreakRequest) = + runSafe { + val info = reBreak ?: return@runSafe ReBreakResult.Ignored + + if (info.context.expectedPos != ctx.expectedPos || info.breakConfig.reBreak.mode != ReBreakSettings.Mode.Manual) { + return@runSafe ReBreakResult.Ignored + } + info.context = ctx + info.request = breakRequest + + val context = info.context + val awaitThenBreak = info.breakConfig.breakConfirmation == BreakConfig.BreakConfirmationMode.AwaitThenBreak + + val breakProgress = context.checkedState.calcBlockBreakingDelta(player, world, context.expectedPos) + return@runSafe if (info.breakingTicks * breakProgress >= info.breakConfig.breakThreshold) { + if (!context.checkedState.isEmpty && !awaitThenBreak) { + destroyBlock(info) + } + info.stopBreakPacket(world, interaction) + val swing = info.breakConfig.swing + if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) { + swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + } + BreakManager.breaksThisTick++ + ReBreakResult.ReBroke + } else { + ReBreakResult.StillBreaking(info) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakResult.kt new file mode 100644 index 000000000..1a144db89 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakResult.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.breaking + +sealed class ReBreakResult { + data object Ignored : ReBreakResult() + + data object ReBroke : ReBreakResult() + + class StillBreaking( + val breakInfo: BreakInfo + ) : ReBreakResult() +} \ No newline at end of file From 49a5965dca94269ba1e43b76ccfdb7c158074466 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 2 May 2025 21:19:55 +0200 Subject: [PATCH 160/364] Fix package structure --- common/src/test/kotlin/RotationTest.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/common/src/test/kotlin/RotationTest.kt b/common/src/test/kotlin/RotationTest.kt index 6e48b552b..b81a907b3 100644 --- a/common/src/test/kotlin/RotationTest.kt +++ b/common/src/test/kotlin/RotationTest.kt @@ -15,8 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation - +import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifference import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.Rotation.Companion.lerp @@ -25,10 +24,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.test.assertFalse -import kotlin.math.abs import kotlin.math.hypot -import net.minecraft.util.math.Vec3d -import net.minecraft.util.math.MathHelper /** * Tests for the Rotation class From 03ba095046963d0a61598d145ad5a43cc9c99d5e Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 2 May 2025 22:06:43 +0200 Subject: [PATCH 161/364] Use correct unit tests and fix algo --- .../rotation/visibilty/PlaceDirection.kt | 61 ++++++++++--- common/src/test/kotlin/PlaceDirectionTest.kt | 90 +++++-------------- 2 files changed, 67 insertions(+), 84 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index 9492dd0bf..147755a7c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -24,6 +24,7 @@ import net.minecraft.util.math.MathHelper import net.minecraft.util.math.MathHelper.wrapDegrees import net.minecraft.util.math.Vec3i import kotlin.math.abs +import kotlin.math.asin import kotlin.math.atan import kotlin.math.cos import kotlin.math.sin @@ -66,31 +67,27 @@ enum class PlaceDirection( // Calculate pitch boundaries based on the snapped yaw val snappedYawRad = Math.toRadians(clampedYaw) - val pitchBoundaryEW = Math.toDegrees(atan(abs(sin(snappedYawRad)))) - val pitchBoundaryNS = Math.toDegrees(atan(abs(cos(snappedYawRad)))) + val sinYaw = abs(sin(snappedYawRad)) + val cosYaw = abs(cos(snappedYawRad)) + val pitchBoundaryEW = Math.toDegrees(atan(sinYaw)) + val pitchBoundaryNS = Math.toDegrees(atan(cosYaw)) // Determine the correct pitch boundary and snap pitch val snappedPitch = when { // Primary E/W Directions isEast() || isWest() -> { when { - isUp() -> pitchBoundaryEW // Snap to lower edge of UP_E/UP_W area - isDown() -> -pitchBoundaryEW // Snap to upper edge of DOWN_E/DOWN_W area - else -> { // Horizontal E/W - val isPitchWithinBounds = abs(rot.pitch - pitchBoundaryEW) < abs(rot.pitch - (-pitchBoundaryEW)) - if (isPitchWithinBounds) pitchBoundaryEW else -pitchBoundaryEW - } + isUp() -> calculateVerticalPitch(sinYaw, pitchBoundaryEW, true) + isDown() -> calculateVerticalPitch(sinYaw, pitchBoundaryEW, false) + else -> calculateHorizontalPitch(rot.pitch, pitchBoundaryEW) } } // Primary N/S Directions isNorth() || isSouth() -> { when { - isUp() -> pitchBoundaryNS // Snap to lower edge of UP_N/UP_S area - isDown() -> -pitchBoundaryNS // Snap to upper edge of DOWN_N/DOWN_S area - else -> { // Horizontal N/S - val isWithinNorthernBoundary = abs(rot.pitch - pitchBoundaryNS) < abs(rot.pitch - (-pitchBoundaryNS)) - if (isWithinNorthernBoundary) pitchBoundaryNS else -pitchBoundaryNS - } + isUp() -> calculateVerticalPitch(cosYaw, pitchBoundaryNS, true) + isDown() -> calculateVerticalPitch(cosYaw, pitchBoundaryNS, false) + else -> calculateHorizontalPitch(rot.pitch, pitchBoundaryNS) } } // Handle purely UP/DOWN directions @@ -103,6 +100,42 @@ enum class PlaceDirection( return Rotation(clampedYaw, clampedPitch) } + /** + * Calculates the pitch for vertical (Up/Down) directions + * + * @param trigValue The trigonometric value (sinYaw for E/W, cosYaw for N/S) + * @param boundaryValue The boundary value (pitchBoundaryEW for E/W, pitchBoundaryNS for N/S) + * @param isUp Whether this is for an Up direction (true) or Down direction (false) + * @return The calculated pitch value + */ + private fun calculateVerticalPitch(trigValue: Double, boundaryValue: Double, isUp: Boolean): Double { + val epsilon = 0.01 + val boundarySign = if (isUp) 1 else -1 + val asinSign = if (isUp) -1 else 1 + + val targetPitch = Math.toDegrees( + asinSign * asin(trigValue * cos(Math.toRadians(boundarySign * boundaryValue)) + epsilon) + ) + + return if (isUp) { + targetPitch.coerceIn(-90.0, 0.0) // Ensure it's in the up range + } else { + targetPitch.coerceIn(0.0, 90.0) // Ensure it's in the down range + } + } + + /** + * Calculates the pitch for horizontal directions + * + * @param currentPitch The current pitch value + * @param boundaryValue The boundary value (pitchBoundaryEW for E/W, pitchBoundaryNS for N/S) + * @return The calculated pitch value + */ + private fun calculateHorizontalPitch(currentPitch: Double, boundaryValue: Double): Double { + val isWithinPositiveBoundary = abs(currentPitch - boundaryValue) < abs(currentPitch - (-boundaryValue)) + return if (isWithinPositiveBoundary) boundaryValue else -boundaryValue + } + // Helper functions to determine direction type private fun isEast(): Boolean = this == East || this == UpEast || this == DownEast private fun isWest(): Boolean = this == West || this == UpWest || this == DownWest diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt index e10a45dcf..c1ccc6144 100644 --- a/common/src/test/kotlin/PlaceDirectionTest.kt +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -26,94 +26,44 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue class PlaceDirectionTest { - + @Test fun `test pitch snapping for East direction`() { val direction = PlaceDirection.East - - // Calculate expected pitch boundary for East at yaw -90.0 - val yawRad = Math.toRadians(-90.0) - val expectedBoundary = Math.toDegrees(atan(abs(sin(yawRad)))) - - // Test rotation outside the area (pitch too high) - val rotationOutsideHigh = Rotation(-90.0, expectedBoundary + 10.0) - val snappedHigh = direction.snapToArea(rotationOutsideHigh) - - // Test rotation outside the area (pitch too low) - val rotationOutsideLow = Rotation(-90.0, -expectedBoundary - 10.0) - val snappedLow = direction.snapToArea(rotationOutsideLow) - + val rot = Rotation(-90.0, 90.0) + val snapped = direction.snapToArea(rot) + // Verify that the pitch is snapped to the boundary - assertEquals(expectedBoundary, snappedHigh.pitch, 0.001, "Pitch should be snapped to the upper boundary") - assertEquals(-expectedBoundary, snappedLow.pitch, 0.001, "Pitch should be snapped to the lower boundary") + assertEquals(direction, PlaceDirection.fromRotation(snapped)) } - + @Test fun `test pitch snapping for North direction`() { val direction = PlaceDirection.North - - // Calculate expected pitch boundary for North at yaw -180.0 - val yawRad = Math.toRadians(-180.0) - val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad)))) - - // Test rotation outside the area (pitch too high) - val rotationOutsideHigh = Rotation(-180.0, expectedBoundary + 10.0) - val snappedHigh = direction.snapToArea(rotationOutsideHigh) - - // Test rotation outside the area (pitch too low) - val rotationOutsideLow = Rotation(-180.0, -expectedBoundary - 10.0) - val snappedLow = direction.snapToArea(rotationOutsideLow) - + val rot = Rotation(-180.0, 90.0) + val snapped = direction.snapToArea(rot) + // Verify that the pitch is snapped to the boundary - assertEquals(expectedBoundary, snappedHigh.pitch, 0.001, "Pitch should be snapped to the upper boundary") - assertEquals(-expectedBoundary, snappedLow.pitch, 0.001, "Pitch should be snapped to the lower boundary") + assertEquals(direction, PlaceDirection.fromRotation(snapped)) } - + @Test fun `test pitch snapping for UpEast direction`() { val direction = PlaceDirection.UpEast - - // Calculate expected pitch boundary for UpEast at yaw -90.0 - val yawRad = Math.toRadians(-90.0) - val expectedBoundary = Math.toDegrees(atan(abs(sin(yawRad)))) - - // Test rotation outside the area (pitch too low) - val rotationOutside = Rotation(-90.0, expectedBoundary - 10.0) - val snapped = direction.snapToArea(rotationOutside) - + val rot = Rotation(-90.0, 0.0) + val snapped = direction.snapToArea(rot) + // Verify that the pitch is snapped to the boundary - assertEquals(expectedBoundary, snapped.pitch, 0.001, "Pitch should be snapped to the boundary") + assertEquals(direction, PlaceDirection.fromRotation(snapped)) } - + @Test fun `test pitch snapping for DownNorth direction`() { val direction = PlaceDirection.DownNorth - - // Calculate expected pitch boundary for DownNorth at yaw -180.0 - val yawRad = Math.toRadians(-180.0) - val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad)))) - - // Test rotation outside the area (pitch too high) - val rotationOutside = Rotation(-180.0, -expectedBoundary + 10.0) - val snapped = direction.snapToArea(rotationOutside) - + val rot = Rotation(-180.0, 0.0) + val snapped = direction.snapToArea(rot) + // Verify that the pitch is snapped to the boundary - assertEquals(-expectedBoundary, snapped.pitch, 0.001, "Pitch should be snapped to the boundary") - } - - @Test - fun `test no snapping when rotation is already in area`() { - val direction = PlaceDirection.East - - // Create a rotation that should be in the East area - val rotation = Rotation(-90.0, 0.0) - - // Verify that the rotation is in the area - assertTrue(direction.isInArea(rotation), "Rotation should be in the East area") - - // Verify that snapToArea returns the same rotation - val snapped = direction.snapToArea(rotation) - assertEquals(rotation.yaw, snapped.yaw, 0.001, "Yaw should not change") - assertEquals(rotation.pitch, snapped.pitch, 0.001, "Pitch should not change") + assertEquals(direction, PlaceDirection.fromRotation(snapped)) } } \ No newline at end of file From 82218470f0527b1cc8bbfacc2f434463e34019dc Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 2 May 2025 22:38:53 +0200 Subject: [PATCH 162/364] More extensive tests, removed up and down --- .../interaction/request/rotation/Rotation.kt | 10 +- .../rotation/visibilty/PlaceDirection.kt | 15 +- common/src/test/kotlin/PlaceDirectionTest.kt | 185 +++++++++++++++++- common/src/test/kotlin/RotationTest.kt | 115 +++++++++++ 4 files changed, 313 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt index cebf10397..9b6ba8753 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt @@ -104,8 +104,14 @@ data class Rotation(val yaw: Double, val pitch: Double) { fun wrap(deg: Double) = wrapDegrees(deg) fun Rotation.lerp(other: Rotation, delta: Double): Rotation { - val yaw = wrap(this.yaw + delta * (other.yaw - this.yaw)) - val pitch = wrap(this.pitch + delta * (other.pitch - this.pitch)) + // Calculate the wrapped difference to ensure we take the shortest path + val yawDiff = wrap(other.yaw - this.yaw) + val pitchDiff = wrap(other.pitch - this.pitch) + + // Apply the delta to the wrapped difference + val yaw = wrap(this.yaw + delta * yawDiff) + val pitch = wrap(this.pitch + delta * pitchDiff) + return Rotation(yaw, pitch) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index 147755a7c..5485816e9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -34,20 +34,17 @@ enum class PlaceDirection( val vector: Vec3i, private val yawRanges: List> ) { - Up ( 0.0, -90.0, 0, 1, 0, listOf(Double.MIN_VALUE..Double.MAX_VALUE)), - Down ( 0.0, 90.0, 0, -1, 0, listOf(Double.MIN_VALUE..Double.MAX_VALUE)), - - UpNorth ( -180.0, -90.0, 0, 1, -1, northYawRanges), + UpNorth ( -180.0, -90.0, 0, 1, -1, northYawRanges), UpSouth ( 0.0, -90.0, 0, 1, 1, listOf(southYawRange)), UpWest ( 90.0, -90.0, 1, 1, 0, listOf(westYawRange)), UpEast ( -90.0, -90.0, -1, 1, 0, listOf(eastYawRange)), - DownNorth( -180.0, 90.0, 0, -1, -1, northYawRanges), + DownNorth( -180.0, 90.0, 0, -1, -1, northYawRanges), DownSouth( 0.0, 90.0, 0, -1, 1, listOf(southYawRange)), DownWest ( 90.0, 90.0, 1, -1, 0, listOf(westYawRange)), DownEast ( -90.0, 90.0, -1, -1, 0, listOf(eastYawRange)), - North ( -180.0, 0.0, 0, 0, -1, northYawRanges), + North ( -180.0, 0.0, 0, 0, -1, northYawRanges), South ( 0.0, 0.0, 0, 0, 1, listOf(southYawRange)), West ( 90.0, 0.0, 1, 0, 0, listOf(westYawRange)), East ( -90.0, 0.0, -1, 0, 0, listOf(eastYawRange)); @@ -90,7 +87,7 @@ enum class PlaceDirection( else -> calculateHorizontalPitch(rot.pitch, pitchBoundaryNS) } } - // Handle purely UP/DOWN directions + // impossible to look just up or just down as you are always facing a horizontal direction else -> rotation.pitch } @@ -141,8 +138,8 @@ enum class PlaceDirection( private fun isWest(): Boolean = this == West || this == UpWest || this == DownWest private fun isNorth(): Boolean = this == North || this == UpNorth || this == DownNorth private fun isSouth(): Boolean = this == South || this == UpSouth || this == DownSouth - private fun isUp(): Boolean = this == UpEast || this == UpWest || this == UpNorth || this == UpSouth || this == Up - private fun isDown(): Boolean = this == DownEast || this == DownWest || this == DownNorth || this == DownSouth || this == Down + private fun isUp(): Boolean = this == UpEast || this == UpWest || this == UpNorth || this == UpSouth + private fun isDown(): Boolean = this == DownEast || this == DownWest || this == DownNorth || this == DownSouth fun isInArea(rot: Rotation) = fromRotation(rot) == this diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt index c1ccc6144..a18758734 100644 --- a/common/src/test/kotlin/PlaceDirectionTest.kt +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -24,9 +24,15 @@ import kotlin.math.sin import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlin.test.assertFalse +/** + * Tests for the PlaceDirection class + */ class PlaceDirectionTest { + // Tests for snapToArea method - Horizontal directions + @Test fun `test pitch snapping for East direction`() { val direction = PlaceDirection.East @@ -37,6 +43,16 @@ class PlaceDirectionTest { assertEquals(direction, PlaceDirection.fromRotation(snapped)) } + @Test + fun `test pitch snapping for West direction`() { + val direction = PlaceDirection.West + val rot = Rotation(90.0, 90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + @Test fun `test pitch snapping for North direction`() { val direction = PlaceDirection.North @@ -47,6 +63,18 @@ class PlaceDirectionTest { assertEquals(direction, PlaceDirection.fromRotation(snapped)) } + @Test + fun `test pitch snapping for South direction`() { + val direction = PlaceDirection.South + val rot = Rotation(0.0, 90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + // Tests for snapToArea method - Up directions + @Test fun `test pitch snapping for UpEast direction`() { val direction = PlaceDirection.UpEast @@ -57,6 +85,58 @@ class PlaceDirectionTest { assertEquals(direction, PlaceDirection.fromRotation(snapped)) } + @Test + fun `test pitch snapping for UpWest direction`() { + val direction = PlaceDirection.UpWest + val rot = Rotation(90.0, 0.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test pitch snapping for UpNorth direction`() { + val direction = PlaceDirection.UpNorth + val rot = Rotation(-180.0, 0.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test pitch snapping for UpSouth direction`() { + val direction = PlaceDirection.UpSouth + val rot = Rotation(0.0, 0.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + // Tests for snapToArea method - Down directions + + @Test + fun `test pitch snapping for DownEast direction`() { + val direction = PlaceDirection.DownEast + val rot = Rotation(-90.0, 0.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test pitch snapping for DownWest direction`() { + val direction = PlaceDirection.DownWest + val rot = Rotation(90.0, 0.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + @Test fun `test pitch snapping for DownNorth direction`() { val direction = PlaceDirection.DownNorth @@ -66,4 +146,107 @@ class PlaceDirectionTest { // Verify that the pitch is snapped to the boundary assertEquals(direction, PlaceDirection.fromRotation(snapped)) } -} \ No newline at end of file + + @Test + fun `test pitch snapping for DownSouth direction`() { + val direction = PlaceDirection.DownSouth + val rot = Rotation(0.0, 0.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + // Tests for snapToArea method - Pure Up/Down directions + + @Test + fun `test pitch snapping for UpSouth direction with extreme pitch`() { + val direction = PlaceDirection.UpSouth + val rot = Rotation(0.0, -45.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test pitch snapping for DownSouth direction with extreme pitch`() { + val direction = PlaceDirection.DownSouth + val rot = Rotation(0.0, 45.0) + val snapped = direction.snapToArea(rot) + + // Verify that the pitch is snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + // Tests for when rotation is already in the area + + @Test + fun `test no snapping when rotation is already in area`() { + val direction = PlaceDirection.East + // Create a rotation that's already in the East area + val rot = Rotation(-90.0, 0.0) + + // Verify it's already in the area + assertEquals(direction, PlaceDirection.fromRotation(rot)) + + val snapped = direction.snapToArea(rot) + + // Verify that the rotation is unchanged + assertEquals(rot, snapped) + } + + // Tests for isInArea method + + @Test + fun `test isInArea method`() { + val eastRot = Rotation(-90.0, 0.0) + val northRot = Rotation(-180.0, 0.0) + + assertTrue(PlaceDirection.East.isInArea(eastRot)) + assertFalse(PlaceDirection.East.isInArea(northRot)) + + assertTrue(PlaceDirection.North.isInArea(northRot)) + assertFalse(PlaceDirection.North.isInArea(eastRot)) + } + + // Tests for fromRotation method + + @Test + fun `test fromRotation for horizontal directions`() { + assertEquals(PlaceDirection.East, PlaceDirection.fromRotation(Rotation(-90.0, 0.0))) + assertEquals(PlaceDirection.West, PlaceDirection.fromRotation(Rotation(90.0, 0.0))) + assertEquals(PlaceDirection.North, PlaceDirection.fromRotation(Rotation(-180.0, 0.0))) + assertEquals(PlaceDirection.South, PlaceDirection.fromRotation(Rotation(0.0, 0.0))) + } + + @Test + fun `test fromRotation for up directions`() { + assertEquals(PlaceDirection.UpEast, PlaceDirection.fromRotation(Rotation(-90.0, -60.0))) + assertEquals(PlaceDirection.UpWest, PlaceDirection.fromRotation(Rotation(90.0, -60.0))) + assertEquals(PlaceDirection.UpNorth, PlaceDirection.fromRotation(Rotation(-180.0, -60.0))) + assertEquals(PlaceDirection.UpSouth, PlaceDirection.fromRotation(Rotation(0.0, -60.0))) + } + + @Test + fun `test fromRotation for down directions`() { + assertEquals(PlaceDirection.DownEast, PlaceDirection.fromRotation(Rotation(-90.0, 60.0))) + assertEquals(PlaceDirection.DownWest, PlaceDirection.fromRotation(Rotation(90.0, 60.0))) + assertEquals(PlaceDirection.DownNorth, PlaceDirection.fromRotation(Rotation(-180.0, 60.0))) + assertEquals(PlaceDirection.DownSouth, PlaceDirection.fromRotation(Rotation(0.0, 60.0))) + } + + // Edge case tests + + @Test + fun `test edge case with yaw at boundaries`() { + // Test with yaw at the boundaries between directions + val boundaryYaw = -135.0 // Boundary between North and East + + // Slightly to the East side + assertEquals(PlaceDirection.East, PlaceDirection.fromRotation(Rotation(boundaryYaw + 0.1, 0.0))) + + // Slightly to the North side + assertEquals(PlaceDirection.North, PlaceDirection.fromRotation(Rotation(boundaryYaw - 0.1, 0.0))) + } +} diff --git a/common/src/test/kotlin/RotationTest.kt b/common/src/test/kotlin/RotationTest.kt index b81a907b3..2ffdff1b7 100644 --- a/common/src/test/kotlin/RotationTest.kt +++ b/common/src/test/kotlin/RotationTest.kt @@ -20,10 +20,13 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifferenc import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.Rotation.Companion.lerp import com.lambda.interaction.request.rotation.Rotation.Companion.slerp +import com.lambda.interaction.request.rotation.Rotation.Companion.wrap +import kotlin.math.abs import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.test.assertFalse +import kotlin.test.assertNotEquals import kotlin.math.hypot /** @@ -67,6 +70,34 @@ class RotationTest { assertFalse(rotation1.equalFloat(rotation4)) } + @Test + fun `test equals and hashCode methods`() { + val rotation1 = Rotation(45.0, 30.0) + val rotation2 = Rotation(45.0, 30.0) + val rotation3 = Rotation(45.0, 31.0) + val rotation4 = Rotation(46.0, 30.0) + val rotation5 = Rotation(46.0, 31.0) + + // Test equals method + assertEquals(rotation1, rotation2) + assertNotEquals(rotation1, rotation3) + assertNotEquals(rotation1, rotation4) + assertNotEquals(rotation1, rotation5) + assertNotEquals(rotation3, rotation4) + assertNotEquals(rotation3, rotation5) + assertNotEquals(rotation4, rotation5) + + // Test hashCode method + assertEquals(rotation1.hashCode(), rotation2.hashCode()) + assertNotEquals(rotation1.hashCode(), rotation3.hashCode()) + assertNotEquals(rotation1.hashCode(), rotation4.hashCode()) + assertNotEquals(rotation1.hashCode(), rotation5.hashCode()) + + // Test equals with null and other types + assertNotEquals(rotation1, null) + assertNotEquals(rotation1, "Not a Rotation") + } + @Test fun `test withDelta method`() { val rotation = Rotation(45.0, 30.0) @@ -109,6 +140,31 @@ class RotationTest { assertEquals(-90.0, Rotation.UP.pitch) } + @Test + fun `test wrap method`() { + // Test wrapping positive angles + assertEquals(0.0, wrap(0.0), 0.001) + assertEquals(90.0, wrap(90.0), 0.001) + assertEquals(-180.0, wrap(180.0), 0.001) + assertEquals(-170.0, wrap(190.0), 0.001) // 190 wraps to -170 + assertEquals(0.0, wrap(360.0), 0.001) + assertEquals(10.0, wrap(370.0), 0.001) + + // Test wrapping negative angles + assertEquals(0.0, wrap(-0.0), 0.001) + assertEquals(-90.0, wrap(-90.0), 0.001) + assertEquals(-180.0, wrap(-180.0), 0.001) + assertEquals(170.0, wrap(-190.0), 0.001) // -190 wraps to 170 + assertEquals(0.0, wrap(-360.0), 0.001) + assertEquals(-10.0, wrap(-370.0), 0.001) + + // Test wrapping large angles + assertEquals(10.0, wrap(370.0), 0.001) + assertEquals(10.0, wrap(730.0), 0.001) // 730 = 2*360 + 10 + assertEquals(-10.0, wrap(-370.0), 0.001) + assertEquals(-10.0, wrap(-730.0), 0.001) // -730 = -2*360 - 10 + } + @Test fun `test lerp method`() { val rotation1 = Rotation(0.0, 0.0) @@ -130,6 +186,23 @@ class RotationTest { assertEquals(22.5, result3.pitch, 0.001) } + @Test + fun `test lerp with angle wrapping`() { + // Test lerp across the -180/180 boundary + val rotation1 = Rotation(170.0, 0.0) + val rotation2 = Rotation(-170.0, 0.0) + + // The shortest path from 170 to -170 is to go clockwise (not counterclockwise) + // So the midpoint should be -180 (or 180, they're equivalent) + val midpoint = rotation1.lerp(rotation2, 0.5) + + // Check that the result is either -180 or 180 (they're equivalent) + assertTrue( + abs(midpoint.yaw - 180.0) < 0.001 || + abs(midpoint.yaw + 180.0) < 0.001 + ) + } + @Test fun `test slerp method`() { val rotation1 = Rotation(0.0, 0.0) @@ -153,6 +226,25 @@ class RotationTest { assertTrue(result3.pitch < rotation2.pitch) } + @Test + fun `test slerp with angle wrapping`() { + // Test slerp across the -180/180 boundary + val rotation1 = Rotation(170.0, 0.0) + val rotation2 = Rotation(-170.0, 0.0) + + // With a very high speed, should go directly to rotation2 + val result = rotation1.slerp(rotation2, 1000.0) + assertEquals(rotation2.yaw, result.yaw, 0.001) + assertEquals(rotation2.pitch, result.pitch, 0.001) + + // With a limited speed, should move in the correct direction (clockwise) + val partialResult = rotation1.slerp(rotation2, 10.0) + + // The yaw should be greater than 170 (moving towards 180/-180) + // or less than -170 (already crossed the boundary) + assertTrue(partialResult.yaw > 170.0 || partialResult.yaw < -170.0) + } + @Test fun `test dist method`() { val rotation1 = Rotation(0.0, 0.0) @@ -173,6 +265,16 @@ class RotationTest { assertEquals(hypot(90.0, 90.0), rotation1 dist rotation4, 0.001) } + @Test + fun `test dist method with angle wrapping`() { + // Test distance across the -180/180 boundary + val rotation1 = Rotation(170.0, 0.0) + val rotation2 = Rotation(-170.0, 0.0) + + // The distance should be 20 degrees (not 340 degrees) + assertEquals(20.0, rotation1 dist rotation2, 0.001) + } + @Test fun `test angleDifference method`() { // Test with angles in the same direction @@ -187,4 +289,17 @@ class RotationTest { assertEquals(20.0, angleDifference(170.0, -170.0), 0.001) assertEquals(20.0, angleDifference(-170.0, 170.0), 0.001) } + + @Test + fun `test angleDifference with extreme values`() { + // Test with angles at the boundaries + assertEquals(0.0, angleDifference(180.0, 180.0), 0.001) + assertEquals(0.0, angleDifference(-180.0, -180.0), 0.001) + assertEquals(0.0, angleDifference(180.0, -180.0), 0.001) // These are equivalent + + // Test with large angles that need wrapping + assertEquals(10.0, angleDifference(365.0, 375.0), 0.001) + assertEquals(10.0, angleDifference(-365.0, -375.0), 0.001) + assertEquals(20.0, angleDifference(370.0, -370.0), 0.001) + } } From 1443cb5c2b6844f5319872784e8116a31dbd3eb2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 4 May 2025 17:00:03 +0100 Subject: [PATCH 163/364] manual rebreak touch ups and manager stage priority --- .../interaction/request/ManagerUtils.kt | 25 +++++++++++++++++ .../interaction/request/RequestHandler.kt | 15 ++++++---- .../request/breaking/BreakManager.kt | 28 +++++++++++++------ .../request/breaking/BrokenBlockHandler.kt | 3 ++ .../request/breaking/ReBreakManager.kt | 7 +++++ .../request/hotbar/HotbarManager.kt | 12 ++++---- .../request/placing/PlaceManager.kt | 7 ++++- .../request/rotation/RotationManager.kt | 17 +++++------ common/src/test/kotlin/PlaceDirectionTest.kt | 14 ++++++---- 9 files changed, 95 insertions(+), 33 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt new file mode 100644 index 000000000..0d9a860cf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request + +import com.lambda.util.reflections.getInstances + +object ManagerUtils { + val managers = getInstances>() + val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index eab896c21..cff46ab65 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -18,9 +18,11 @@ package com.lambda.interaction.request import com.lambda.context.SafeContext +import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.ManagerUtils.accumulatedManagerPriority import com.lambda.threading.runSafe /** @@ -28,10 +30,11 @@ import com.lambda.threading.runSafe * next opening if closed */ abstract class RequestHandler( - vararg openStages: Event, + val stagePriority: Int, + private vararg val openStages: Event, private val onOpen: (SafeContext.() -> Unit)? = null, private val onClose: (SafeContext.() -> Unit)? = null -) { +) : Loadable { /** * Represents if the handler is accepting requests at any given time */ @@ -53,7 +56,7 @@ abstract class RequestHandler( */ var activeThisTick = false; protected set - init { + override fun load(): String { openStages.forEach { when (it) { is TickEvent.Pre -> openRequestsFor(it) @@ -77,13 +80,15 @@ abstract class RequestHandler( listen(Int.MIN_VALUE) { activeThisTick = false } + + return super.load() } /** * opens the handler for requests for the duration of the given event */ private inline fun openRequestsFor(stage: T) { - listen(priority = Int.MAX_VALUE) { + listen(priority = Int.MAX_VALUE - (accumulatedManagerPriority - stagePriority)) { tickStage = stage queuedRequest?.let { request -> handleRequest(request) @@ -94,7 +99,7 @@ abstract class RequestHandler( onOpen?.invoke(this) preEvent() } - listen(priority = Int.MIN_VALUE) { + listen(priority = Int.MIN_VALUE + stagePriority) { onClose?.invoke(this) acceptingRequests = false } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 2c9195df3..d7829d931 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -62,6 +62,7 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos object BreakManager : RequestHandler( + 0, TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post, @@ -93,6 +94,10 @@ object BreakManager : RequestHandler( private var instantBreaks = mutableListOf() var lastPosStarted: BlockPos? = null + set(value) { + if (value != field) ReBreakManager.clearReBreak() + field = value + } fun Any.onBreak( alwaysListen: Boolean = false, @@ -102,7 +107,9 @@ object BreakManager : RequestHandler( block() } - init { + override fun load(): String { + super.load() + listen(priority = Int.MIN_VALUE) { if (breakCooldown > 0) { breakCooldown-- @@ -123,7 +130,7 @@ object BreakManager : RequestHandler( breaksThisTick = 0 } - listen(priority = Int.MIN_VALUE + 1) { event -> + listen(priority = Int.MIN_VALUE) { event -> breakInfos .filterNotNull() .firstOrNull { it.context.expectedPos == event.pos } @@ -147,7 +154,7 @@ object BreakManager : RequestHandler( } // ToDo: Dependent on the tracked data order. When set stack is called after position it wont work - listen(priority = Int.MIN_VALUE + 1) { + listen(priority = Int.MIN_VALUE) { if (it.entity !is ItemEntity) return@listen breakInfos @@ -156,10 +163,12 @@ object BreakManager : RequestHandler( ?.internalOnItemDrop(it.entity) } - listenUnsafe(priority = Int.MIN_VALUE + 1) { + listenUnsafe(priority = Int.MIN_VALUE) { breakInfos.forEach { it?.nullify() } breakCooldown = 0 } + + return "Loaded Break Manager" } /** @@ -489,6 +498,7 @@ object BreakManager : RequestHandler( return true } breakCooldown = info.breakConfig.breakDelay + lastPosStarted = ctx.expectedPos interaction.sendSequencedPacket(world) { sequence -> onBlockBreak(info) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) @@ -508,10 +518,9 @@ object BreakManager : RequestHandler( ReBreakManager.clearReBreak() } - primaryBreak?.let { primary -> + return primaryBreak?.let { primary -> updateBreakProgress(primary) - } - return true + } ?: false } is ReBreakResult.ReBroke -> { info.nullify() @@ -523,7 +532,6 @@ object BreakManager : RequestHandler( info.nullify() return false } - ReBreakManager.clearReBreak() val swing = info.breakConfig.swing if (swing.isEnabled() && swing != BreakConfig.SwingMode.End) { swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) @@ -612,6 +620,7 @@ object BreakManager : RequestHandler( if (!world.worldBorder.contains(ctx.expectedPos)) return false if (gamemode.isCreative) { + lastPosStarted = ctx.expectedPos interaction.sendSequencedPacket(world) { sequence: Int -> onBlockBreak(info) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) @@ -621,6 +630,8 @@ object BreakManager : RequestHandler( } if (info.breaking) return false + lastPosStarted = ctx.expectedPos + val blockState = blockState(ctx.expectedPos) val notAir = !blockState.isAir if (notAir && info.breakingTicks == 0) { @@ -644,7 +655,6 @@ object BreakManager : RequestHandler( if (info.breakConfig.breakMode == BreakMode.Packet) { info.stopBreakPacket(world, interaction) } - lastPosStarted = ctx.expectedPos info.startBreakPacket(world, interaction) if (info.isSecondary || (breakDelta < 1 && breakDelta >= info.breakConfig.breakThreshold)) { info.stopBreakPacket(world, interaction) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index f845030a5..d5fe6a5c1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -111,6 +111,9 @@ object BrokenBlockHandler { pending.internalOnItemDrop(it.entity) if (pending.callbacksCompleted) { pending.stopPending() + if (lastPosStarted == pending.context.expectedPos) { + ReBreakManager.startReBreak(pending) + } } return@listen } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 1fb60a921..2c1094606 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -18,8 +18,10 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.ReBreakSettings +import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock import com.lambda.interaction.request.breaking.BrokenBlockHandler.isEmpty @@ -37,11 +39,16 @@ object ReBreakManager { activeAge++ } } + + listenUnsafe(priority = Int.MIN_VALUE) { + reBreak = null + } } fun startReBreak(info: BreakInfo?) { reBreak = info?.apply { type = BreakType.ReBreak + breaking = true } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 341a57a76..99d3e7853 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.request.hotbar import com.lambda.context.SafeContext -import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.InventoryEvent @@ -38,25 +37,26 @@ import com.lambda.threading.runSafe * @see InGameHudMixin.onTick */ object HotbarManager : RequestHandler( + 1, TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post, // ToDo: Post interact onClose = { checkResetSwap() } -), Loadable { +) { val serverSlot get() = runSafe { interaction.lastSelectedSlot } ?: 0 - override fun load() = "Loaded Hotbar Manager" - private var swapsThisTick = 0 private var maxSwapsThisTick = 0 private var swapDelay = 0 private var activeRequest: HotbarRequest? = null - init { + override fun load(): String { + super.load() + listen(priority = Int.MIN_VALUE) { swapsThisTick = 0 if (swapDelay > 0) swapDelay-- @@ -74,6 +74,8 @@ object HotbarManager : RequestHandler( listen(priority = Int.MIN_VALUE) { it.slot = activeRequest?.slot ?: return@listen } + + return "Loaded Hotbar Manager" } override fun SafeContext.handleRequest(request: HotbarRequest) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 55df956a8..af809d443 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -58,6 +58,7 @@ import net.minecraft.util.math.Direction import net.minecraft.world.GameMode object PlaceManager : RequestHandler( + 0, TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post, @@ -85,7 +86,9 @@ object PlaceManager : RequestHandler( block() } - init { + override fun load(): String { + super.load() + listen(priority = Int.MIN_VALUE) { activeRequest = null placementsThisTick = 0 @@ -97,6 +100,8 @@ object PlaceManager : RequestHandler( it.input.sneaking = true } } + + return "Loaded Place Manager" } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index af114be4a..2ae60b521 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -18,9 +18,7 @@ package com.lambda.interaction.request.rotation import com.lambda.Lambda.mc -import com.lambda.config.groups.TickStage import com.lambda.context.SafeContext -import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent @@ -50,11 +48,12 @@ import kotlin.math.sign import kotlin.math.sin object RotationManager : RequestHandler( + 1, TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post, // ToDo: Post interact -), Loadable { +) { var activeRotation = Rotation.ZERO var serverRotation = Rotation.ZERO var prevServerRotation = Rotation.ZERO @@ -62,8 +61,6 @@ object RotationManager : RequestHandler( var activeRequest: RotationRequest? = null private var changedThisTick = false - override fun load() = "Loaded Rotation Manager" - fun Any.onRotate( alwaysListen: Boolean = false, priority: Priority = 0, @@ -72,7 +69,9 @@ object RotationManager : RequestHandler( block() } - init { + override fun load(): String { + super.load() + listen(priority = Int.MIN_VALUE) { activeRequest?.let { request -> request.age++ @@ -80,7 +79,7 @@ object RotationManager : RequestHandler( changedThisTick = false } - listen { event -> + listen(priority = Int.MIN_VALUE) { event -> val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listen @@ -89,9 +88,11 @@ object RotationManager : RequestHandler( } } - listenUnsafe { + listenUnsafe(priority = Int.MIN_VALUE) { reset(Rotation.ZERO) } + + return "Loaded Rotation Manager" } override fun SafeContext.handleRequest(request: RotationRequest) { diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt index c1ccc6144..63439f28b 100644 --- a/common/src/test/kotlin/PlaceDirectionTest.kt +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -20,10 +20,8 @@ import com.lambda.interaction.request.rotation.visibilty.PlaceDirection import kotlin.math.abs import kotlin.math.atan import kotlin.math.cos -import kotlin.math.sin import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue class PlaceDirectionTest { @@ -60,9 +58,15 @@ class PlaceDirectionTest { @Test fun `test pitch snapping for DownNorth direction`() { val direction = PlaceDirection.DownNorth - val rot = Rotation(-180.0, 0.0) - val snapped = direction.snapToArea(rot) - + + // Calculate expected pitch boundary for DownNorth at yaw -180.0 + val yawRad = Math.toRadians(-180.0) + val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad)))) + + // Test rotation outside the area (pitch too high) + val rotationOutside = Rotation(-180.0, -expectedBoundary + 10.0) + val snapped = direction.snapToArea(rotationOutside) + // Verify that the pitch is snapped to the boundary assertEquals(direction, PlaceDirection.fromRotation(snapped)) } From c946eca367246d9034603cec452bb979a2630594 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 4 May 2025 20:48:55 +0100 Subject: [PATCH 164/364] disallow vanilla instant breakables from re-break and cleanup --- .../interaction/request/breaking/BreakInfo.kt | 9 +++++++++ .../request/breaking/BreakManager.kt | 17 +++++++++-------- .../request/breaking/BrokenBlockHandler.kt | 4 ++-- .../request/breaking/ReBreakManager.kt | 11 ++++++----- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 85967a568..420cf6e47 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -43,6 +43,9 @@ data class BreakInfo( var breakingTicks = 0 var soundsCooldown = 0.0f + var vanillaInstantBreakable = false + val reBreakable get() = !vanillaInstantBreakable && isPrimary + val isPrimary get() = type == BreakType.Primary val isSecondary get() = type == BreakType.Secondary val isRedundant get() = type == BreakType.RedundantSecondary @@ -85,6 +88,12 @@ data class BreakInfo( } } + fun tickStats() { + activeAge++ + updatedThisTick = false + updatedProgressThisTick = false + } + fun resetCallbacks() { broken = false item = null diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d7829d931..2d9a579d6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -121,9 +121,7 @@ object BreakManager : RequestHandler( this.cancelBreak() return@apply } - activeAge++ - updatedThisTick = false - updatedProgressThisTick = false + tickStats() } } activeRequest = null @@ -146,8 +144,8 @@ object BreakManager : RequestHandler( info.internalOnBreak() if (!info.callbacksCompleted) { info.startPending() - } else if (info.isPrimary) { - ReBreakManager.startReBreak(info) + } else { + ReBreakManager.offerReBreak(info) } info.nullify() } @@ -402,8 +400,8 @@ object BreakManager : RequestHandler( info.internalOnBreak() if (!info.callbacksCompleted) { info.startPending() - } else if (info.isPrimary) { - ReBreakManager.startReBreak(info) + } else { + ReBreakManager.offerReBreak(info) } } BreakConfirmationMode.BreakThenAwait -> { @@ -655,8 +653,11 @@ object BreakManager : RequestHandler( if (info.breakConfig.breakMode == BreakMode.Packet) { info.stopBreakPacket(world, interaction) } + info.startBreakPacket(world, interaction) - if (info.isSecondary || (breakDelta < 1 && breakDelta >= info.breakConfig.breakThreshold)) { + info.vanillaInstantBreakable = breakDelta >= 1 + + if (info.isSecondary || (!info.vanillaInstantBreakable && breakDelta >= info.breakConfig.breakThreshold)) { info.stopBreakPacket(world, interaction) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index d5fe6a5c1..e47bb8190 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -92,7 +92,7 @@ object BrokenBlockHandler { if (pending.callbacksCompleted) { pending.stopPending() if (lastPosStarted == pending.context.expectedPos) { - ReBreakManager.startReBreak(pending) + ReBreakManager.offerReBreak(pending) } } return@listen @@ -112,7 +112,7 @@ object BrokenBlockHandler { if (pending.callbacksCompleted) { pending.stopPending() if (lastPosStarted == pending.context.expectedPos) { - ReBreakManager.startReBreak(pending) + ReBreakManager.offerReBreak(pending) } } return@listen diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 2c1094606..267b68b60 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -36,7 +36,7 @@ object ReBreakManager { listen(priority = Int.MIN_VALUE) { reBreak?.apply { breakingTicks++ - activeAge++ + tickStats() } } @@ -45,8 +45,10 @@ object ReBreakManager { } } - fun startReBreak(info: BreakInfo?) { - reBreak = info?.apply { + fun offerReBreak(info: BreakInfo) { + if (!info.reBreakable) return + + reBreak = info.apply { type = BreakType.ReBreak breaking = true } @@ -63,8 +65,7 @@ object ReBreakManager { if (info.context.expectedPos != ctx.expectedPos || info.breakConfig.reBreak.mode != ReBreakSettings.Mode.Manual) { return@runSafe ReBreakResult.Ignored } - info.context = ctx - info.request = breakRequest + info.updateInfo(ctx, breakRequest) val context = info.context val awaitThenBreak = info.breakConfig.breakConfirmation == BreakConfig.BreakConfirmationMode.AwaitThenBreak From 182485e5afbbf1ec2bffe921463915be9d48adc8 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 5 May 2025 13:25:09 +0100 Subject: [PATCH 165/364] failing edge case unit test for PlaceDirection --- common/src/test/kotlin/PlaceDirectionTest.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt index a18758734..9df074297 100644 --- a/common/src/test/kotlin/PlaceDirectionTest.kt +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -17,14 +17,10 @@ import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.visibilty.PlaceDirection -import kotlin.math.abs -import kotlin.math.atan -import kotlin.math.cos -import kotlin.math.sin import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue import kotlin.test.assertFalse +import kotlin.test.assertTrue /** * Tests for the PlaceDirection class @@ -179,6 +175,16 @@ class PlaceDirectionTest { assertEquals(direction, PlaceDirection.fromRotation(snapped)) } + @Test + fun `test yaw and pitch snapping for East direction`() { + val direction = PlaceDirection.East + val rot = Rotation(0.0, -90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + // Tests for when rotation is already in the area @Test From 5bd49852cce867754ff5791dcefd5d9be92e330f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 5 May 2025 13:26:30 +0100 Subject: [PATCH 166/364] input post event for updating break manager from packetmine module if not done already --- .../com/lambda/interaction/request/breaking/BreakRequest.kt | 1 - .../kotlin/com/lambda/module/modules/player/PacketMine.kt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 57d248b9d..cff9824e7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig -import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.Priority diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 7e2b1bd20..09f454696 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -78,7 +78,7 @@ object PacketMine : Module( sendBreakRequest(event.pos) } - listen { + listen { if (!requestedThisTick) sendBreakRequest() } } @@ -94,7 +94,7 @@ object PacketMine : Module( onAccept = { breakingPositions[0] = it }, onCancel = { nullifyBreakPos(it) }, onBreak = { breaks++; nullifyBreakPos(it) }, - { _ -> itemDrops++ } + onItemDrop = { _ -> itemDrops++ } ) breakConfig.request(request) requestedThisTick = true From 64206d5111fa79df2286565a3efe6d71e4c6fc5e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 5 May 2025 15:04:45 +0100 Subject: [PATCH 167/364] cancel break at the end of the tick if not updated --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 2d9a579d6..9ef1e20ec 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -257,8 +257,6 @@ object BreakManager : RequestHandler( newBreaks.remove(ctx) return@forEach } - - if (!info.updatedThisTick) info.cancelBreak() } instantBreaks = newBreaks From 38a86e581ee4c836085039aa36aa96514596aa46 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 5 May 2025 22:07:03 +0200 Subject: [PATCH 168/364] Maybe fix for snapToArea --- .../interaction/request/rotation/visibilty/PlaceDirection.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index 5485816e9..14dea552f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -129,6 +129,11 @@ enum class PlaceDirection( * @return The calculated pitch value */ private fun calculateHorizontalPitch(currentPitch: Double, boundaryValue: Double): Double { + // Handle extreme pitch values (-90 or 90) by returning 0 + if (abs(currentPitch) >= 90.0 - 0.1) { + return 0.0 + } + val isWithinPositiveBoundary = abs(currentPitch - boundaryValue) < abs(currentPitch - (-boundaryValue)) return if (isWithinPositiveBoundary) boundaryValue else -boundaryValue } From 2e353911b37ae0f57256c55140d1428515677a96 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 6 May 2025 16:20:06 +0100 Subject: [PATCH 169/364] PlaceDirection todo --- .../interaction/request/rotation/visibilty/PlaceDirection.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index 14dea552f..f4389804e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -29,6 +29,7 @@ import kotlin.math.atan import kotlin.math.cos import kotlin.math.sin +//ToDo: This is broken in many ways. Still a WIP enum class PlaceDirection( val rotation: Rotation, val vector: Vec3i, From 5f7a85f9ea371bef0bd42fe86e902a4751243c4a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 6 May 2025 16:20:31 +0100 Subject: [PATCH 170/364] more PlaceDirection tests --- common/src/test/kotlin/PlaceDirectionTest.kt | 124 ++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt index 9df074297..51415defb 100644 --- a/common/src/test/kotlin/PlaceDirectionTest.kt +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -176,7 +176,7 @@ class PlaceDirectionTest { } @Test - fun `test yaw and pitch snapping for East direction`() { + fun `test yaw and pitch snapping for East direction from up`() { val direction = PlaceDirection.East val rot = Rotation(0.0, -90.0) val snapped = direction.snapToArea(rot) @@ -185,6 +185,128 @@ class PlaceDirectionTest { assertEquals(direction, PlaceDirection.fromRotation(snapped)) } + @Test + fun `test yaw and pitch snapping for South direction from up`() { + val direction = PlaceDirection.South + val rot = Rotation(90.0, -90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping for West direction from up`() { + val direction = PlaceDirection.West + val rot = Rotation(-180.0, -90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping for North direction from up`() { + val direction = PlaceDirection.North + val rot = Rotation(-90.0, -90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping for East direction from down`() { + val direction = PlaceDirection.East + val rot = Rotation(0.0, 90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping for South direction from down`() { + val direction = PlaceDirection.South + val rot = Rotation(90.0, 90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping for West direction from down`() { + val direction = PlaceDirection.West + val rot = Rotation(-180.0, 90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping for North direction from down`() { + val direction = PlaceDirection.North + val rot = Rotation(-90.0, 90.0) + val snapped = direction.snapToArea(rot) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(snapped)) + } + + @Test + fun `test yaw and pitch snapping from one snap to another starting with East from up`() { + val direction = PlaceDirection.East + val rot = Rotation(0.0, -90.0) + val firstSnapped = direction.snapToArea(rot) + + val nextDirection = PlaceDirection.South + val secondSnapped = nextDirection.snapToArea(firstSnapped) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + } + + @Test + fun `test yaw and pitch snapping from one snap to another starting with South from up`() { + val direction = PlaceDirection.South + val rot = Rotation(90.0, -90.0) + val firstSnapped = direction.snapToArea(rot) + + val nextDirection = PlaceDirection.West + val secondSnapped = nextDirection.snapToArea(firstSnapped) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + } + + @Test + fun `test yaw and pitch snapping from one snap to another starting with West from up`() { + val direction = PlaceDirection.West + val rot = Rotation(-180.0, -90.0) + val firstSnapped = direction.snapToArea(rot) + + val nextDirection = PlaceDirection.North + val secondSnapped = nextDirection.snapToArea(firstSnapped) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + } + + @Test + fun `test yaw and pitch snapping from one snap to another starting with North from up`() { + val direction = PlaceDirection.North + val rot = Rotation(-90.0, -90.0) + val firstSnapped = direction.snapToArea(rot) + + val nextDirection = PlaceDirection.East + val secondSnapped = nextDirection.snapToArea(firstSnapped) + + // Verify that the yaw and pitch are snapped to the boundary + assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + } + // Tests for when rotation is already in the area @Test From a5096a28af067e688b9576026cddd0aeb8e55c4c Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 6 May 2025 16:24:26 +0100 Subject: [PATCH 171/364] better re-break setting and updatedThisTick check --- .../lambda/interaction/request/breaking/ReBreakManager.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 267b68b60..034d194ab 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.request.breaking -import com.lambda.config.groups.ReBreakSettings import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -62,7 +61,9 @@ object ReBreakManager { runSafe { val info = reBreak ?: return@runSafe ReBreakResult.Ignored - if (info.context.expectedPos != ctx.expectedPos || info.breakConfig.reBreak.mode != ReBreakSettings.Mode.Manual) { + if (info.updatedThisTick) return@runSafe ReBreakResult.ReBroke + + if (info.context.expectedPos != ctx.expectedPos || !info.breakConfig.reBreak.mode.isEnabled()) { return@runSafe ReBreakResult.Ignored } info.updateInfo(ctx, breakRequest) From 50ae6d798b4ebdb15176e159e3d2b2422aaa8934 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 6 May 2025 16:25:37 +0100 Subject: [PATCH 172/364] check updatedThisTick over activeAge --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 9ef1e20ec..a3429064f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -344,7 +344,7 @@ object BreakManager : RequestHandler( val breakInfo = BreakInfo(requestCtx, Primary, request) primaryBreak?.let { primaryInfo -> if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) { - if (primaryInfo.activeAge > 0) { + if (!primaryInfo.updatedThisTick) { primaryInfo.abortBreakPacket(world, interaction) return@let } else return null From a018c9ea0b360b0379c402fdaf8509d2440f37e4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 6 May 2025 22:30:52 +0100 Subject: [PATCH 173/364] =?UTF-8?q?removed=20needless=20comma=20?= =?UTF-8?q?=F0=9F=A7=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/com/lambda/event/listener/SafeListener.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt index b2b3620fb..301471be1 100644 --- a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt +++ b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt @@ -119,7 +119,7 @@ class SafeListener( inline fun Any.listen( priority: Int = 0, alwaysListen: Boolean = false, - noinline function: SafeContext.(T) -> Unit = {}, + noinline function: SafeContext.(T) -> Unit = {} ): SafeListener { val listener = SafeListener(priority, this, alwaysListen) { event -> runGameScheduled { function(event) } From 4db79c63761cdebbc2ad09f5e456721f1ba638a4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 7 May 2025 02:59:52 +0100 Subject: [PATCH 174/364] automatic re-break --- .../interaction/request/breaking/BreakInfo.kt | 2 ++ .../request/breaking/BreakManager.kt | 4 ++- .../request/breaking/BreakRequest.kt | 2 ++ .../request/breaking/BrokenBlockHandler.kt | 1 + .../request/breaking/ReBreakManager.kt | 1 + .../module/modules/player/PacketMine.kt | 36 ++++++++++++++++--- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 420cf6e47..13df8bc44 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -43,6 +43,8 @@ data class BreakInfo( var breakingTicks = 0 var soundsCooldown = 0.0f + var pending = false + var vanillaInstantBreakable = false val reBreakable get() = !vanillaInstantBreakable && isPrimary diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index a3429064f..bd79c3d45 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -455,7 +455,7 @@ object BreakManager : RequestHandler( */ private fun BreakInfo.nullify() { type.nullify() - if (!broken && !isReBreaking && !isRedundant) internalOnCancel() + if (!broken && !pending && !isReBreaking && !isRedundant) internalOnCancel() } /** @@ -519,7 +519,9 @@ object BreakManager : RequestHandler( } ?: false } is ReBreakResult.ReBroke -> { + info.type = ReBreak info.nullify() + info.request.onReBreak?.invoke(info.context.expectedPos) return true } else -> {} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index cff9824e7..07585a9ea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -39,6 +39,8 @@ data class BreakRequest( val onCancel: ((BlockPos) -> Unit)? = null, val onBreak: ((BlockPos) -> Unit)? = null, val onItemDrop: ((ItemEntity) -> Unit)? = null, + val onReBreakStart: ((BlockPos) -> Unit)? = null, + val onReBreak: ((BlockPos) -> Unit)? = null, private val prio: Priority = 0 ) : Request(prio, build.breaking) { override val done: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index e47bb8190..a7a4e33f9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -128,6 +128,7 @@ object BrokenBlockHandler { * Adds the [info] to the [BrokenBlockHandler], and requesters, pending interaction collections. */ fun BreakInfo.startPending() { + pending = true pendingBreaks.add(this) pendingInteractions.add(context) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 034d194ab..9440669ba 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -51,6 +51,7 @@ object ReBreakManager { type = BreakType.ReBreak breaking = true } + info.request.onReBreakStart?.invoke(info.context.expectedPos) } fun clearReBreak() { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 09f454696..78168a985 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -21,6 +21,7 @@ import com.lambda.config.groups.BuildSettings import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings +import com.lambda.config.groups.ReBreakSettings import com.lambda.config.groups.RotationSettings import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent @@ -59,6 +60,7 @@ object PacketMine : Module( private var itemDrops = 0 private val breakingPositions = arrayOfNulls(2) + private var reBreakPos: BlockPos? = null private var requestedThisTick = false @@ -67,6 +69,14 @@ object PacketMine : Module( requestedThisTick = false } + //ToDo: run on every tick stage + listen { + val reBreakMode = breakConfig.reBreak.mode + if (reBreakMode != ReBreakSettings.Mode.Auto && reBreakMode != ReBreakSettings.Mode.AutoConstant) return@listen + val reBreak = reBreakPos ?: return@listen + requestBreakManager(listOf(reBreak)) + } + listen { it.cancel() } listen { event -> event.cancel() @@ -76,6 +86,7 @@ object PacketMine : Module( } breakingPositions[0] = null sendBreakRequest(event.pos) + requestedThisTick = true } listen { @@ -89,18 +100,33 @@ object PacketMine : Module( requestPositions.add(pos) } + if (requestPositions.isNotEmpty()) requestBreakManager(requestPositions) + } + + private fun SafeContext.requestBreakManager(requestPositions: List) { val request = BreakRequest( breakContexts(requestPositions), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onAccept = { breakingPositions[0] = it }, - onCancel = { nullifyBreakPos(it) }, - onBreak = { breaks++; nullifyBreakPos(it) }, + onAccept = { + breakingPositions[0] = it + reBreakPos = null + }, + onCancel = { nullifyBreakPos(it, true) }, + onBreak = { + breaks++ + nullifyBreakPos(it) + }, + onReBreakStart = { reBreakPos = it }, + onReBreak = { reBreakPos = it }, onItemDrop = { _ -> itemDrops++ } ) breakConfig.request(request) - requestedThisTick = true } - private fun nullifyBreakPos(pos: BlockPos) { + private fun nullifyBreakPos(pos: BlockPos, includeReBreak: Boolean = false) { + if (includeReBreak && pos == reBreakPos) { + reBreakPos = null + return + } breakingPositions.forEachIndexed { index, breakPos -> if (breakPos == pos) { breakingPositions[index] = null From f878a53c4ce25b58f3f24325491de8d5f2c00e5e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 7 May 2025 14:55:42 +0100 Subject: [PATCH 175/364] ignore block updates / item drops in break manager if they match the rebreak --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index bd79c3d45..c8d721f63 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -133,6 +133,8 @@ object BreakManager : RequestHandler( .filterNotNull() .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> + if (event.pos == ReBreakManager.reBreak?.context?.expectedPos) return@listen + // if not broken if (!isBroken(info.context.checkedState, event.newState)) { this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.checkedState.brokenState}") @@ -155,6 +157,10 @@ object BreakManager : RequestHandler( listen(priority = Int.MIN_VALUE) { if (it.entity !is ItemEntity) return@listen + ReBreakManager.reBreak?.let { reBreak -> + if (matchesBlockItem(reBreak, it.entity)) return@listen + } + breakInfos .filterNotNull() .firstOrNull { info -> matchesBlockItem(info, it.entity) } From d847675b41300cb45a106491ad65278be0eb7ec3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 7 May 2025 15:04:48 +0100 Subject: [PATCH 176/364] call cancel callback if break times out --- .../lambda/interaction/request/breaking/BrokenBlockHandler.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index a7a4e33f9..50e8b1932 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -62,6 +62,7 @@ object BrokenBlockHandler { world.setBlockState(info.context.expectedPos, info.context.checkedState) } } + info.internalOnCancel() info.pendingInteractions.remove(info.context) } From 77101de0f4b06a1797542fe01b52c2a456f46f7b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 7 May 2025 18:11:00 +0100 Subject: [PATCH 177/364] call onAccept if continued mining from rebreak --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index c8d721f63..d90fbcea2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -518,6 +518,7 @@ object BreakManager : RequestHandler( primaryBreak = reBreakResult.breakInfo.apply { type = Primary ReBreakManager.clearReBreak() + request.onAccept?.invoke(ctx.expectedPos) } return primaryBreak?.let { primary -> From e0a95b2665f60779f899631443651002c8519bd5 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 7 May 2025 21:37:09 +0100 Subject: [PATCH 178/364] packemine refactor and rebreak fixes --- .../interaction/request/breaking/BreakInfo.kt | 6 +-- .../request/breaking/BreakManager.kt | 47 +++++++++---------- .../request/breaking/BrokenBlockHandler.kt | 6 +-- .../request/breaking/ReBreakManager.kt | 4 +- .../module/modules/player/PacketMine.kt | 47 ++++++++----------- 5 files changed, 51 insertions(+), 59 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 13df8bc44..1712608a5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -62,7 +62,7 @@ data class BreakInfo( @Synchronized fun internalOnBreak() { - broken = true + if (!isReBreaking) broken = true request.onBreak?.invoke(context.expectedPos) item?.let { item -> request.onItemDrop?.invoke(item) @@ -71,8 +71,8 @@ data class BreakInfo( @Synchronized fun internalOnItemDrop(item: ItemEntity) { - this.item = item - if (broken) { + if (!isReBreaking) this.item = item + if (broken || isReBreaking) { request.onItemDrop?.invoke(item) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d90fbcea2..3069edf33 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -129,12 +129,12 @@ object BreakManager : RequestHandler( } listen(priority = Int.MIN_VALUE) { event -> + if (event.pos == ReBreakManager.reBreak?.context?.expectedPos) return@listen + breakInfos .filterNotNull() .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> - if (event.pos == ReBreakManager.reBreak?.context?.expectedPos) return@listen - // if not broken if (!isBroken(info.context.checkedState, event.newState)) { this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.checkedState.brokenState}") @@ -203,8 +203,7 @@ object BreakManager : RequestHandler( if (request.fresh) populateFrom(request) - if (!atMaxBreakInfos(request.build.breaking)) run processNewBreaks@ { - if (!performInstantBreaks(request)) return@processNewBreaks + if (performInstantBreaks(request)) { processNewBreaks(request) } @@ -215,7 +214,7 @@ object BreakManager : RequestHandler( .filterNotNull() .filter { !it.isRedundant } .also { - rotationRequest = it.firstOrNull { it.breakConfig.rotateForBreak } + rotationRequest = it.firstOrNull { info -> info.breakConfig.rotateForBreak } ?.let { info -> val rotation = info.context.rotation if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation @@ -251,7 +250,7 @@ object BreakManager : RequestHandler( private fun SafeContext.populateFrom(request: BreakRequest) { // Sanitize the new breaks val newBreaks = request.contexts - .filter { ctx -> canAccept(ctx) } + .filter { ctx -> canAccept(ctx, request.build.breaking) } .toMutableList() // Update the current break infos or cancel if abandoned @@ -281,14 +280,16 @@ object BreakManager : RequestHandler( /** * @return if the break context can be accepted. */ - private fun SafeContext.canAccept(ctx: BreakContext): Boolean { + private fun SafeContext.canAccept(ctx: BreakContext, breakConfig: BreakConfig): Boolean { if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false - breakInfos - .firstOrNull { it != null && !it.isRedundant } - ?.let { info -> - if (ctx.hotbarIndex != info.context.hotbarIndex) return false - } + if (breakConfig.doubleBreak) { + breakInfos + .firstOrNull { it != null && !it.isRedundant } + ?.let { info -> + if (ctx.hotbarIndex != info.context.hotbarIndex) return false + } + } return !blockState(ctx.expectedPos).isAir } @@ -449,27 +450,23 @@ object BreakManager : RequestHandler( if (isPrimary) { abortBreakPacket(world, interaction) nullify() - return@runSafe - } - if (isSecondary && breakConfig.unsafeCancels) { + } else if (isSecondary && breakConfig.unsafeCancels) { makeRedundant() } + + internalOnCancel() } /** * Nullifies the break. If the block is not broken, the [BreakInfo.internalOnCancel] callback gets triggered */ - private fun BreakInfo.nullify() { - type.nullify() - if (!broken && !pending && !isReBreaking && !isRedundant) internalOnCancel() - } + private fun BreakInfo.nullify() = type.nullify() /** * Makes the [BreakInfo] redundant and triggers the [BreakInfo.internalOnCancel] callback */ private fun BreakInfo.makeRedundant() { type = BreakType.RedundantSecondary - internalOnCancel() } /** @@ -494,15 +491,15 @@ object BreakManager : RequestHandler( val ctx = info.context val hitResult = ctx.result - if (gamemode.isCreative && world.worldBorder.contains(ctx.expectedPos)) { + if (gamemode.isCreative && world.worldBorder.contains(ctx.expectedPos) && info.breaking) { if (info.isRedundant) { onBlockBreak(info) return true } breakCooldown = info.breakConfig.breakDelay lastPosStarted = ctx.expectedPos + onBlockBreak(info) interaction.sendSequencedPacket(world) { sequence -> - onBlockBreak(info) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } val swing = info.breakConfig.swing @@ -535,6 +532,7 @@ object BreakManager : RequestHandler( } if (!startBreaking(info)) { info.nullify() + info.internalOnCancel() return false } val swing = info.breakConfig.swing @@ -547,6 +545,7 @@ object BreakManager : RequestHandler( val blockState = blockState(ctx.expectedPos) if (blockState.isAir) { info.nullify() + info.internalOnCancel() return false } @@ -595,8 +594,8 @@ object BreakManager : RequestHandler( val swing = info.breakConfig.swing if (overBreakThreshold) { if (info.isPrimary) { + onBlockBreak(info) interaction.sendSequencedPacket(world) { sequence -> - onBlockBreak(info) PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) } } else { @@ -626,8 +625,8 @@ object BreakManager : RequestHandler( if (gamemode.isCreative) { lastPosStarted = ctx.expectedPos + onBlockBreak(info) interaction.sendSequencedPacket(world) { sequence: Int -> - onBlockBreak(info) PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } breakCooldown = info.breakConfig.breakDelay diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 50e8b1932..a0635e3c5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -81,8 +81,10 @@ object BrokenBlockHandler { if (!isBroken(pending.context.checkedState, event.newState)) { if (!pending.isReBreaking) { this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.checkedState.brokenState}") + pending.stopPending() + } else { + pending.context.checkedState = event.newState } - pending.stopPending() return@listen } @@ -141,8 +143,6 @@ object BrokenBlockHandler { if (!isReBreaking) { pendingBreaks.remove(this) pendingInteractions.remove(context) - } else { - resetCallbacks() } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 9440669ba..2c002823c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -50,6 +50,7 @@ object ReBreakManager { reBreak = info.apply { type = BreakType.ReBreak breaking = true + resetCallbacks() } info.request.onReBreakStart?.invoke(info.context.expectedPos) } @@ -62,11 +63,10 @@ object ReBreakManager { runSafe { val info = reBreak ?: return@runSafe ReBreakResult.Ignored - if (info.updatedThisTick) return@runSafe ReBreakResult.ReBroke - if (info.context.expectedPos != ctx.expectedPos || !info.breakConfig.reBreak.mode.isEnabled()) { return@runSafe ReBreakResult.Ignored } + if (info.updatedThisTick) return@runSafe ReBreakResult.ReBroke info.updateInfo(ctx, breakRequest) val context = info.context diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 78168a985..2f852717a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -62,11 +62,11 @@ object PacketMine : Module( private val breakingPositions = arrayOfNulls(2) private var reBreakPos: BlockPos? = null - private var requestedThisTick = false + private var attackedThisTick = false init { listen { - requestedThisTick = false + attackedThisTick = false } //ToDo: run on every tick stage @@ -74,39 +74,33 @@ object PacketMine : Module( val reBreakMode = breakConfig.reBreak.mode if (reBreakMode != ReBreakSettings.Mode.Auto && reBreakMode != ReBreakSettings.Mode.AutoConstant) return@listen val reBreak = reBreakPos ?: return@listen - requestBreakManager(listOf(reBreak)) + requestBreakManager(reBreak) } listen { it.cancel() } listen { event -> event.cancel() if (breakingPositions.any { it == event.pos }) return@listen - if (breakConfig.doubleBreak && breakingPositions[1] == null) { - breakingPositions[1] = breakingPositions[0] - } - breakingPositions[0] = null - sendBreakRequest(event.pos) - requestedThisTick = true + val secondary = if (breakConfig.doubleBreak) { + breakingPositions[1] ?: breakingPositions[0] + } else null + requestBreakManager(event.pos, secondary) + attackedThisTick = true } listen { - if (!requestedThisTick) sendBreakRequest() + if (!attackedThisTick) requestBreakManager(*breakingPositions.toList().toTypedArray()) } } - private fun SafeContext.sendBreakRequest(hitPos: BlockPos? = null) { - val requestPositions = arrayListOf(*breakingPositions.filterNotNull().toTypedArray()) - hitPos?.let { pos -> - requestPositions.add(pos) - } - - if (requestPositions.isNotEmpty()) requestBreakManager(requestPositions) - } - - private fun SafeContext.requestBreakManager(requestPositions: List) { + private fun SafeContext.requestBreakManager(vararg requestPositions: BlockPos?) { + if (requestPositions.isEmpty()) return val request = BreakRequest( - breakContexts(requestPositions), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, + breakContexts(requestPositions.filterNotNull()), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, onAccept = { + if (breakConfig.doubleBreak && breakingPositions[1] == null) { + breakingPositions[1] = breakingPositions[0] + } breakingPositions[0] = it reBreakPos = null }, @@ -123,16 +117,15 @@ object PacketMine : Module( } private fun nullifyBreakPos(pos: BlockPos, includeReBreak: Boolean = false) { - if (includeReBreak && pos == reBreakPos) { - reBreakPos = null - return - } breakingPositions.forEachIndexed { index, breakPos -> if (breakPos == pos) { breakingPositions[index] = null - return } } + if (includeReBreak && pos == reBreakPos) { + reBreakPos = null + return + } } private fun SafeContext.breakContexts(breakPositions: Collection) = @@ -152,4 +145,4 @@ object PacketMine : Module( enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } -} +} \ No newline at end of file From 0aa38064259729bb7990d62e2ebcad6d9c1ad839 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 8 May 2025 01:26:48 +0100 Subject: [PATCH 179/364] removed empty space at the top of the file --- .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index db73375c8..2f852717a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -1,4 +1,3 @@ - /* * Copyright 2025 Lambda * From c9309811650668d13e40d5ae8a8bf01410b38719 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 8 May 2025 01:55:03 +0100 Subject: [PATCH 180/364] packetmine onDisable --- .../kotlin/com/lambda/module/modules/player/PacketMine.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 2f852717a..e92f2caf6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -91,6 +91,13 @@ object PacketMine : Module( listen { if (!attackedThisTick) requestBreakManager(*breakingPositions.toList().toTypedArray()) } + + onDisable { + breakingPositions[0] = null + breakingPositions[1] = null + reBreakPos = null + attackedThisTick = false + } } private fun SafeContext.requestBreakManager(vararg requestPositions: BlockPos?) { From 0f15b3039c744d5ccfb49fa68850acc6aea5ffc2 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Thu, 8 May 2025 09:48:20 -0400 Subject: [PATCH 181/364] Generic events --- .../interaction/request/RequestHandler.kt | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index cff46ab65..b7240f864 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -24,6 +24,7 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.ManagerUtils.accumulatedManagerPriority import com.lambda.threading.runSafe +import kotlin.reflect.KClass /** * This class handles requests, offering specific opening times, and an option to queue a request for the @@ -57,25 +58,7 @@ abstract class RequestHandler( var activeThisTick = false; protected set override fun load(): String { - openStages.forEach { - when (it) { - is TickEvent.Pre -> openRequestsFor(it) - is TickEvent.Post -> openRequestsFor(it) - is TickEvent.Network.Pre -> openRequestsFor(it) - is TickEvent.Network.Post -> openRequestsFor(it) - is TickEvent.Input.Pre -> openRequestsFor(it) - is TickEvent.Input.Post -> openRequestsFor(it) - is TickEvent.WorldRender.Pre -> openRequestsFor(it) - is TickEvent.WorldRender.Post -> openRequestsFor(it) - is TickEvent.Sound.Pre -> openRequestsFor(it) - is TickEvent.Sound.Post -> openRequestsFor(it) - is TickEvent.Render.Pre -> openRequestsFor(it) - is TickEvent.Render.Post -> openRequestsFor(it) - is TickEvent.Player.Pre -> openRequestsFor(it) - is TickEvent.Player.Post -> openRequestsFor(it) - else -> throw IllegalArgumentException("Event '$it' is not allowed for requests") - } - } + openStages.forEach { openRequestsFor(it::class, it) } listen(Int.MIN_VALUE) { activeThisTick = false @@ -87,8 +70,8 @@ abstract class RequestHandler( /** * opens the handler for requests for the duration of the given event */ - private inline fun openRequestsFor(stage: T) { - listen(priority = Int.MAX_VALUE - (accumulatedManagerPriority - stagePriority)) { + private inline fun openRequestsFor(instance: KClass, stage: T) { + listen(instance, priority = Int.MAX_VALUE - (accumulatedManagerPriority - stagePriority)) { tickStage = stage queuedRequest?.let { request -> handleRequest(request) @@ -99,7 +82,8 @@ abstract class RequestHandler( onOpen?.invoke(this) preEvent() } - listen(priority = Int.MIN_VALUE + stagePriority) { + + listen(instance, priority = Int.MIN_VALUE + stagePriority) { onClose?.invoke(this) acceptingRequests = false } From ae3ae443075339fa754829d1a1b34894b4bbc8b1 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 9 May 2025 03:44:37 +0100 Subject: [PATCH 182/364] dont rotate for break by default --- .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 5532f12af..c6271f7f3 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -40,7 +40,7 @@ class BreakSettings( override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } - override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() } + override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { vis() } override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() } From 800cd6827de671d3fbfabf79410558eaa28efb6a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 9 May 2025 03:58:43 +0100 Subject: [PATCH 183/364] reBreakMode in packetmine rather than break config --- .../com/lambda/config/groups/BreakSettings.kt | 2 +- .../lambda/config/groups/ReBreakSettings.kt | 36 ------------------- .../request/breaking/BreakConfig.kt | 3 +- .../request/breaking/ReBreakManager.kt | 2 +- .../module/modules/player/PacketMine.kt | 11 ++++-- 5 files changed, 11 insertions(+), 43 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index c6271f7f3..197c0fa18 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -29,7 +29,7 @@ class BreakSettings( vis: () -> Boolean = { true } ) : BreakConfig(priority) { override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() } - override val reBreak = ReBreakSettings(c, vis) + override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", vis) override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt deleted file mode 100644 index c5474f39f..000000000 --- a/common/src/main/kotlin/com/lambda/config/groups/ReBreakSettings.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.config.groups - -import com.lambda.config.Configurable - -class ReBreakSettings( - c: Configurable, - vis: () -> Boolean = { true } -) { - val mode by c.setting("ReBreak Mode", Mode.Manual, "The method used to re-break blocks after they've been broken once", vis) - - enum class Mode { - None, - Manual, - Auto, - AutoConstant; - - fun isEnabled() = this != None - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 61bb62a99..2dadb56c0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.ReBreakSettings import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -28,7 +27,7 @@ abstract class BreakConfig( priority: Priority = 0 ) : RequestConfig(priority) { abstract val breakMode: BreakMode - abstract val reBreak: ReBreakSettings + abstract val reBreak: Boolean abstract val unsafeCancels: Boolean abstract val breakThreshold: Float abstract val doubleBreak: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 2c002823c..12305b61a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -63,7 +63,7 @@ object ReBreakManager { runSafe { val info = reBreak ?: return@runSafe ReBreakResult.Ignored - if (info.context.expectedPos != ctx.expectedPos || !info.breakConfig.reBreak.mode.isEnabled()) { + if (info.context.expectedPos != ctx.expectedPos || !info.breakConfig.reBreak) { return@runSafe ReBreakResult.Ignored } if (info.updatedThisTick) return@runSafe ReBreakResult.ReBroke diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index e92f2caf6..4bbd74e79 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -21,7 +21,6 @@ import com.lambda.config.groups.BuildSettings import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings -import com.lambda.config.groups.ReBreakSettings import com.lambda.config.groups.RotationSettings import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent @@ -53,6 +52,7 @@ object PacketMine : Module( private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } private val inventory = InventorySettings(this) { page == Page.Inventory } private val hotbar = HotbarSettings(this) { page == Page.Hotbar } + private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } private val pendingInteractionsList = ConcurrentLinkedQueue() @@ -71,8 +71,7 @@ object PacketMine : Module( //ToDo: run on every tick stage listen { - val reBreakMode = breakConfig.reBreak.mode - if (reBreakMode != ReBreakSettings.Mode.Auto && reBreakMode != ReBreakSettings.Mode.AutoConstant) return@listen + if (breakConfig.reBreak && reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant) return@listen val reBreak = reBreakPos ?: return@listen requestBreakManager(reBreak) } @@ -152,4 +151,10 @@ object PacketMine : Module( enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } + + enum class ReBreakMode { + Manual, + Auto, + AutoConstant; + } } \ No newline at end of file From 439e414a885da7aa23470fd9ce03739771cbb3c0 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 9 May 2025 04:01:50 +0100 Subject: [PATCH 184/364] correct config checks in packetmine --- .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 4bbd74e79..2f3ab7fc6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -71,7 +71,7 @@ object PacketMine : Module( //ToDo: run on every tick stage listen { - if (breakConfig.reBreak && reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant) return@listen + if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen val reBreak = reBreakPos ?: return@listen requestBreakManager(reBreak) } From 5de89357a05aa92e873e47150926aca598bedf99 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 9 May 2025 19:15:12 +0100 Subject: [PATCH 185/364] cleaner configs idea --- .../lambda/command/commands/BuildCommand.kt | 14 +-- .../com/lambda/context/AbstractContext.kt | 41 --------- .../com/lambda/context/ClientContext.kt | 27 ++++-- .../kotlin/com/lambda/context/Configured.kt | 32 +++++++ .../lambda/context/ConfiguredSafeContext.kt | 23 +++++ .../com/lambda/context/DefaultConfigs.kt | 31 +++++++ .../kotlin/com/lambda/context/SafeContext.kt | 13 +-- .../construction/result/BreakResult.kt | 3 +- .../construction/result/BuildResult.kt | 5 +- .../construction/result/PlaceResult.kt | 3 +- .../construction/result/Resolvable.kt | 3 +- .../construction/simulation/BuildGoal.kt | 6 +- .../construction/simulation/BuildSimulator.kt | 89 ++++++++---------- .../construction/simulation/Simulation.kt | 29 ++---- .../containers/ShulkerBoxContainer.kt | 5 +- .../module/modules/player/HighwayTools.kt | 58 ++++++------ .../module/modules/player/InventoryTweaks.kt | 5 +- .../com/lambda/module/modules/player/Nuker.kt | 13 +-- .../module/modules/player/PacketMine.kt | 90 ++++++++++--------- .../module/modules/player/WorldEater.kt | 10 ++- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 71 +++++---------- .../com/lambda/task/tasks/PlaceContainer.kt | 20 ++--- .../kotlin/com/lambda/threading/Threading.kt | 7 +- 23 files changed, 310 insertions(+), 288 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/context/AbstractContext.kt create mode 100644 common/src/main/kotlin/com/lambda/context/Configured.kt create mode 100644 common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt create mode 100644 common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt diff --git a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt index 12a1dcb87..edab28327 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt @@ -25,6 +25,7 @@ import com.lambda.brigadier.argument.value import com.lambda.brigadier.executeWithResult import com.lambda.brigadier.required import com.lambda.command.LambdaCommand +import com.lambda.context.DefaultConfigs import com.lambda.interaction.construction.StructureRegistry import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint @@ -61,11 +62,14 @@ object BuildCommand : LambdaCommand( .loadStructureByRelativePath(Path.of(pathString)) .let { template -> info("Building structure $pathString with dimensions ${template.size.toShortString()} created by ${template.author}") - lastBuildTask = template.toStructure() - .move(player.blockPos) - .toBlueprint() - .build() - .run() + DefaultConfigs.run { + lastBuildTask = build( + template + .toStructure() + .move(player.blockPos) + .toBlueprint() + ).run() + } return@executeWithResult success() } diff --git a/common/src/main/kotlin/com/lambda/context/AbstractContext.kt b/common/src/main/kotlin/com/lambda/context/AbstractContext.kt deleted file mode 100644 index 4c5e94335..000000000 --- a/common/src/main/kotlin/com/lambda/context/AbstractContext.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.context - -import net.minecraft.client.MinecraftClient -import net.minecraft.client.network.ClientPlayNetworkHandler -import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.client.network.ClientPlayerInteractionManager -import net.minecraft.client.world.ClientWorld - -/** - * Representing an abstract context in the [MinecraftClient]. - * - * @property mc The Minecraft client instance. - * @property world The world in which the player is currently located, or `null` if the world is not available. - * @property player The player entity, or `null` if the player is not available. - * @property interaction The interaction manager for the player, or `null` if the interaction manager is not available. - * @property connection The network handler for the player, or `null` if the network handler is not available. - */ -abstract class AbstractContext { - val mc: MinecraftClient = MinecraftClient.getInstance() - abstract val world: ClientWorld? - abstract val player: ClientPlayerEntity? - abstract val interaction: ClientPlayerInteractionManager? - abstract val connection: ClientPlayNetworkHandler? -} diff --git a/common/src/main/kotlin/com/lambda/context/ClientContext.kt b/common/src/main/kotlin/com/lambda/context/ClientContext.kt index 906b72699..b362b07cf 100644 --- a/common/src/main/kotlin/com/lambda/context/ClientContext.kt +++ b/common/src/main/kotlin/com/lambda/context/ClientContext.kt @@ -35,16 +35,29 @@ import net.minecraft.client.world.ClientWorld * * @function toSafe Converts the `ClientContext` to a `SafeContext` if all properties are not `null`, or returns `null` otherwise. */ -open class ClientContext : AbstractContext() { - final override val world: ClientWorld? = mc.world - final override val player: ClientPlayerEntity? = mc.player - final override val interaction: ClientPlayerInteractionManager? = mc.interactionManager - final override val connection: ClientPlayNetworkHandler? = mc.networkHandler +open class ClientContext { + val mc: MinecraftClient = MinecraftClient.getInstance() + val clientWorld: ClientWorld? = mc.world + val clientPlayer: ClientPlayerEntity? = mc.player + val clientInteraction: ClientPlayerInteractionManager? = mc.interactionManager + val clientConnection: ClientPlayNetworkHandler? = mc.networkHandler fun toSafe(): SafeContext? { - if (world == null || player == null || interaction == null || connection == null) { + if (clientWorld == null || clientPlayer == null || clientInteraction == null || clientConnection == null) { return null } - return SafeContext(world, player, interaction, connection) + return object : SafeContext { + override val mc = this@ClientContext.mc + override val world: ClientWorld = clientWorld + override val player: ClientPlayerEntity = clientPlayer + override val interaction: ClientPlayerInteractionManager = clientInteraction + override val connection: ClientPlayNetworkHandler = clientConnection + } + } + + fun toSafeConfigured(configured: Configured): ConfiguredSafeContext? { + return toSafe()?.let { safeContext -> + ConfiguredSafeContext(safeContext, configured) + } } } diff --git a/common/src/main/kotlin/com/lambda/context/Configured.kt b/common/src/main/kotlin/com/lambda/context/Configured.kt new file mode 100644 index 000000000..8115d4d68 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/context/Configured.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.context + +import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig +import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.interaction.request.rotation.RotationConfig + +interface Configured { + val build: BuildConfig + val interact: InteractionConfig + val inventory: InventoryConfig + val hotbar: HotbarConfig + val rotation: RotationConfig +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt b/common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt new file mode 100644 index 000000000..86a69bec0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.context + +class ConfiguredSafeContext( + safeContext: SafeContext, + configured: Configured +) : SafeContext by safeContext, Configured by configured \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt b/common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt new file mode 100644 index 000000000..87e8d2664 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.context + +import com.lambda.module.modules.client.TaskFlowModule + +object DefaultConfigs : Configured { + override val build = TaskFlowModule.build + override val interact = TaskFlowModule.interact + override val inventory = TaskFlowModule.inventory + override val hotbar = TaskFlowModule.hotbar + override val rotation = TaskFlowModule.rotation +} + +val Configured.breaking get() = build.breaking +val Configured.placing get() = build.placing \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/context/SafeContext.kt b/common/src/main/kotlin/com/lambda/context/SafeContext.kt index dd6b31aef..c37bf5fd1 100644 --- a/common/src/main/kotlin/com/lambda/context/SafeContext.kt +++ b/common/src/main/kotlin/com/lambda/context/SafeContext.kt @@ -46,9 +46,10 @@ import net.minecraft.client.world.ClientWorld * @property interaction The interaction manager for the player. * @property connection The network handler for the player. **/ -open class SafeContext internal constructor( - override val world: ClientWorld, - override val player: ClientPlayerEntity, - override val interaction: ClientPlayerInteractionManager, - override val connection: ClientPlayNetworkHandler, -) : AbstractContext() +interface SafeContext { + val mc: MinecraftClient + val world: ClientWorld + val player: ClientPlayerEntity + val interaction: ClientPlayerInteractionManager + val connection: ClientPlayNetworkHandler +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 1548a8408..004532137 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted import com.lambda.config.groups.InventoryConfig +import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.material.StackSelection.Companion.selectStack @@ -96,7 +97,7 @@ sealed class BreakResult : BuildResult() { override val pausesParent get() = true - override fun resolve() = + override fun Configured.resolve() = selectStack { isItem(badItem).not() }.transfer(MainHandContainer, inventory) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 1477b9c33..62ed706a8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalNear import com.lambda.config.groups.InventoryConfig +import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.StackSelection @@ -205,7 +206,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true - override fun resolve() = neededSelection + override fun Configured.resolve() = neededSelection .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find $neededSelection anywhere.") override fun SafeContext.buildRenderer() { @@ -241,7 +242,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true - override fun resolve() = + override fun Configured.resolve() = neededStack.select() .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find ${neededStack.item.name.string} anywhere.") diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 860dbe7d8..f2175dff7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted +import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.tasks.BuildTask.Companion.breakBlock @@ -109,7 +110,7 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_CANT_REPLACE - override fun resolve() = breakBlock(blockPos) + override fun Configured.resolve() = breakBlock(blockPos) } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt index ac28db61c..0b64108f6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt @@ -17,8 +17,9 @@ package com.lambda.interaction.construction.result +import com.lambda.context.Configured import com.lambda.task.Task interface Resolvable { - fun resolve(): Task<*> + fun Configured.resolve(): Task<*> } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt index e4d868965..209be44b1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.construction.simulation import baritone.api.pathing.goals.Goal +import com.lambda.context.DefaultConfigs import com.lambda.util.world.fastVectorOf import com.lambda.util.world.toFastVec import net.minecraft.util.math.BlockPos @@ -27,14 +28,15 @@ class BuildGoal( blocked: BlockPos ) : Goal { private val blockedVec = blocked.toFastVec() + private val configs = DefaultConfigs override fun isInGoal(x: Int, y: Int, z: Int): Boolean { val pos = fastVectorOf(x, y, z) - return sim.simulate(pos).any { it.rank.ordinal < 4 } && blockedVec != pos + return sim.simulate(pos, configs).any { it.rank.ordinal < 4 } && blockedVec != pos } override fun heuristic(x: Int, y: Int, z: Int): Double { - val bestRank = sim.simulate(fastVectorOf(x, y, z)) + val bestRank = sim.simulate(fastVectorOf(x, y, z), configs) .minOrNull()?.rank?.ordinal ?: 100000 return 1 / (bestRank.toDouble() + 1) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index cde372b92..92684c642 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -17,10 +17,9 @@ package com.lambda.interaction.construction.simulation -import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.InventoryConfig -import com.lambda.context.SafeContext +import com.lambda.context.Configured +import com.lambda.context.ConfiguredSafeContext +import com.lambda.context.placing import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.PlaceContext @@ -34,10 +33,8 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.request.placing.PlaceConfig import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.interaction.request.rotation.visibilty.PlaceDirection @@ -47,7 +44,6 @@ import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanS import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.interaction.request.rotation.visibilty.lookInDirection -import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState @@ -80,34 +76,28 @@ import net.minecraft.util.shape.VoxelShapes import kotlin.math.pow object BuildSimulator { - fun Blueprint.simulate( - eye: Vec3d, - interact: InteractionConfig = TaskFlowModule.interact, - rotation: RotationConfig = TaskFlowModule.rotation, - inventory: InventoryConfig = TaskFlowModule.inventory, - build: BuildConfig = TaskFlowModule.build, - ) = runSafe { - structure.entries.flatMap { (pos, target) -> - checkRequirements(pos, target, build)?.let { - return@flatMap setOf(it) - } - checkPlaceResults(pos, target, eye, build.placing, interact, rotation, inventory).let { - if (it.isEmpty()) return@let - return@flatMap it - } - checkBreakResults(pos, eye, build.placing, interact, rotation, inventory, build).let { - if (it.isEmpty()) return@let - return@flatMap it - } - warn("Nothing matched $pos $target") - emptySet() - }.toSet() - } ?: emptySet() + fun Configured.simulate(blueprint: Blueprint, eye: Vec3d) = + runSafe { + blueprint.structure.entries.flatMap { (pos, target) -> + checkRequirements(pos, target)?.let { + return@flatMap setOf(it) + } + checkPlaceResults(pos, target, eye).let { + if (it.isEmpty()) return@let + return@flatMap it + } + checkBreakResults(pos, eye).let { + if (it.isEmpty()) return@let + return@flatMap it + } + warn("Nothing matched $pos $target") + emptySet() + }.toSet() + } ?: emptySet() - private fun SafeContext.checkRequirements( + private fun ConfiguredSafeContext.checkRequirements( pos: BlockPos, target: TargetState, - build: BuildConfig ): BuildResult? {/* the chunk is not loaded */ if (!world.isChunkLoaded(pos)) { return BuildResult.ChunkNotLoaded(pos) @@ -148,14 +138,10 @@ object BuildSimulator { return null } - private fun SafeContext.checkPlaceResults( + private fun ConfiguredSafeContext.checkPlaceResults( pos: BlockPos, target: TargetState, eye: Vec3d, - place: PlaceConfig, - interact: InteractionConfig, - rotation: RotationConfig, - inventory: InventoryConfig ): Set { val acc = mutableSetOf() val targetPosState = blockState(pos) @@ -165,13 +151,13 @@ object BuildSimulator { val preprocessing = target.findProcessorForState() preprocessing.sides.forEach { neighbor -> - val hitPos = if (!place.airPlace.isEnabled() && targetPosState.isAir || targetPosState.isLiquid) + val hitPos = if (!placing.airPlace.isEnabled() && targetPosState.isAir || targetPosState.isLiquid) pos.offset(neighbor) else pos val hitSide = neighbor.opposite val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> - if (!outlineShape.isEmpty || !place.airPlace.isEnabled()) outlineShape + if (!outlineShape.isEmpty || !placing.airPlace.isEnabled()) outlineShape else VoxelShapes.fullCube() } if (voxelShape.isEmpty) return@forEach @@ -204,10 +190,10 @@ object BuildSimulator { val hit = if (interact.strictRayCast) { val rayCast = newRotation.rayCast(interact.interactReach, eye) when { - rayCast != null && (!place.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> + rayCast != null && (!placing.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> rayCast.blockResult - place.airPlace.isEnabled() -> { + placing.airPlace.isEnabled() -> { val hitVec = newRotation.castBox(box, interact.interactReach, eye) BlockHitResult(hitVec, hitSide, hitPos, false) } @@ -311,15 +297,15 @@ object BuildSimulator { } val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> - if (!place.rotateForPlace) { + if (!placing.rotateForPlace) { acc.add(basePlaceResult) return@forEach } true } ?: false - if (place.rotateForPlace) run rotate@ { - if (!place.axisRotate) { + if (placing.rotateForPlace) run rotate@ { + if (!placing.axisRotate) { fakePlayer.rotation = checkedHit.targetRotation simulatePlaceState()?.let { rotatedPlaceResult -> acc.add(rotatedPlaceResult) @@ -363,7 +349,7 @@ object BuildSimulator { val hitBlock = blockState(blockHit.blockPos).block val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks - val rotationRequest = if (place.axisRotate) { + val rotationRequest = if (placing.axisRotate) { lookInDirection(PlaceDirection.fromRotation(rot)) } else lookAt(rot, 0.001) @@ -400,14 +386,9 @@ object BuildSimulator { return acc } - private fun SafeContext.checkBreakResults( + private fun ConfiguredSafeContext.checkBreakResults( pos: BlockPos, eye: Vec3d, - place: PlaceConfig, - interact: InteractionConfig, - rotation: RotationConfig, - inventory: InventoryConfig, - build: BuildConfig ): Set { val acc = mutableSetOf() val state = blockState(pos) @@ -431,7 +412,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye, place, interact, rotation, inventory) + val submerge = checkPlaceResults(pos, TargetState.Solid, eye) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -446,9 +427,9 @@ object BuildSimulator { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { - checkPlaceResults(liquidPos, TargetState.Solid, eye, place, interact, rotation, inventory) + checkPlaceResults(liquidPos, TargetState.Solid, eye) } else { - checkBreakResults(liquidPos, eye, place, interact, rotation, inventory, build) + checkBreakResults(liquidPos, eye) } acc.addAll(submerge) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index 3a8cea455..bd24a9131 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -17,16 +17,12 @@ package com.lambda.interaction.construction.simulation -import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.InventoryConfig +import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.simulation.BuildSimulator.simulate -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.FastVector @@ -39,19 +35,11 @@ import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import java.awt.Color -data class Simulation( - val blueprint: Blueprint, - val interact: InteractionConfig = TaskFlowModule.interact, - val rotation: RotationConfig = TaskFlowModule.rotation, - val inventory: InventoryConfig = TaskFlowModule.inventory, - val build: BuildConfig = TaskFlowModule.build, -) { +data class Simulation(val blueprint: Blueprint) { private val cache: MutableMap> = mutableMapOf() private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.DEFAULT_EYE_HEIGHT.toDouble(), 0.5) - fun simulate( - pos: FastVector, - ) = cache.getOrPut(pos) { + fun simulate(pos: FastVector, configured: Configured) = cache.getOrPut(pos) { val view = pos.toView() val isOutOfBounds = blueprint.isOutOfBounds(view) val isTooFar = blueprint.getClosestPointTo(view).distanceTo(view) > 10.0 @@ -63,7 +51,9 @@ data class Simulation( if (!playerFitsIn(blockPos)) return@getOrPut emptySet() } - blueprint.simulate(view, interact, rotation, inventory, build) + configured.run { + simulate(blueprint, view) + } } fun goodPositions() = cache @@ -83,11 +73,6 @@ data class Simulation( companion object { fun Vec3d.playerBox(): Box = Box(x - 0.3, y, z - 0.3, x + 0.3, y + 1.8, z + 0.3).contract(1.0E-6) - fun Blueprint.simulation( - interact: InteractionConfig = TaskFlowModule.interact, - rotation: RotationConfig = TaskFlowModule.rotation, - inventory: InventoryConfig = TaskFlowModule.inventory, - build: BuildConfig = TaskFlowModule.build, - ) = Simulation(this, interact, rotation, inventory, build) + fun Blueprint.simulation() = Simulation(this) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt index 1a28863c4..7d79e9dc7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.material.container.containers +import com.lambda.context.DefaultConfigs import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer @@ -57,7 +58,7 @@ data class ShulkerBoxContainer( PlaceContainer(shulkerStack).then { placePos -> OpenContainer(placePos).then { screen -> withdraw(screen, selection).then { - breakAndCollectBlock(placePos).finally { + DefaultConfigs.breakAndCollectBlock(placePos).finally { success() } } @@ -78,7 +79,7 @@ data class ShulkerBoxContainer( PlaceContainer(shulkerStack).then { placePos -> OpenContainer(placePos).then { screen -> deposit(screen, selection).then { - breakAndCollectBlock(placePos).finally { + DefaultConfigs.breakAndCollectBlock(placePos).finally { success() } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 86b7a9aa2..f3617b2c3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -17,14 +17,19 @@ package com.lambda.module.modules.player -import com.lambda.config.groups.* +import com.lambda.config.groups.BuildSettings +import com.lambda.config.groups.InteractionSettings +import com.lambda.config.groups.InventorySettings +import com.lambda.config.groups.RotationSettings +import com.lambda.context.Configured +import com.lambda.context.DefaultConfigs import com.lambda.interaction.construction.blueprint.Blueprint.Companion.emptyStructure import com.lambda.interaction.construction.blueprint.PropagatingBlueprint.Companion.propagatingBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.Task import com.lambda.task.RootTask.run +import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.info @@ -45,7 +50,7 @@ object HighwayTools : Module( name = "HighwayTools", description = "Auto highway builder", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) -) { +), Configured by DefaultConfigs { private val page by setting("Page", Page.Structure) private val height by setting("Height", 4, 2..10, 1, "Height of the full tunnel tube including the pavement", " blocks") { page == Page.Structure } @@ -63,10 +68,10 @@ object HighwayTools : Module( private val distance by setting("Distance", -1, -1..1000000, 1, "Distance to build the highway/tunnel (negative for infinite)", " blocks") { page == Page.Structure } private val sliceSize by setting("Slice Size", 3, 1..5, 1, "Number of slices to build at once", " blocks") { page == Page.Structure } - private val build = BuildSettings(this) { page == Page.Build } - private val rotation = RotationSettings(this) { page == Page.Rotation } - private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } - private val inventory = InventorySettings(this) { page == Page.Inventory } + override val build = BuildSettings(this) { page == Page.Build } + override val rotation = RotationSettings(this) { page == Page.Rotation } + override val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } + override val inventory = InventorySettings(this) { page == Page.Inventory } private var octant = EightWayDirection.NORTH private var distanceMoved = 0 @@ -102,28 +107,25 @@ object HighwayTools : Module( } private fun buildHighway() { - runningTask = propagatingBlueprint { - if (distanceMoved < distance || distance < 0) { - var structure = emptyStructure() - val slice = generateSlice() - repeat(sliceSize) { - structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) - val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) - currentPos = currentPos.add(vec) + runningTask = build( + propagatingBlueprint { + if (distanceMoved < distance || distance < 0) { + var structure = emptyStructure() + val slice = generateSlice() + repeat(sliceSize) { + structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) + val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) + currentPos = currentPos.add(vec) + } + distanceMoved += sliceSize + structure + } else { + this@HighwayTools.info("Highway built") + disable() + emptyStructure() } - distanceMoved += sliceSize - structure - } else { - this@HighwayTools.info("Highway built") - disable() - emptyStructure() - } - }.build( - collectDrops = build.collectDrops, - build = build, - rotation = rotation, - interact = interact, - inventory = inventory, + }, + collectDrops = build.collectDrops ).run() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt index 3242b8ffb..52d4c64f2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt @@ -18,13 +18,14 @@ package com.lambda.module.modules.player import com.lambda.config.groups.InventorySettings +import com.lambda.context.DefaultConfigs import com.lambda.event.events.InventoryEvent import com.lambda.event.events.PlayerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.Task import com.lambda.task.RootTask.run +import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock import com.lambda.task.tasks.OpenContainer import com.lambda.task.tasks.PlaceContainer @@ -65,7 +66,7 @@ object InventoryTweaks : Module( if (event.screenHandler != lastOpenScreen) return@listen lastOpenScreen = null placedPos?.let { - lastBreak = breakAndCollectBlock(it).run() + lastBreak = DefaultConfigs.breakAndCollectBlock(it).run() placedPos = null } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index da3574bc3..29dda17c0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -17,12 +17,14 @@ package com.lambda.module.modules.player +import com.lambda.context.Configured +import com.lambda.context.DefaultConfigs import com.lambda.interaction.construction.blueprint.TickingBlueprint.Companion.tickingBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.Task import com.lambda.task.RootTask.run +import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState @@ -32,7 +34,7 @@ object Nuker : Module( name = "Nuker", description = "Breaks blocks around you", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) -) { +), Configured by DefaultConfigs { private val height by setting("Height", 4, 1..8, 1) private val width by setting("Width", 4, 1..8, 1) private val flatten by setting("Flatten", true) @@ -43,7 +45,8 @@ object Nuker : Module( init { onEnable { - task = tickingBlueprint { + task = build( + tickingBlueprint { val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) .asSequence() .map { it.blockPos } @@ -62,9 +65,7 @@ object Nuker : Module( selection } - // ToDo: Add build setting delegates - .build() - task?.run() + ).run() } onDisable { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 2f3ab7fc6..4a2ef65f9 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -22,7 +22,9 @@ import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings -import com.lambda.context.SafeContext +import com.lambda.context.Configured +import com.lambda.context.ConfiguredSafeContext +import com.lambda.context.DefaultConfigs import com.lambda.event.events.PlayerEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -34,6 +36,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos @@ -43,16 +46,17 @@ object PacketMine : Module( "PacketMine", "automatically breaks blocks, and does it faster", setOf(ModuleTag.PLAYER) -) { +), Configured by DefaultConfigs { private val page by setting("Page", Page.Build) - private val build = BuildSettings(this) { page == Page.Build } - private val breakConfig = build.breaking - private val rotation = RotationSettings(this) { page == Page.Rotation } - private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } - private val inventory = InventorySettings(this) { page == Page.Inventory } - private val hotbar = HotbarSettings(this) { page == Page.Hotbar } - private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } + override val build = BuildSettings(this) { page == Page.Build } + val breaking = build.breaking + val placing = build.placing + override val rotation = RotationSettings(this) { page == Page.Rotation } + override val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } + override val inventory = InventorySettings(this) { page == Page.Inventory } + override val hotbar = HotbarSettings(this) { page == Page.Hotbar } + private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breaking.reBreak } private val pendingInteractionsList = ConcurrentLinkedQueue() @@ -71,7 +75,7 @@ object PacketMine : Module( //ToDo: run on every tick stage listen { - if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen + if (!breaking.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen val reBreak = reBreakPos ?: return@listen requestBreakManager(reBreak) } @@ -80,7 +84,7 @@ object PacketMine : Module( listen { event -> event.cancel() if (breakingPositions.any { it == event.pos }) return@listen - val secondary = if (breakConfig.doubleBreak) { + val secondary = if (breaking.doubleBreak) { breakingPositions[1] ?: breakingPositions[0] } else null requestBreakManager(event.pos, secondary) @@ -88,7 +92,9 @@ object PacketMine : Module( } listen { - if (!attackedThisTick) requestBreakManager(*breakingPositions.toList().toTypedArray()) + if (!attackedThisTick) runSafe { + requestBreakManager(*breakingPositions.toList().toTypedArray()) + } } onDisable { @@ -99,27 +105,29 @@ object PacketMine : Module( } } - private fun SafeContext.requestBreakManager(vararg requestPositions: BlockPos?) { + private fun requestBreakManager(vararg requestPositions: BlockPos?) { if (requestPositions.isEmpty()) return - val request = BreakRequest( - breakContexts(requestPositions.filterNotNull()), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onAccept = { - if (breakConfig.doubleBreak && breakingPositions[1] == null) { - breakingPositions[1] = breakingPositions[0] - } - breakingPositions[0] = it - reBreakPos = null - }, - onCancel = { nullifyBreakPos(it, true) }, - onBreak = { - breaks++ - nullifyBreakPos(it) - }, - onReBreakStart = { reBreakPos = it }, - onReBreak = { reBreakPos = it }, - onItemDrop = { _ -> itemDrops++ } - ) - breakConfig.request(request) + runSafe { + val request = BreakRequest( + breakContexts(requestPositions.filterNotNull()), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, + onAccept = { + if (this@PacketMine.breaking.doubleBreak && breakingPositions[1] == null) { + breakingPositions[1] = breakingPositions[0] + } + breakingPositions[0] = it + reBreakPos = null + }, + onCancel = { nullifyBreakPos(it, true) }, + onBreak = { + breaks++ + nullifyBreakPos(it) + }, + onReBreakStart = { reBreakPos = it }, + onReBreak = { reBreakPos = it }, + onItemDrop = { _ -> itemDrops++ } + ) + this@PacketMine.breaking.request(request) + } } private fun nullifyBreakPos(pos: BlockPos, includeReBreak: Boolean = false) { @@ -134,18 +142,12 @@ object PacketMine : Module( } } - private fun SafeContext.breakContexts(breakPositions: Collection) = - breakPositions - .associateWith { TargetState.State(blockState(it).fluidState.blockState) } - .toBlueprint() - .simulate( - player.eyePos, - interact = interact, - rotation = rotation, - inventory = inventory, - build = build - ) - .filterIsInstance() + private fun ConfiguredSafeContext.breakContexts(breakPositions: Collection) = + simulate( + breakPositions + .associateWith { TargetState.State(blockState(it).fluidState.blockState) } + .toBlueprint(), player.eyePos + ).filterIsInstance() .map { it.context } enum class Page { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt b/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt index c3303369a..01cab2fa8 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt @@ -17,6 +17,8 @@ package com.lambda.module.modules.player +import com.lambda.context.Configured +import com.lambda.context.DefaultConfigs import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.builders.buildOutline @@ -25,8 +27,8 @@ import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.t import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.Task import com.lambda.task.RootTask.run +import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockBox @@ -38,7 +40,7 @@ object WorldEater : Module( name = "WorldEater", description = "Eats the world", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) -) { +), Configured by DefaultConfigs { // private val height by setting("Height", 4, 1..10, 1) // private val width by setting("Width", 6, 1..30, 1) private val pos1 by setting("Position 1", BlockPos(351, 104, 103)) @@ -74,10 +76,10 @@ object WorldEater : Module( private fun buildLayer() { work.firstOrNull()?.let { box -> - runningTask = build { + runningTask = build( box.toStructure(TargetState.Air) .toBlueprint() - }.finally { + ).finally { work.removeFirstOrNull() buildLayer() }.run() diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index abd3f5ce2..caa305288 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -19,9 +19,7 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.GoalBlock import com.lambda.Lambda.LOG -import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.InventoryConfig +import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -44,9 +42,7 @@ import com.lambda.interaction.construction.simulation.Simulation.Companion.simul import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer import com.lambda.interaction.request.breaking.BreakRequest -import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.placing.PlaceRequest -import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -63,12 +59,8 @@ class BuildTask @Ta5kBuilder constructor( private val blueprint: Blueprint, private val finishOnDone: Boolean = true, private val collectDrops: Boolean = TaskFlowModule.build.collectDrops, - private val build: BuildConfig = TaskFlowModule.build, - private val rotation: RotationConfig = TaskFlowModule.rotation, - private val interact: InteractionConfig = TaskFlowModule.interact, - private val inventory: InventoryConfig = TaskFlowModule.inventory, - private val hotbar: HotbarConfig = TaskFlowModule.hotbar, -) : Task() { + configured: Configured +) : Task(), Configured by configured { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" private val pendingInteractions = ConcurrentLinkedQueue() @@ -95,7 +87,7 @@ class BuildTask @Ta5kBuilder constructor( listen { if (collectDrops()) return@listen - val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) + val results = simulate(blueprint, player.eyePos) TaskFlowModule.drawables = results .filterIsInstance() @@ -125,7 +117,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { if (!build.pathing) return@listen - val sim = blueprint.simulation(interact, rotation, inventory, build) + val sim = blueprint.simulation() val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) } @@ -180,8 +172,9 @@ class BuildTask @Ta5kBuilder constructor( is Resolvable -> { LOG.info("Resolving: ${bestResult.name}") - - bestResult.resolve().execute(this@BuildTask) + bestResult.run { + resolve().execute(this@BuildTask) + } } } } @@ -228,62 +221,44 @@ class BuildTask @Ta5kBuilder constructor( companion object { @Ta5kBuilder - fun build( + fun Configured.build( + blueprint: () -> Blueprint, finishOnDone: Boolean = true, collectDrops: Boolean = TaskFlowModule.build.collectDrops, - build: BuildConfig = TaskFlowModule.build, - rotation: RotationConfig = TaskFlowModule.rotation, - interact: InteractionConfig = TaskFlowModule.interact, - inventory: InventoryConfig = TaskFlowModule.inventory, - blueprint: () -> Blueprint, - ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) + ) = BuildTask(blueprint(), finishOnDone, collectDrops, this) @Ta5kBuilder - fun Structure.build( + fun Configured.build( + structure: Structure, finishOnDone: Boolean = true, - collectDrops: Boolean = TaskFlowModule.build.collectDrops, - build: BuildConfig = TaskFlowModule.build, - rotation: RotationConfig = TaskFlowModule.rotation, - interact: InteractionConfig = TaskFlowModule.interact, - inventory: InventoryConfig = TaskFlowModule.inventory, - ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) + collectDrops: Boolean = TaskFlowModule.build.collectDrops + ) = BuildTask(structure.toBlueprint(), finishOnDone, collectDrops, this) @Ta5kBuilder - fun Blueprint.build( + fun Configured.build( + blueprint: Blueprint, finishOnDone: Boolean = true, - collectDrops: Boolean = TaskFlowModule.build.collectDrops, - build: BuildConfig = TaskFlowModule.build, - rotation: RotationConfig = TaskFlowModule.rotation, - interact: InteractionConfig = TaskFlowModule.interact, - inventory: InventoryConfig = TaskFlowModule.inventory, - ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory) + collectDrops: Boolean = TaskFlowModule.build.collectDrops + ) = BuildTask(blueprint, finishOnDone, collectDrops, this) @Ta5kBuilder - fun breakAndCollectBlock( + fun Configured.breakAndCollectBlock( blockPos: BlockPos, finishOnDone: Boolean = true, collectDrops: Boolean = true, - build: BuildConfig = TaskFlowModule.build, - rotation: RotationConfig = TaskFlowModule.rotation, - interact: InteractionConfig = TaskFlowModule.interact, - inventory: InventoryConfig = TaskFlowModule.inventory, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact, inventory + finishOnDone, collectDrops, this ) @Ta5kBuilder - fun breakBlock( + fun Configured.breakBlock( blockPos: BlockPos, finishOnDone: Boolean = true, collectDrops: Boolean = TaskFlowModule.build.collectDrops, - build: BuildConfig = TaskFlowModule.build, - rotation: RotationConfig = TaskFlowModule.rotation, - interact: InteractionConfig = TaskFlowModule.interact, - inventory: InventoryConfig = TaskFlowModule.inventory, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact, inventory + finishOnDone, collectDrops, this ) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index a568a27ee..b6066ab17 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -20,6 +20,7 @@ package com.lambda.task.tasks import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InventoryConfig +import com.lambda.context.DefaultConfigs import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint @@ -54,10 +55,12 @@ class PlaceContainer @Ta5kBuilder constructor( val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4) .map { it.blockPos } .flatMap { - it.blockPos - .toStructure(TargetState.Stack(startStack)) - .toBlueprint() - .simulate(player.eyePos) + DefaultConfigs.simulate( + it.blockPos + .toStructure(TargetState.Stack(startStack)) + .toBlueprint(), + player.eyePos + ) } val succeeds = results.filterIsInstance().filter { @@ -67,16 +70,11 @@ class PlaceContainer @Ta5kBuilder constructor( canBeOpened(startStack, it.blockPos, it.context.result.side) } (succeeds + wrongStacks).minOrNull()?.let { result -> - build( - build = build, - rotation = rotation, - interact = interact, - inventory = inventory, - ) { + DefaultConfigs.build( result.blockPos .toStructure(TargetState.Stack(startStack)) .toBlueprint() - }.finally { + ).finally { success(result.blockPos) }.execute(this@PlaceContainer) } ?: { diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index 50a06eb2a..8449ceea5 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -19,6 +19,8 @@ package com.lambda.threading import com.lambda.Lambda.mc import com.lambda.context.ClientContext +import com.lambda.context.Configured +import com.lambda.context.ConfiguredSafeContext import com.lambda.context.SafeContext import com.lambda.event.EventFlow import com.mojang.blaze3d.systems.RenderSystem.isOnRenderThread @@ -42,7 +44,10 @@ import java.util.concurrent.CompletableFuture * @return The result of the block execution if the context is safe, null otherwise. */ inline fun runSafe(block: SafeContext.() -> T) = - ClientContext().toSafe()?.let { block(it) } + ClientContext().toSafe()?.block() + +inline fun Configured.runSafe(block: ConfiguredSafeContext.() -> T) = + ClientContext().toSafeConfigured(this)?.block() /** * This function is used to execute a block of code on a new thread running asynchronously to the game thread. From 7f7040cca522896cc7bd749c3744c177a1f8167d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 9 May 2025 19:45:14 +0100 Subject: [PATCH 186/364] Revert "cleaner configs idea" This reverts commit 5de89357a05aa92e873e47150926aca598bedf99. --- .../lambda/command/commands/BuildCommand.kt | 14 ++- .../com/lambda/context/AbstractContext.kt | 41 +++++++++ .../com/lambda/context/ClientContext.kt | 27 ++---- .../kotlin/com/lambda/context/Configured.kt | 32 ------- .../lambda/context/ConfiguredSafeContext.kt | 23 ----- .../com/lambda/context/DefaultConfigs.kt | 31 ------- .../kotlin/com/lambda/context/SafeContext.kt | 13 ++- .../construction/result/BreakResult.kt | 3 +- .../construction/result/BuildResult.kt | 5 +- .../construction/result/PlaceResult.kt | 3 +- .../construction/result/Resolvable.kt | 3 +- .../construction/simulation/BuildGoal.kt | 6 +- .../construction/simulation/BuildSimulator.kt | 89 ++++++++++-------- .../construction/simulation/Simulation.kt | 29 ++++-- .../containers/ShulkerBoxContainer.kt | 5 +- .../module/modules/player/HighwayTools.kt | 58 ++++++------ .../module/modules/player/InventoryTweaks.kt | 5 +- .../com/lambda/module/modules/player/Nuker.kt | 13 ++- .../module/modules/player/PacketMine.kt | 90 +++++++++---------- .../module/modules/player/WorldEater.kt | 10 +-- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 71 ++++++++++----- .../com/lambda/task/tasks/PlaceContainer.kt | 20 +++-- .../kotlin/com/lambda/threading/Threading.kt | 7 +- 23 files changed, 288 insertions(+), 310 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/context/AbstractContext.kt delete mode 100644 common/src/main/kotlin/com/lambda/context/Configured.kt delete mode 100644 common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt delete mode 100644 common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt diff --git a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt index edab28327..12a1dcb87 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt @@ -25,7 +25,6 @@ import com.lambda.brigadier.argument.value import com.lambda.brigadier.executeWithResult import com.lambda.brigadier.required import com.lambda.command.LambdaCommand -import com.lambda.context.DefaultConfigs import com.lambda.interaction.construction.StructureRegistry import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint @@ -62,14 +61,11 @@ object BuildCommand : LambdaCommand( .loadStructureByRelativePath(Path.of(pathString)) .let { template -> info("Building structure $pathString with dimensions ${template.size.toShortString()} created by ${template.author}") - DefaultConfigs.run { - lastBuildTask = build( - template - .toStructure() - .move(player.blockPos) - .toBlueprint() - ).run() - } + lastBuildTask = template.toStructure() + .move(player.blockPos) + .toBlueprint() + .build() + .run() return@executeWithResult success() } diff --git a/common/src/main/kotlin/com/lambda/context/AbstractContext.kt b/common/src/main/kotlin/com/lambda/context/AbstractContext.kt new file mode 100644 index 000000000..4c5e94335 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/context/AbstractContext.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.context + +import net.minecraft.client.MinecraftClient +import net.minecraft.client.network.ClientPlayNetworkHandler +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.client.network.ClientPlayerInteractionManager +import net.minecraft.client.world.ClientWorld + +/** + * Representing an abstract context in the [MinecraftClient]. + * + * @property mc The Minecraft client instance. + * @property world The world in which the player is currently located, or `null` if the world is not available. + * @property player The player entity, or `null` if the player is not available. + * @property interaction The interaction manager for the player, or `null` if the interaction manager is not available. + * @property connection The network handler for the player, or `null` if the network handler is not available. + */ +abstract class AbstractContext { + val mc: MinecraftClient = MinecraftClient.getInstance() + abstract val world: ClientWorld? + abstract val player: ClientPlayerEntity? + abstract val interaction: ClientPlayerInteractionManager? + abstract val connection: ClientPlayNetworkHandler? +} diff --git a/common/src/main/kotlin/com/lambda/context/ClientContext.kt b/common/src/main/kotlin/com/lambda/context/ClientContext.kt index b362b07cf..906b72699 100644 --- a/common/src/main/kotlin/com/lambda/context/ClientContext.kt +++ b/common/src/main/kotlin/com/lambda/context/ClientContext.kt @@ -35,29 +35,16 @@ import net.minecraft.client.world.ClientWorld * * @function toSafe Converts the `ClientContext` to a `SafeContext` if all properties are not `null`, or returns `null` otherwise. */ -open class ClientContext { - val mc: MinecraftClient = MinecraftClient.getInstance() - val clientWorld: ClientWorld? = mc.world - val clientPlayer: ClientPlayerEntity? = mc.player - val clientInteraction: ClientPlayerInteractionManager? = mc.interactionManager - val clientConnection: ClientPlayNetworkHandler? = mc.networkHandler +open class ClientContext : AbstractContext() { + final override val world: ClientWorld? = mc.world + final override val player: ClientPlayerEntity? = mc.player + final override val interaction: ClientPlayerInteractionManager? = mc.interactionManager + final override val connection: ClientPlayNetworkHandler? = mc.networkHandler fun toSafe(): SafeContext? { - if (clientWorld == null || clientPlayer == null || clientInteraction == null || clientConnection == null) { + if (world == null || player == null || interaction == null || connection == null) { return null } - return object : SafeContext { - override val mc = this@ClientContext.mc - override val world: ClientWorld = clientWorld - override val player: ClientPlayerEntity = clientPlayer - override val interaction: ClientPlayerInteractionManager = clientInteraction - override val connection: ClientPlayNetworkHandler = clientConnection - } - } - - fun toSafeConfigured(configured: Configured): ConfiguredSafeContext? { - return toSafe()?.let { safeContext -> - ConfiguredSafeContext(safeContext, configured) - } + return SafeContext(world, player, interaction, connection) } } diff --git a/common/src/main/kotlin/com/lambda/context/Configured.kt b/common/src/main/kotlin/com/lambda/context/Configured.kt deleted file mode 100644 index 8115d4d68..000000000 --- a/common/src/main/kotlin/com/lambda/context/Configured.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.context - -import com.lambda.config.groups.BuildConfig -import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.InventoryConfig -import com.lambda.interaction.request.hotbar.HotbarConfig -import com.lambda.interaction.request.rotation.RotationConfig - -interface Configured { - val build: BuildConfig - val interact: InteractionConfig - val inventory: InventoryConfig - val hotbar: HotbarConfig - val rotation: RotationConfig -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt b/common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt deleted file mode 100644 index 86a69bec0..000000000 --- a/common/src/main/kotlin/com/lambda/context/ConfiguredSafeContext.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.context - -class ConfiguredSafeContext( - safeContext: SafeContext, - configured: Configured -) : SafeContext by safeContext, Configured by configured \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt b/common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt deleted file mode 100644 index 87e8d2664..000000000 --- a/common/src/main/kotlin/com/lambda/context/DefaultConfigs.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.context - -import com.lambda.module.modules.client.TaskFlowModule - -object DefaultConfigs : Configured { - override val build = TaskFlowModule.build - override val interact = TaskFlowModule.interact - override val inventory = TaskFlowModule.inventory - override val hotbar = TaskFlowModule.hotbar - override val rotation = TaskFlowModule.rotation -} - -val Configured.breaking get() = build.breaking -val Configured.placing get() = build.placing \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/context/SafeContext.kt b/common/src/main/kotlin/com/lambda/context/SafeContext.kt index c37bf5fd1..dd6b31aef 100644 --- a/common/src/main/kotlin/com/lambda/context/SafeContext.kt +++ b/common/src/main/kotlin/com/lambda/context/SafeContext.kt @@ -46,10 +46,9 @@ import net.minecraft.client.world.ClientWorld * @property interaction The interaction manager for the player. * @property connection The network handler for the player. **/ -interface SafeContext { - val mc: MinecraftClient - val world: ClientWorld - val player: ClientPlayerEntity - val interaction: ClientPlayerInteractionManager - val connection: ClientPlayNetworkHandler -} +open class SafeContext internal constructor( + override val world: ClientWorld, + override val player: ClientPlayerEntity, + override val interaction: ClientPlayerInteractionManager, + override val connection: ClientPlayNetworkHandler, +) : AbstractContext() diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 004532137..1548a8408 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -20,7 +20,6 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted import com.lambda.config.groups.InventoryConfig -import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.material.StackSelection.Companion.selectStack @@ -97,7 +96,7 @@ sealed class BreakResult : BuildResult() { override val pausesParent get() = true - override fun Configured.resolve() = + override fun resolve() = selectStack { isItem(badItem).not() }.transfer(MainHandContainer, inventory) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 62ed706a8..1477b9c33 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -20,7 +20,6 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalNear import com.lambda.config.groups.InventoryConfig -import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.StackSelection @@ -206,7 +205,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true - override fun Configured.resolve() = neededSelection + override fun resolve() = neededSelection .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find $neededSelection anywhere.") override fun SafeContext.buildRenderer() { @@ -242,7 +241,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true - override fun Configured.resolve() = + override fun resolve() = neededStack.select() .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find ${neededStack.item.name.string} anywhere.") diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index f2175dff7..860dbe7d8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -19,7 +19,6 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted -import com.lambda.context.Configured import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.tasks.BuildTask.Companion.breakBlock @@ -110,7 +109,7 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_CANT_REPLACE - override fun Configured.resolve() = breakBlock(blockPos) + override fun resolve() = breakBlock(blockPos) } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt index 0b64108f6..ac28db61c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt @@ -17,9 +17,8 @@ package com.lambda.interaction.construction.result -import com.lambda.context.Configured import com.lambda.task.Task interface Resolvable { - fun Configured.resolve(): Task<*> + fun resolve(): Task<*> } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt index 209be44b1..e4d868965 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.construction.simulation import baritone.api.pathing.goals.Goal -import com.lambda.context.DefaultConfigs import com.lambda.util.world.fastVectorOf import com.lambda.util.world.toFastVec import net.minecraft.util.math.BlockPos @@ -28,15 +27,14 @@ class BuildGoal( blocked: BlockPos ) : Goal { private val blockedVec = blocked.toFastVec() - private val configs = DefaultConfigs override fun isInGoal(x: Int, y: Int, z: Int): Boolean { val pos = fastVectorOf(x, y, z) - return sim.simulate(pos, configs).any { it.rank.ordinal < 4 } && blockedVec != pos + return sim.simulate(pos).any { it.rank.ordinal < 4 } && blockedVec != pos } override fun heuristic(x: Int, y: Int, z: Int): Double { - val bestRank = sim.simulate(fastVectorOf(x, y, z), configs) + val bestRank = sim.simulate(fastVectorOf(x, y, z)) .minOrNull()?.rank?.ordinal ?: 100000 return 1 / (bestRank.toDouble() + 1) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 92684c642..cde372b92 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -17,9 +17,10 @@ package com.lambda.interaction.construction.simulation -import com.lambda.context.Configured -import com.lambda.context.ConfiguredSafeContext -import com.lambda.context.placing +import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig +import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.PlaceContext @@ -33,8 +34,10 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.request.placing.PlaceConfig import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.interaction.request.rotation.visibilty.PlaceDirection @@ -44,6 +47,7 @@ import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanS import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.interaction.request.rotation.visibilty.lookInDirection +import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState @@ -76,28 +80,34 @@ import net.minecraft.util.shape.VoxelShapes import kotlin.math.pow object BuildSimulator { - fun Configured.simulate(blueprint: Blueprint, eye: Vec3d) = - runSafe { - blueprint.structure.entries.flatMap { (pos, target) -> - checkRequirements(pos, target)?.let { - return@flatMap setOf(it) - } - checkPlaceResults(pos, target, eye).let { - if (it.isEmpty()) return@let - return@flatMap it - } - checkBreakResults(pos, eye).let { - if (it.isEmpty()) return@let - return@flatMap it - } - warn("Nothing matched $pos $target") - emptySet() - }.toSet() - } ?: emptySet() + fun Blueprint.simulate( + eye: Vec3d, + interact: InteractionConfig = TaskFlowModule.interact, + rotation: RotationConfig = TaskFlowModule.rotation, + inventory: InventoryConfig = TaskFlowModule.inventory, + build: BuildConfig = TaskFlowModule.build, + ) = runSafe { + structure.entries.flatMap { (pos, target) -> + checkRequirements(pos, target, build)?.let { + return@flatMap setOf(it) + } + checkPlaceResults(pos, target, eye, build.placing, interact, rotation, inventory).let { + if (it.isEmpty()) return@let + return@flatMap it + } + checkBreakResults(pos, eye, build.placing, interact, rotation, inventory, build).let { + if (it.isEmpty()) return@let + return@flatMap it + } + warn("Nothing matched $pos $target") + emptySet() + }.toSet() + } ?: emptySet() - private fun ConfiguredSafeContext.checkRequirements( + private fun SafeContext.checkRequirements( pos: BlockPos, target: TargetState, + build: BuildConfig ): BuildResult? {/* the chunk is not loaded */ if (!world.isChunkLoaded(pos)) { return BuildResult.ChunkNotLoaded(pos) @@ -138,10 +148,14 @@ object BuildSimulator { return null } - private fun ConfiguredSafeContext.checkPlaceResults( + private fun SafeContext.checkPlaceResults( pos: BlockPos, target: TargetState, eye: Vec3d, + place: PlaceConfig, + interact: InteractionConfig, + rotation: RotationConfig, + inventory: InventoryConfig ): Set { val acc = mutableSetOf() val targetPosState = blockState(pos) @@ -151,13 +165,13 @@ object BuildSimulator { val preprocessing = target.findProcessorForState() preprocessing.sides.forEach { neighbor -> - val hitPos = if (!placing.airPlace.isEnabled() && targetPosState.isAir || targetPosState.isLiquid) + val hitPos = if (!place.airPlace.isEnabled() && targetPosState.isAir || targetPosState.isLiquid) pos.offset(neighbor) else pos val hitSide = neighbor.opposite val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> - if (!outlineShape.isEmpty || !placing.airPlace.isEnabled()) outlineShape + if (!outlineShape.isEmpty || !place.airPlace.isEnabled()) outlineShape else VoxelShapes.fullCube() } if (voxelShape.isEmpty) return@forEach @@ -190,10 +204,10 @@ object BuildSimulator { val hit = if (interact.strictRayCast) { val rayCast = newRotation.rayCast(interact.interactReach, eye) when { - rayCast != null && (!placing.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> + rayCast != null && (!place.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> rayCast.blockResult - placing.airPlace.isEnabled() -> { + place.airPlace.isEnabled() -> { val hitVec = newRotation.castBox(box, interact.interactReach, eye) BlockHitResult(hitVec, hitSide, hitPos, false) } @@ -297,15 +311,15 @@ object BuildSimulator { } val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> - if (!placing.rotateForPlace) { + if (!place.rotateForPlace) { acc.add(basePlaceResult) return@forEach } true } ?: false - if (placing.rotateForPlace) run rotate@ { - if (!placing.axisRotate) { + if (place.rotateForPlace) run rotate@ { + if (!place.axisRotate) { fakePlayer.rotation = checkedHit.targetRotation simulatePlaceState()?.let { rotatedPlaceResult -> acc.add(rotatedPlaceResult) @@ -349,7 +363,7 @@ object BuildSimulator { val hitBlock = blockState(blockHit.blockPos).block val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks - val rotationRequest = if (placing.axisRotate) { + val rotationRequest = if (place.axisRotate) { lookInDirection(PlaceDirection.fromRotation(rot)) } else lookAt(rot, 0.001) @@ -386,9 +400,14 @@ object BuildSimulator { return acc } - private fun ConfiguredSafeContext.checkBreakResults( + private fun SafeContext.checkBreakResults( pos: BlockPos, eye: Vec3d, + place: PlaceConfig, + interact: InteractionConfig, + rotation: RotationConfig, + inventory: InventoryConfig, + build: BuildConfig ): Set { val acc = mutableSetOf() val state = blockState(pos) @@ -412,7 +431,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye) + val submerge = checkPlaceResults(pos, TargetState.Solid, eye, place, interact, rotation, inventory) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -427,9 +446,9 @@ object BuildSimulator { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { - checkPlaceResults(liquidPos, TargetState.Solid, eye) + checkPlaceResults(liquidPos, TargetState.Solid, eye, place, interact, rotation, inventory) } else { - checkBreakResults(liquidPos, eye) + checkBreakResults(liquidPos, eye, place, interact, rotation, inventory, build) } acc.addAll(submerge) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index bd24a9131..3a8cea455 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -17,12 +17,16 @@ package com.lambda.interaction.construction.simulation -import com.lambda.context.Configured +import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.simulation.BuildSimulator.simulate +import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.FastVector @@ -35,11 +39,19 @@ import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import java.awt.Color -data class Simulation(val blueprint: Blueprint) { +data class Simulation( + val blueprint: Blueprint, + val interact: InteractionConfig = TaskFlowModule.interact, + val rotation: RotationConfig = TaskFlowModule.rotation, + val inventory: InventoryConfig = TaskFlowModule.inventory, + val build: BuildConfig = TaskFlowModule.build, +) { private val cache: MutableMap> = mutableMapOf() private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.DEFAULT_EYE_HEIGHT.toDouble(), 0.5) - fun simulate(pos: FastVector, configured: Configured) = cache.getOrPut(pos) { + fun simulate( + pos: FastVector, + ) = cache.getOrPut(pos) { val view = pos.toView() val isOutOfBounds = blueprint.isOutOfBounds(view) val isTooFar = blueprint.getClosestPointTo(view).distanceTo(view) > 10.0 @@ -51,9 +63,7 @@ data class Simulation(val blueprint: Blueprint) { if (!playerFitsIn(blockPos)) return@getOrPut emptySet() } - configured.run { - simulate(blueprint, view) - } + blueprint.simulate(view, interact, rotation, inventory, build) } fun goodPositions() = cache @@ -73,6 +83,11 @@ data class Simulation(val blueprint: Blueprint) { companion object { fun Vec3d.playerBox(): Box = Box(x - 0.3, y, z - 0.3, x + 0.3, y + 1.8, z + 0.3).contract(1.0E-6) - fun Blueprint.simulation() = Simulation(this) + fun Blueprint.simulation( + interact: InteractionConfig = TaskFlowModule.interact, + rotation: RotationConfig = TaskFlowModule.rotation, + inventory: InventoryConfig = TaskFlowModule.inventory, + build: BuildConfig = TaskFlowModule.build, + ) = Simulation(this, interact, rotation, inventory, build) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt index 7d79e9dc7..1a28863c4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.DefaultConfigs import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer @@ -58,7 +57,7 @@ data class ShulkerBoxContainer( PlaceContainer(shulkerStack).then { placePos -> OpenContainer(placePos).then { screen -> withdraw(screen, selection).then { - DefaultConfigs.breakAndCollectBlock(placePos).finally { + breakAndCollectBlock(placePos).finally { success() } } @@ -79,7 +78,7 @@ data class ShulkerBoxContainer( PlaceContainer(shulkerStack).then { placePos -> OpenContainer(placePos).then { screen -> deposit(screen, selection).then { - DefaultConfigs.breakAndCollectBlock(placePos).finally { + breakAndCollectBlock(placePos).finally { success() } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index f3617b2c3..86b7a9aa2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -17,19 +17,14 @@ package com.lambda.module.modules.player -import com.lambda.config.groups.BuildSettings -import com.lambda.config.groups.InteractionSettings -import com.lambda.config.groups.InventorySettings -import com.lambda.config.groups.RotationSettings -import com.lambda.context.Configured -import com.lambda.context.DefaultConfigs +import com.lambda.config.groups.* import com.lambda.interaction.construction.blueprint.Blueprint.Companion.emptyStructure import com.lambda.interaction.construction.blueprint.PropagatingBlueprint.Companion.propagatingBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run import com.lambda.task.Task +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.info @@ -50,7 +45,7 @@ object HighwayTools : Module( name = "HighwayTools", description = "Auto highway builder", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) -), Configured by DefaultConfigs { +) { private val page by setting("Page", Page.Structure) private val height by setting("Height", 4, 2..10, 1, "Height of the full tunnel tube including the pavement", " blocks") { page == Page.Structure } @@ -68,10 +63,10 @@ object HighwayTools : Module( private val distance by setting("Distance", -1, -1..1000000, 1, "Distance to build the highway/tunnel (negative for infinite)", " blocks") { page == Page.Structure } private val sliceSize by setting("Slice Size", 3, 1..5, 1, "Number of slices to build at once", " blocks") { page == Page.Structure } - override val build = BuildSettings(this) { page == Page.Build } - override val rotation = RotationSettings(this) { page == Page.Rotation } - override val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } - override val inventory = InventorySettings(this) { page == Page.Inventory } + private val build = BuildSettings(this) { page == Page.Build } + private val rotation = RotationSettings(this) { page == Page.Rotation } + private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } + private val inventory = InventorySettings(this) { page == Page.Inventory } private var octant = EightWayDirection.NORTH private var distanceMoved = 0 @@ -107,25 +102,28 @@ object HighwayTools : Module( } private fun buildHighway() { - runningTask = build( - propagatingBlueprint { - if (distanceMoved < distance || distance < 0) { - var structure = emptyStructure() - val slice = generateSlice() - repeat(sliceSize) { - structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) - val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) - currentPos = currentPos.add(vec) - } - distanceMoved += sliceSize - structure - } else { - this@HighwayTools.info("Highway built") - disable() - emptyStructure() + runningTask = propagatingBlueprint { + if (distanceMoved < distance || distance < 0) { + var structure = emptyStructure() + val slice = generateSlice() + repeat(sliceSize) { + structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) + val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) + currentPos = currentPos.add(vec) } - }, - collectDrops = build.collectDrops + distanceMoved += sliceSize + structure + } else { + this@HighwayTools.info("Highway built") + disable() + emptyStructure() + } + }.build( + collectDrops = build.collectDrops, + build = build, + rotation = rotation, + interact = interact, + inventory = inventory, ).run() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt index 52d4c64f2..3242b8ffb 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt @@ -18,14 +18,13 @@ package com.lambda.module.modules.player import com.lambda.config.groups.InventorySettings -import com.lambda.context.DefaultConfigs import com.lambda.event.events.InventoryEvent import com.lambda.event.events.PlayerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run import com.lambda.task.Task +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock import com.lambda.task.tasks.OpenContainer import com.lambda.task.tasks.PlaceContainer @@ -66,7 +65,7 @@ object InventoryTweaks : Module( if (event.screenHandler != lastOpenScreen) return@listen lastOpenScreen = null placedPos?.let { - lastBreak = DefaultConfigs.breakAndCollectBlock(it).run() + lastBreak = breakAndCollectBlock(it).run() placedPos = null } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 29dda17c0..da3574bc3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -17,14 +17,12 @@ package com.lambda.module.modules.player -import com.lambda.context.Configured -import com.lambda.context.DefaultConfigs import com.lambda.interaction.construction.blueprint.TickingBlueprint.Companion.tickingBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run import com.lambda.task.Task +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState @@ -34,7 +32,7 @@ object Nuker : Module( name = "Nuker", description = "Breaks blocks around you", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) -), Configured by DefaultConfigs { +) { private val height by setting("Height", 4, 1..8, 1) private val width by setting("Width", 4, 1..8, 1) private val flatten by setting("Flatten", true) @@ -45,8 +43,7 @@ object Nuker : Module( init { onEnable { - task = build( - tickingBlueprint { + task = tickingBlueprint { val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) .asSequence() .map { it.blockPos } @@ -65,7 +62,9 @@ object Nuker : Module( selection } - ).run() + // ToDo: Add build setting delegates + .build() + task?.run() } onDisable { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 4a2ef65f9..2f3ab7fc6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -22,9 +22,7 @@ import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings -import com.lambda.context.Configured -import com.lambda.context.ConfiguredSafeContext -import com.lambda.context.DefaultConfigs +import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -36,7 +34,6 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos @@ -46,17 +43,16 @@ object PacketMine : Module( "PacketMine", "automatically breaks blocks, and does it faster", setOf(ModuleTag.PLAYER) -), Configured by DefaultConfigs { +) { private val page by setting("Page", Page.Build) - override val build = BuildSettings(this) { page == Page.Build } - val breaking = build.breaking - val placing = build.placing - override val rotation = RotationSettings(this) { page == Page.Rotation } - override val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } - override val inventory = InventorySettings(this) { page == Page.Inventory } - override val hotbar = HotbarSettings(this) { page == Page.Hotbar } - private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breaking.reBreak } + private val build = BuildSettings(this) { page == Page.Build } + private val breakConfig = build.breaking + private val rotation = RotationSettings(this) { page == Page.Rotation } + private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } + private val inventory = InventorySettings(this) { page == Page.Inventory } + private val hotbar = HotbarSettings(this) { page == Page.Hotbar } + private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } private val pendingInteractionsList = ConcurrentLinkedQueue() @@ -75,7 +71,7 @@ object PacketMine : Module( //ToDo: run on every tick stage listen { - if (!breaking.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen + if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen val reBreak = reBreakPos ?: return@listen requestBreakManager(reBreak) } @@ -84,7 +80,7 @@ object PacketMine : Module( listen { event -> event.cancel() if (breakingPositions.any { it == event.pos }) return@listen - val secondary = if (breaking.doubleBreak) { + val secondary = if (breakConfig.doubleBreak) { breakingPositions[1] ?: breakingPositions[0] } else null requestBreakManager(event.pos, secondary) @@ -92,9 +88,7 @@ object PacketMine : Module( } listen { - if (!attackedThisTick) runSafe { - requestBreakManager(*breakingPositions.toList().toTypedArray()) - } + if (!attackedThisTick) requestBreakManager(*breakingPositions.toList().toTypedArray()) } onDisable { @@ -105,29 +99,27 @@ object PacketMine : Module( } } - private fun requestBreakManager(vararg requestPositions: BlockPos?) { + private fun SafeContext.requestBreakManager(vararg requestPositions: BlockPos?) { if (requestPositions.isEmpty()) return - runSafe { - val request = BreakRequest( - breakContexts(requestPositions.filterNotNull()), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onAccept = { - if (this@PacketMine.breaking.doubleBreak && breakingPositions[1] == null) { - breakingPositions[1] = breakingPositions[0] - } - breakingPositions[0] = it - reBreakPos = null - }, - onCancel = { nullifyBreakPos(it, true) }, - onBreak = { - breaks++ - nullifyBreakPos(it) - }, - onReBreakStart = { reBreakPos = it }, - onReBreak = { reBreakPos = it }, - onItemDrop = { _ -> itemDrops++ } - ) - this@PacketMine.breaking.request(request) - } + val request = BreakRequest( + breakContexts(requestPositions.filterNotNull()), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, + onAccept = { + if (breakConfig.doubleBreak && breakingPositions[1] == null) { + breakingPositions[1] = breakingPositions[0] + } + breakingPositions[0] = it + reBreakPos = null + }, + onCancel = { nullifyBreakPos(it, true) }, + onBreak = { + breaks++ + nullifyBreakPos(it) + }, + onReBreakStart = { reBreakPos = it }, + onReBreak = { reBreakPos = it }, + onItemDrop = { _ -> itemDrops++ } + ) + breakConfig.request(request) } private fun nullifyBreakPos(pos: BlockPos, includeReBreak: Boolean = false) { @@ -142,12 +134,18 @@ object PacketMine : Module( } } - private fun ConfiguredSafeContext.breakContexts(breakPositions: Collection) = - simulate( - breakPositions - .associateWith { TargetState.State(blockState(it).fluidState.blockState) } - .toBlueprint(), player.eyePos - ).filterIsInstance() + private fun SafeContext.breakContexts(breakPositions: Collection) = + breakPositions + .associateWith { TargetState.State(blockState(it).fluidState.blockState) } + .toBlueprint() + .simulate( + player.eyePos, + interact = interact, + rotation = rotation, + inventory = inventory, + build = build + ) + .filterIsInstance() .map { it.context } enum class Page { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt b/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt index 01cab2fa8..c3303369a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt @@ -17,8 +17,6 @@ package com.lambda.module.modules.player -import com.lambda.context.Configured -import com.lambda.context.DefaultConfigs import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.builders.buildOutline @@ -27,8 +25,8 @@ import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.t import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run import com.lambda.task.Task +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockBox @@ -40,7 +38,7 @@ object WorldEater : Module( name = "WorldEater", description = "Eats the world", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) -), Configured by DefaultConfigs { +) { // private val height by setting("Height", 4, 1..10, 1) // private val width by setting("Width", 6, 1..30, 1) private val pos1 by setting("Position 1", BlockPos(351, 104, 103)) @@ -76,10 +74,10 @@ object WorldEater : Module( private fun buildLayer() { work.firstOrNull()?.let { box -> - runningTask = build( + runningTask = build { box.toStructure(TargetState.Air) .toBlueprint() - ).finally { + }.finally { work.removeFirstOrNull() buildLayer() }.run() diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index caa305288..abd3f5ce2 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -19,7 +19,9 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.GoalBlock import com.lambda.Lambda.LOG -import com.lambda.context.Configured +import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -42,7 +44,9 @@ import com.lambda.interaction.construction.simulation.Simulation.Companion.simul import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer import com.lambda.interaction.request.breaking.BreakRequest +import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.placing.PlaceRequest +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -59,8 +63,12 @@ class BuildTask @Ta5kBuilder constructor( private val blueprint: Blueprint, private val finishOnDone: Boolean = true, private val collectDrops: Boolean = TaskFlowModule.build.collectDrops, - configured: Configured -) : Task(), Configured by configured { + private val build: BuildConfig = TaskFlowModule.build, + private val rotation: RotationConfig = TaskFlowModule.rotation, + private val interact: InteractionConfig = TaskFlowModule.interact, + private val inventory: InventoryConfig = TaskFlowModule.inventory, + private val hotbar: HotbarConfig = TaskFlowModule.hotbar, +) : Task() { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" private val pendingInteractions = ConcurrentLinkedQueue() @@ -87,7 +95,7 @@ class BuildTask @Ta5kBuilder constructor( listen { if (collectDrops()) return@listen - val results = simulate(blueprint, player.eyePos) + val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) TaskFlowModule.drawables = results .filterIsInstance() @@ -117,7 +125,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { if (!build.pathing) return@listen - val sim = blueprint.simulation() + val sim = blueprint.simulation(interact, rotation, inventory, build) val goal = BuildGoal(sim, player.blockPos) BaritoneUtils.setGoalAndPath(goal) } @@ -172,9 +180,8 @@ class BuildTask @Ta5kBuilder constructor( is Resolvable -> { LOG.info("Resolving: ${bestResult.name}") - bestResult.run { - resolve().execute(this@BuildTask) - } + + bestResult.resolve().execute(this@BuildTask) } } } @@ -221,44 +228,62 @@ class BuildTask @Ta5kBuilder constructor( companion object { @Ta5kBuilder - fun Configured.build( - blueprint: () -> Blueprint, + fun build( finishOnDone: Boolean = true, collectDrops: Boolean = TaskFlowModule.build.collectDrops, - ) = BuildTask(blueprint(), finishOnDone, collectDrops, this) + build: BuildConfig = TaskFlowModule.build, + rotation: RotationConfig = TaskFlowModule.rotation, + interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, + blueprint: () -> Blueprint, + ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) @Ta5kBuilder - fun Configured.build( - structure: Structure, + fun Structure.build( finishOnDone: Boolean = true, - collectDrops: Boolean = TaskFlowModule.build.collectDrops - ) = BuildTask(structure.toBlueprint(), finishOnDone, collectDrops, this) + collectDrops: Boolean = TaskFlowModule.build.collectDrops, + build: BuildConfig = TaskFlowModule.build, + rotation: RotationConfig = TaskFlowModule.rotation, + interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) @Ta5kBuilder - fun Configured.build( - blueprint: Blueprint, + fun Blueprint.build( finishOnDone: Boolean = true, - collectDrops: Boolean = TaskFlowModule.build.collectDrops - ) = BuildTask(blueprint, finishOnDone, collectDrops, this) + collectDrops: Boolean = TaskFlowModule.build.collectDrops, + build: BuildConfig = TaskFlowModule.build, + rotation: RotationConfig = TaskFlowModule.rotation, + interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory) @Ta5kBuilder - fun Configured.breakAndCollectBlock( + fun breakAndCollectBlock( blockPos: BlockPos, finishOnDone: Boolean = true, collectDrops: Boolean = true, + build: BuildConfig = TaskFlowModule.build, + rotation: RotationConfig = TaskFlowModule.rotation, + interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, this + finishOnDone, collectDrops, build, rotation, interact, inventory ) @Ta5kBuilder - fun Configured.breakBlock( + fun breakBlock( blockPos: BlockPos, finishOnDone: Boolean = true, collectDrops: Boolean = TaskFlowModule.build.collectDrops, + build: BuildConfig = TaskFlowModule.build, + rotation: RotationConfig = TaskFlowModule.rotation, + interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, this + finishOnDone, collectDrops, build, rotation, interact, inventory ) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index b6066ab17..a568a27ee 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -20,7 +20,6 @@ package com.lambda.task.tasks import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InventoryConfig -import com.lambda.context.DefaultConfigs import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint @@ -55,12 +54,10 @@ class PlaceContainer @Ta5kBuilder constructor( val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4) .map { it.blockPos } .flatMap { - DefaultConfigs.simulate( - it.blockPos - .toStructure(TargetState.Stack(startStack)) - .toBlueprint(), - player.eyePos - ) + it.blockPos + .toStructure(TargetState.Stack(startStack)) + .toBlueprint() + .simulate(player.eyePos) } val succeeds = results.filterIsInstance().filter { @@ -70,11 +67,16 @@ class PlaceContainer @Ta5kBuilder constructor( canBeOpened(startStack, it.blockPos, it.context.result.side) } (succeeds + wrongStacks).minOrNull()?.let { result -> - DefaultConfigs.build( + build( + build = build, + rotation = rotation, + interact = interact, + inventory = inventory, + ) { result.blockPos .toStructure(TargetState.Stack(startStack)) .toBlueprint() - ).finally { + }.finally { success(result.blockPos) }.execute(this@PlaceContainer) } ?: { diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index 8449ceea5..50a06eb2a 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -19,8 +19,6 @@ package com.lambda.threading import com.lambda.Lambda.mc import com.lambda.context.ClientContext -import com.lambda.context.Configured -import com.lambda.context.ConfiguredSafeContext import com.lambda.context.SafeContext import com.lambda.event.EventFlow import com.mojang.blaze3d.systems.RenderSystem.isOnRenderThread @@ -44,10 +42,7 @@ import java.util.concurrent.CompletableFuture * @return The result of the block execution if the context is safe, null otherwise. */ inline fun runSafe(block: SafeContext.() -> T) = - ClientContext().toSafe()?.block() - -inline fun Configured.runSafe(block: ConfiguredSafeContext.() -> T) = - ClientContext().toSafeConfigured(this)?.block() + ClientContext().toSafe()?.let { block(it) } /** * This function is used to execute a block of code on a new thread running asynchronously to the game thread. From ab4917b38b712edefb76d5cc456c07d1a0c698dd Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 9 May 2025 14:45:52 -0400 Subject: [PATCH 187/364] Removed TickStage in favor of TickEvent --- .../lambda/config/groups/RotationSettings.kt | 3 ++- .../com/lambda/config/groups/TickStage.kt | 25 ------------------- .../request/rotation/RotationConfig.kt | 7 +++--- 3 files changed, 6 insertions(+), 29 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/config/groups/TickStage.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 1d17d24b9..5dc0de2bc 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -18,6 +18,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationMode @@ -44,7 +45,7 @@ class RotationSettings( /** * At what sub-tick stages rotations can be performed */ - override val rotationStageMask by c.setting("Rotation Stage Mask", setOf(*TickStage.entries.toTypedArray()), "The sub-tick stages at which rotations can be performed", vis) + override val rotationStageMask by c.setting("Rotation Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick stages at which rotations can be performed", vis) /** Whether the rotation is instant */ var instant by c.setting("Instant Rotation", true, "Instantly rotate") { rotate && vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt b/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt deleted file mode 100644 index 060287782..000000000 --- a/common/src/main/kotlin/com/lambda/config/groups/TickStage.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.config.groups - -enum class TickStage { - TickStart, - PostHotbar, - PostInteract, - PlayerTickPost -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index 9759071f2..a61110cf1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -17,7 +17,8 @@ package com.lambda.interaction.request.rotation -import com.lambda.config.groups.TickStage +import com.lambda.event.Event +import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -53,7 +54,7 @@ abstract class RotationConfig(priority: Priority) : RequestConfig + abstract val rotationStageMask: Set val rotate: Boolean get() = rotationMode != RotationMode.None @@ -65,7 +66,7 @@ abstract class RotationConfig(priority: Priority) : RequestConfig Date: Fri, 9 May 2025 19:51:29 +0100 Subject: [PATCH 188/364] reverted PlaceDirection back to cardinal snaps rather than area for now --- .../rotation/visibilty/PlaceDirection.kt | 137 ++---------------- .../rotation/visibilty/RotationTargets.kt | 2 +- 2 files changed, 17 insertions(+), 122 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index f4389804e..b31907d34 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -21,131 +21,33 @@ import com.lambda.interaction.request.rotation.Rotation import net.minecraft.entity.Entity import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper -import net.minecraft.util.math.MathHelper.wrapDegrees import net.minecraft.util.math.Vec3i -import kotlin.math.abs -import kotlin.math.asin -import kotlin.math.atan -import kotlin.math.cos -import kotlin.math.sin //ToDo: This is broken in many ways. Still a WIP enum class PlaceDirection( val rotation: Rotation, val vector: Vec3i, - private val yawRanges: List> ) { - UpNorth ( -180.0, -90.0, 0, 1, -1, northYawRanges), - UpSouth ( 0.0, -90.0, 0, 1, 1, listOf(southYawRange)), - UpWest ( 90.0, -90.0, 1, 1, 0, listOf(westYawRange)), - UpEast ( -90.0, -90.0, -1, 1, 0, listOf(eastYawRange)), + UpNorth (-180.0, -90.0, 0, 1, -1), + UpSouth ( 0.0, -90.0, 0, 1, 1), + UpWest ( 90.0, -90.0, 1, 1, 0), + UpEast ( -90.0, -90.0, -1, 1, 0), - DownNorth( -180.0, 90.0, 0, -1, -1, northYawRanges), - DownSouth( 0.0, 90.0, 0, -1, 1, listOf(southYawRange)), - DownWest ( 90.0, 90.0, 1, -1, 0, listOf(westYawRange)), - DownEast ( -90.0, 90.0, -1, -1, 0, listOf(eastYawRange)), + DownNorth(-180.0, 90.0, 0, -1, -1), + DownSouth( 0.0, 90.0, 0, -1, 1), + DownWest ( 90.0, 90.0, 1, -1, 0), + DownEast ( -90.0, 90.0, -1, -1, 0), - North ( -180.0, 0.0, 0, 0, -1, northYawRanges), - South ( 0.0, 0.0, 0, 0, 1, listOf(southYawRange)), - West ( 90.0, 0.0, 1, 0, 0, listOf(westYawRange)), - East ( -90.0, 0.0, -1, 0, 0, listOf(eastYawRange)); + North (-180.0, 0.0, 0, 0, -1), + South ( 0.0, 0.0, 0, 0, 1), + West ( 90.0, 0.0, 1, 0, 0), + East ( -90.0, 0.0, -1, 0, 0); - constructor(yaw: Double, pitch: Double, x: Int, y: Int, z: Int, yawRanges: List>) - : this(Rotation(yaw, pitch), Vec3i(x, y, z), yawRanges) + constructor(yaw: Double, pitch: Double, x: Int, y: Int, z: Int) + : this(Rotation(yaw, pitch), Vec3i(x, y, z)) - fun snapToArea(rot: Rotation): Rotation { - if (isInArea(rot)) return rot - - val normalizedYaw = wrapDegrees(rot.yaw) - val clampedYaw = when { - rotation.yaw != -180.0 -> normalizedYaw.coerceIn(yawRanges[0]) - normalizedYaw < 0 -> normalizedYaw.coerceIn(yawRanges[0]) - else -> normalizedYaw.coerceIn(yawRanges[1]) - } - - // Calculate pitch boundaries based on the snapped yaw - val snappedYawRad = Math.toRadians(clampedYaw) - val sinYaw = abs(sin(snappedYawRad)) - val cosYaw = abs(cos(snappedYawRad)) - val pitchBoundaryEW = Math.toDegrees(atan(sinYaw)) - val pitchBoundaryNS = Math.toDegrees(atan(cosYaw)) - - // Determine the correct pitch boundary and snap pitch - val snappedPitch = when { - // Primary E/W Directions - isEast() || isWest() -> { - when { - isUp() -> calculateVerticalPitch(sinYaw, pitchBoundaryEW, true) - isDown() -> calculateVerticalPitch(sinYaw, pitchBoundaryEW, false) - else -> calculateHorizontalPitch(rot.pitch, pitchBoundaryEW) - } - } - // Primary N/S Directions - isNorth() || isSouth() -> { - when { - isUp() -> calculateVerticalPitch(cosYaw, pitchBoundaryNS, true) - isDown() -> calculateVerticalPitch(cosYaw, pitchBoundaryNS, false) - else -> calculateHorizontalPitch(rot.pitch, pitchBoundaryNS) - } - } - // impossible to look just up or just down as you are always facing a horizontal direction - else -> rotation.pitch - } - - // Clamp pitch to valid range - val clampedPitch = snappedPitch.coerceIn(-90.0, 90.0) - - return Rotation(clampedYaw, clampedPitch) - } - - /** - * Calculates the pitch for vertical (Up/Down) directions - * - * @param trigValue The trigonometric value (sinYaw for E/W, cosYaw for N/S) - * @param boundaryValue The boundary value (pitchBoundaryEW for E/W, pitchBoundaryNS for N/S) - * @param isUp Whether this is for an Up direction (true) or Down direction (false) - * @return The calculated pitch value - */ - private fun calculateVerticalPitch(trigValue: Double, boundaryValue: Double, isUp: Boolean): Double { - val epsilon = 0.01 - val boundarySign = if (isUp) 1 else -1 - val asinSign = if (isUp) -1 else 1 - - val targetPitch = Math.toDegrees( - asinSign * asin(trigValue * cos(Math.toRadians(boundarySign * boundaryValue)) + epsilon) - ) - - return if (isUp) { - targetPitch.coerceIn(-90.0, 0.0) // Ensure it's in the up range - } else { - targetPitch.coerceIn(0.0, 90.0) // Ensure it's in the down range - } - } - - /** - * Calculates the pitch for horizontal directions - * - * @param currentPitch The current pitch value - * @param boundaryValue The boundary value (pitchBoundaryEW for E/W, pitchBoundaryNS for N/S) - * @return The calculated pitch value - */ - private fun calculateHorizontalPitch(currentPitch: Double, boundaryValue: Double): Double { - // Handle extreme pitch values (-90 or 90) by returning 0 - if (abs(currentPitch) >= 90.0 - 0.1) { - return 0.0 - } - - val isWithinPositiveBoundary = abs(currentPitch - boundaryValue) < abs(currentPitch - (-boundaryValue)) - return if (isWithinPositiveBoundary) boundaryValue else -boundaryValue - } - - // Helper functions to determine direction type - private fun isEast(): Boolean = this == East || this == UpEast || this == DownEast - private fun isWest(): Boolean = this == West || this == UpWest || this == DownWest - private fun isNorth(): Boolean = this == North || this == UpNorth || this == DownNorth - private fun isSouth(): Boolean = this == South || this == UpSouth || this == DownSouth - private fun isUp(): Boolean = this == UpEast || this == UpWest || this == UpNorth || this == UpSouth - private fun isDown(): Boolean = this == DownEast || this == DownWest || this == DownNorth || this == DownSouth + //ToDo: snap to the area not the cardinal to avoid excess rotation distance + fun snapToArea(rot: Rotation): Rotation = rotation fun isInArea(rot: Rotation) = fromRotation(rot) == this @@ -197,10 +99,3 @@ enum class PlaceDirection( } } } - -const val FUDGE_FACTOR = 0.01 - -val northYawRanges = listOf(-180.0..(-135.0 - FUDGE_FACTOR), (135.0 + FUDGE_FACTOR)..180.0) -val southYawRange = ( -45.0 + FUDGE_FACTOR)..( 45.0 - FUDGE_FACTOR) -val eastYawRange = (-135.0 + FUDGE_FACTOR)..(-45.0 - FUDGE_FACTOR) -val westYawRange = ( 45.0 + FUDGE_FACTOR)..(135.0 - FUDGE_FACTOR) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index 241010b3c..f98b695e8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -57,7 +57,7 @@ fun lookInDirection(direction: PlaceDirection) = PlaceDirection.fromRotation(RotationManager.activeRotation) == direction }) { if (!direction.isInArea(RotationManager.activeRotation) || !direction.isInArea(player.rotation)) { - direction.snapToArea(RotationManager.activeRotation) + RotationManager.activeRotation } else { player.rotation } From 7e820d44b4844f345530e8814e0cbcd9e69ef59f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 10 May 2025 15:48:25 +0100 Subject: [PATCH 189/364] fix oversight in PlaceDirection revert --- .../interaction/request/rotation/visibilty/RotationTargets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index f98b695e8..241010b3c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -57,7 +57,7 @@ fun lookInDirection(direction: PlaceDirection) = PlaceDirection.fromRotation(RotationManager.activeRotation) == direction }) { if (!direction.isInArea(RotationManager.activeRotation) || !direction.isInArea(player.rotation)) { - RotationManager.activeRotation + direction.snapToArea(RotationManager.activeRotation) } else { player.rotation } From adc8a425f7e5fce49a1f6060592a1353204896ea Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 11 May 2025 15:56:49 +0100 Subject: [PATCH 190/364] removed outdated todo --- .../interaction/request/rotation/visibilty/PlaceDirection.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt index b31907d34..a6cfb2c2d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt @@ -23,7 +23,6 @@ import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper import net.minecraft.util.math.Vec3i -//ToDo: This is broken in many ways. Still a WIP enum class PlaceDirection( val rotation: Rotation, val vector: Vec3i, From 61af38e49054b1864cbdbb8bd0c8dc9f06644c32 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 14 May 2025 17:20:38 +0100 Subject: [PATCH 191/364] allow breaking with any tool with a setting for suitable only --- .../com/lambda/config/groups/BreakSettings.kt | 1 + .../construction/simulation/BuildSimulator.kt | 85 ++++++++++++------- .../interaction/material/StackSelection.kt | 52 +++++++----- .../request/breaking/BreakConfig.kt | 1 + 4 files changed, 89 insertions(+), 50 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 197c0fa18..8e5617d21 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -45,6 +45,7 @@ class BreakSettings( override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() } override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } + override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis) override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index cde372b92..f330d9d93 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -30,6 +30,7 @@ import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer +import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial @@ -56,7 +57,6 @@ import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal -import com.lambda.util.item.ItemUtils.findBestToolsForBreaking import com.lambda.util.math.distSq import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.copyPlayer @@ -68,6 +68,7 @@ import net.minecraft.block.pattern.CachedBlockPosition import net.minecraft.enchantment.Enchantments import net.minecraft.item.BlockItem import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys import net.minecraft.util.Hand @@ -542,45 +543,67 @@ object BuildSimulator { return acc } - val bestTools = findBestToolsForBreaking(state, inventory.allowedTools) - - /* there is no good tool for the job */ - if (bestTools.isEmpty()) { - /* The current selected item cant mine the block */ - Hand.entries.forEach { - val stack = player.getStackInHand(it) - if (stack.isEmpty) return@forEach - if (stack.item.canMine(state, world, pos, player)) return@forEach - acc.add(BreakResult.ItemCantMine(pos, state, stack.item, inventory)) - return acc +// val bestTools = findBestToolForBreaking(state, inventory.allowedTools) +// +// /* there is no good tool for the job */ +// if (bestTools.isEmpty()) { +// /* The current selected item cant mine the block */ +// Hand.entries.forEach { +// val stack = player.getStackInHand(it) +// if (stack.isEmpty) return@forEach +// if (stack.item.canMine(state, world, pos, player)) return@forEach +// acc.add(BreakResult.ItemCantMine(pos, state, stack.item, inventory)) +// return acc +// } +// // ToDo: Switch to non destroyable item +// acc.add(BreakResult.Break(pos, breakContext)) +// return acc +// } +// +// val toolSelection = if (build.breaking.forceSilkTouch) { +// selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.SILK_TOUCH) } +// } else if (build.breaking.forceFortunePickaxe) { +// selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.breaking.minFortuneLevel) } +// } else { +// bestTools.select() +// } +// val silentSwapSelection = selectContainer { +// matches(toolSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) +// } +// val fullSelection = selectContainer { +// matches(toolSelection) and matches(inventory.containerSelection) +// } + + val stackSelection = selectStack( + block = { + run { + if (build.breaking.suitableToolsOnly) isSuitableForBreaking(state) + else StackSelection.EVERYTHING + } and if (build.breaking.forceSilkTouch) { + hasEnchantment(Enchantments.SILK_TOUCH) + } else if (build.breaking.forceFortunePickaxe) { + hasEnchantment(Enchantments.FORTUNE, build.breaking.minFortuneLevel) + } else StackSelection.EVERYTHING + }, + sorter = compareByDescending { + it.canDestroy(world.registryManager.get(RegistryKeys.BLOCK), CachedBlockPosition(world, pos, false)) + }.thenByDescending { + state.calcItemBlockBreakingDelta(player, world, pos, it) } - // ToDo: Switch to non destroyable item - acc.add(BreakResult.Break(pos, breakContext)) - return acc - } + ) - val toolSelection = if (build.breaking.forceSilkTouch) { - selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.SILK_TOUCH) } - } else if (build.breaking.forceFortunePickaxe) { - selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.breaking.minFortuneLevel) } - } else { - bestTools.select() - } val silentSwapSelection = selectContainer { - matches(toolSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) + matches(stackSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) } - val fullSelection = selectContainer { - matches(toolSelection) and matches(inventory.containerSelection) - } - val swapCandidates = toolSelection.containerWithMaterial(inventory, silentSwapSelection) + val swapCandidates = stackSelection.containerWithMaterial(inventory, silentSwapSelection) if (swapCandidates.isEmpty()) { - acc.add(BuildResult.WrongItemSelection(pos, breakContext, toolSelection, player.mainHandStack, inventory)) + acc.add(BuildResult.WrongItemSelection(pos, breakContext, stackSelection, player.mainHandStack, inventory)) return acc } - val matchingStacks = swapCandidates.associateWith { it.matchingStacks(toolSelection) } - val (container, toolPair) = matchingStacks.mapValues { (_, stacks) -> + val matchingStacks = swapCandidates.associateWith { it.matchingStacks(stackSelection) } + val (_, toolPair) = matchingStacks.mapValues { (_, stacks) -> stacks.associateWith { state.calcItemBlockBreakingDelta(player, world, pos, it) } .maxByOrNull { it.value } ?.toPair() diff --git a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 05fae125d..c2d903f3d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.material import com.lambda.util.BlockUtils.item import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.enchantment.Enchantment import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.item.Item @@ -32,6 +33,7 @@ import kotlin.reflect.KClass */ class StackSelection { var selector: (ItemStack) -> Boolean = { true } + var comparator: Comparator? = null var count: Int = DEFAULT_AMOUNT var inShulkerBox: Boolean = false @@ -45,28 +47,24 @@ class StackSelection { val optimalStack: ItemStack? get() = itemStack ?: item?.let { ItemStack(it, count) } - val filterStack: (ItemStack) -> Boolean - get() = { stack -> - if (inShulkerBox) { - stack.shulkerBoxContents.any { selector(it) } - } else { - selector(stack) - } - } + fun filterStack(stack: ItemStack) = + if (inShulkerBox) stack.shulkerBoxContents.any { selector(it) } + else selector(stack) - val filterSlot: (Slot) -> Boolean - get() = { slot -> - filterStack(slot.stack) - } + fun filterSlot(slot: Slot) = filterStack(slot.stack) - val filterStacks: (List) -> List - get() = { - it.filter(filterStack) + fun filterStacks(stacks: List): List = + stacks.filter(::filterStack).let { filteredStacks -> + comparator?.run { + filteredStacks.sortedWith(this) + } ?: filteredStacks } - val filterSlots: (List) -> List - get() = { slots -> - slots.filter { filterSlot(it) } + fun filterSlots(slots: List): List = + slots.filter(::filterSlot).let { filteredSlots -> + comparator?.run { + filteredSlots.sortedWith { slot, slot2 -> compare(slot.stack, slot2.stack) } + } ?: filteredSlots } /** @@ -127,6 +125,9 @@ class StackSelection { @StackSelectionDsl fun isOneOfStacks(stacks: Collection): (ItemStack) -> Boolean = { it in stacks } + @StackSelectionDsl + fun isSuitableForBreaking(blockState: BlockState): (ItemStack) -> Boolean = { it.isSuitableFor(blockState) } + /** * [isItem] returns a predicate that matches a specific [Item] instance. * @param T The instance of [Item] to be matched. @@ -257,15 +258,28 @@ class StackSelection { * @param block The predicate to be used to select the items. * @return A [StackSelection] with the given parameters. */ + @StackSelectionDsl + fun selectStack( + count: Int = DEFAULT_AMOUNT, + inShulkerBox: Boolean = false, + block: StackSelection.() -> (ItemStack) -> Boolean + ) = StackSelection().apply { + selector = block() + this.count = count + this.inShulkerBox = inShulkerBox + } + @StackSelectionDsl fun selectStack( count: Int = DEFAULT_AMOUNT, inShulkerBox: Boolean = false, block: StackSelection.() -> (ItemStack) -> Boolean, + sorter: Comparator? = null ) = StackSelection().apply { selector = block() + comparator = sorter this.count = count this.inShulkerBox = inShulkerBox } } -} +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 2dadb56c0..eca500c4e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -42,6 +42,7 @@ abstract class BreakConfig( abstract val breakConfirmation: BreakConfirmationMode abstract val maxPendingBreaks: Int abstract val breaksPerTick: Int + abstract val suitableToolsOnly: Boolean abstract val breakWeakBlocks: Boolean abstract val forceSilkTouch: Boolean abstract val forceFortunePickaxe: Boolean From d309ca102206765ad0be9722035de755ab5d9324 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 15 May 2025 20:56:21 +0100 Subject: [PATCH 192/364] queue and refactors --- .../module/modules/player/PacketMine.kt | 94 ++++++++++++------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 2f3ab7fc6..4c343a220 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -37,6 +37,7 @@ import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos +import java.util.* import java.util.concurrent.ConcurrentLinkedQueue object PacketMine : Module( @@ -52,14 +53,19 @@ object PacketMine : Module( private val interact = InteractionSettings(this, InteractionMask.Block) { page == Page.Interaction } private val inventory = InventorySettings(this) { page == Page.Inventory } private val hotbar = HotbarSettings(this) { page == Page.Hotbar } + private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } + private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once") + .onValueChange { _, to -> if (!to) queuePositions.clear() } private val pendingInteractionsList = ConcurrentLinkedQueue() private var breaks = 0 private var itemDrops = 0 - private val breakingPositions = arrayOfNulls(2) + private val breakPositions = arrayOfNulls(2) + private val queuePositions = LinkedList() + private var reBreakPos: BlockPos? = null private var attackedThisTick = false @@ -73,47 +79,61 @@ object PacketMine : Module( listen { if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen val reBreak = reBreakPos ?: return@listen - requestBreakManager(reBreak) + requestBreakManager(listOf(reBreak), true) } listen { it.cancel() } listen { event -> event.cancel() - if (breakingPositions.any { it == event.pos }) return@listen - val secondary = if (breakConfig.doubleBreak) { - breakingPositions[1] ?: breakingPositions[0] - } else null - requestBreakManager(event.pos, secondary) + if ((breakPositions + queuePositions).any { it == event.pos }) return@listen + val activeBreaking = if (queue) { + queuePositions.addLast(event.pos) + breakPositions + queuePositions + } else { + arrayOf(event.pos) + if (breakConfig.doubleBreak) { + breakPositions[1] ?: breakPositions[0] + } else null + } + requestBreakManager(activeBreaking.toList()) attackedThisTick = true } listen { - if (!attackedThisTick) requestBreakManager(*breakingPositions.toList().toTypedArray()) + if (!attackedThisTick) requestBreakManager((breakPositions + queuePositions).toList()) } onDisable { - breakingPositions[0] = null - breakingPositions[1] = null + breakPositions[0] = null + breakPositions[1] = null + queuePositions.clear() reBreakPos = null attackedThisTick = false } } - private fun SafeContext.requestBreakManager(vararg requestPositions: BlockPos?) { + private fun SafeContext.requestBreakManager(requestPositions: Collection, reBreaking: Boolean = false) { if (requestPositions.isEmpty()) return + val breakContexts = breakContexts(requestPositions) + if (!reBreaking) { + breakPositions.forEachIndexed { index, breakPos -> + if (breakContexts.none { it.expectedPos == breakPos }) { + breakPositions[index] = null + } + } + queuePositions.removeIf { queuePos -> + breakContexts.none { it.expectedPos == queuePos } + } + } val request = BreakRequest( - breakContexts(requestPositions.filterNotNull()), build, rotation, hotbar, pendingInteractions = pendingInteractionsList, + breakContexts, build, rotation, hotbar, pendingInteractions = pendingInteractionsList, onAccept = { - if (breakConfig.doubleBreak && breakingPositions[1] == null) { - breakingPositions[1] = breakingPositions[0] - } - breakingPositions[0] = it - reBreakPos = null + addBreak(it) + queuePositions.remove(it) }, - onCancel = { nullifyBreakPos(it, true) }, + onCancel = { removeBreak(it, true) }, onBreak = { breaks++ - nullifyBreakPos(it) + removeBreak(it) }, onReBreakStart = { reBreakPos = it }, onReBreak = { reBreakPos = it }, @@ -122,20 +142,9 @@ object PacketMine : Module( breakConfig.request(request) } - private fun nullifyBreakPos(pos: BlockPos, includeReBreak: Boolean = false) { - breakingPositions.forEachIndexed { index, breakPos -> - if (breakPos == pos) { - breakingPositions[index] = null - } - } - if (includeReBreak && pos == reBreakPos) { - reBreakPos = null - return - } - } - - private fun SafeContext.breakContexts(breakPositions: Collection) = - breakPositions + private fun SafeContext.breakContexts(positions: Collection) = + positions + .filterNotNull() .associateWith { TargetState.State(blockState(it).fluidState.blockState) } .toBlueprint() .simulate( @@ -148,6 +157,25 @@ object PacketMine : Module( .filterIsInstance() .map { it.context } + private fun addBreak(pos: BlockPos) { + if (breakConfig.doubleBreak && breakPositions[1] == null) { + breakPositions[1] = breakPositions[0] + } + breakPositions[0] = pos + reBreakPos = null + } + + private fun removeBreak(pos: BlockPos, includeReBreak: Boolean = false) { + breakPositions.forEachIndexed { index, breakPos -> + if (breakPos == pos) { + breakPositions[index] = null + } + } + if (includeReBreak && pos == reBreakPos) { + reBreakPos = null + } + } + enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } From ef54193059f55aa512b771f48a8f75799af9bbab Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 16 May 2025 00:32:30 +0100 Subject: [PATCH 193/364] queue sort setting --- .../lambda/module/modules/player/PacketMine.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 4c343a220..186b5bc15 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -57,6 +57,7 @@ object PacketMine : Module( private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once") .onValueChange { _, to -> if (!to) queuePositions.clear() } + private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue } private val pendingInteractionsList = ConcurrentLinkedQueue() @@ -65,6 +66,11 @@ object PacketMine : Module( private val breakPositions = arrayOfNulls(2) private val queuePositions = LinkedList() + private val queueSorted + get() = when (queueOrder) { + QueueOrder.Standard -> queuePositions + QueueOrder.Reversed -> queuePositions.asReversed() + } private var reBreakPos: BlockPos? = null @@ -88,7 +94,7 @@ object PacketMine : Module( if ((breakPositions + queuePositions).any { it == event.pos }) return@listen val activeBreaking = if (queue) { queuePositions.addLast(event.pos) - breakPositions + queuePositions + breakPositions + queueSorted } else { arrayOf(event.pos) + if (breakConfig.doubleBreak) { breakPositions[1] ?: breakPositions[0] @@ -99,7 +105,7 @@ object PacketMine : Module( } listen { - if (!attackedThisTick) requestBreakManager((breakPositions + queuePositions).toList()) + if (!attackedThisTick) requestBreakManager((breakPositions + queueSorted).toList()) } onDisable { @@ -185,4 +191,9 @@ object PacketMine : Module( Auto, AutoConstant; } + + enum class QueueOrder { + Standard, + Reversed + } } \ No newline at end of file From f26320b4a24a00029a48ee5679173616fe4a7cdb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 16 May 2025 23:15:05 +0100 Subject: [PATCH 194/364] prevent blocking double break in the event the current secondary is pending and the break manager accepts a new break --- .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 186b5bc15..b31ad5ae3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -164,7 +164,7 @@ object PacketMine : Module( .map { it.context } private fun addBreak(pos: BlockPos) { - if (breakConfig.doubleBreak && breakPositions[1] == null) { + if (breakConfig.doubleBreak && breakPositions[0] != null) { breakPositions[1] = breakPositions[0] } breakPositions[0] = pos From aa8f794526c39e85494c96d85a643da16307031a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 16 May 2025 23:32:04 +0100 Subject: [PATCH 195/364] formatting --- .../com/lambda/module/modules/player/PacketMine.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index b31ad5ae3..567a9db6a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -132,15 +132,9 @@ object PacketMine : Module( } val request = BreakRequest( breakContexts, build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onAccept = { - addBreak(it) - queuePositions.remove(it) - }, + onAccept = { queuePositions.remove(it); addBreak(it) }, onCancel = { removeBreak(it, true) }, - onBreak = { - breaks++ - removeBreak(it) - }, + onBreak = { removeBreak(it); breaks++ }, onReBreakStart = { reBreakPos = it }, onReBreak = { reBreakPos = it }, onItemDrop = { _ -> itemDrops++ } From 1938a2fb3db3993467f9d857cb294229ba3d916d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 17 May 2025 14:56:40 +0100 Subject: [PATCH 196/364] call cancelBreak instead of just sending an abort when overriding a primary break --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 3069edf33..74eaa4227 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -352,7 +352,7 @@ object BreakManager : RequestHandler( primaryBreak?.let { primaryInfo -> if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) { if (!primaryInfo.updatedThisTick) { - primaryInfo.abortBreakPacket(world, interaction) + primaryInfo.cancelBreak() return@let } else return null } From 2683c5b0a01d6a1a178826805adca38e2410e355 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 17 May 2025 16:17:05 +0100 Subject: [PATCH 197/364] update breaks regardless if active request is null or not and return for each if break stage mask doesnt match as other breaks could have a different config --- .../request/breaking/BreakManager.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 74eaa4227..f9997269f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -67,7 +67,7 @@ object BreakManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Player.Post, // ToDo: Post interact - onOpen = { activeRequest?.let { processRequest(it) } } + onOpen = { processRequest(activeRequest) } ), PositionBlocking { private var primaryBreak: BreakInfo? get() = breakInfos[0] @@ -125,6 +125,8 @@ object BreakManager : RequestHandler( } } activeRequest = null + breaks = mutableListOf() + instantBreaks = mutableListOf() breaksThisTick = 0 } @@ -198,13 +200,15 @@ object BreakManager : RequestHandler( * @see processNewBreaks * @see updateBreakProgress */ - private fun SafeContext.processRequest(request: BreakRequest) { + private fun SafeContext.processRequest(breakRequest: BreakRequest?) { pendingBreaks.cleanUp() - if (request.fresh) populateFrom(request) + breakRequest?.let { request -> + if (request.fresh) populateFrom(request) - if (performInstantBreaks(request)) { - processNewBreaks(request) + if (performInstantBreaks(request)) { + processNewBreaks(request) + } } // Reversed so that the breaking order feels natural to the user as the primary break is always the @@ -212,7 +216,7 @@ object BreakManager : RequestHandler( run { breakInfos .filterNotNull() - .filter { !it.isRedundant } + .filter { !it.isRedundant && it.updatedThisTick } .also { rotationRequest = it.firstOrNull { info -> info.breakConfig.rotateForBreak } ?.let { info -> @@ -223,8 +227,9 @@ object BreakManager : RequestHandler( .asReversed() .forEach { info -> if (info.updatedProgressThisTick) return@forEach - if (!info.context.requestDependencies(request)) return@run - if ((!rotated && info.isPrimary) || tickStage !in info.breakConfig.breakStageMask) return@run + if (!info.context.requestDependencies(info.request)) return@run + if (tickStage !in info.breakConfig.breakStageMask) return@forEach + if ((!rotated && info.isPrimary)) return@run updateBreakProgress(info) } From e07c8298b92c8e1d6ad19beb24dac2e41ae398cd Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 17 May 2025 18:29:01 +0100 Subject: [PATCH 198/364] added break radius for breaking a larger area at once with a single click --- .../module/modules/player/PacketMine.kt | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 567a9db6a..66c000f3a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -35,6 +35,7 @@ import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState +import com.lambda.util.math.distSq import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos import java.util.* @@ -55,6 +56,8 @@ object PacketMine : Module( private val hotbar = HotbarSettings(this) { page == Page.Hotbar } private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } + private val breakRadius by setting("Break Radius", 0, 0..5, 1, "Selects and breaks all blocks within the break radius of the selected block") + private val flatten by setting("Flatten", false, "Wont allow breaking extra blocks under your players position") { breakRadius > 0 } private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once") .onValueChange { _, to -> if (!to) queuePositions.clear() } private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue } @@ -91,12 +94,26 @@ object PacketMine : Module( listen { it.cancel() } listen { event -> event.cancel() - if ((breakPositions + queuePositions).any { it == event.pos }) return@listen + val pos = event.pos + val positions = if (breakRadius > 0) { + arrayListOf().apply { + BlockPos.iterateOutwards(pos, breakRadius, breakRadius, breakRadius).forEach { blockPos -> + if (blockPos distSq pos <= breakRadius * breakRadius && (!flatten || blockPos.y >= player.blockPos.y)) { + add(blockPos.toImmutable()) + } + } + } + } else { + listOf(pos) + } + if ((breakPositions + queuePositions).any { pending -> positions.any { it == pending } }) return@listen val activeBreaking = if (queue) { - queuePositions.addLast(event.pos) - breakPositions + queueSorted + queuePositions.addAll(positions) + breakPositions.toList() + queueSorted } else { - arrayOf(event.pos) + if (breakConfig.doubleBreak) { + queuePositions.clear() + queuePositions.addAll(positions) + queuePositions + if (breakConfig.doubleBreak) { breakPositions[1] ?: breakPositions[0] } else null } @@ -121,11 +138,6 @@ object PacketMine : Module( if (requestPositions.isEmpty()) return val breakContexts = breakContexts(requestPositions) if (!reBreaking) { - breakPositions.forEachIndexed { index, breakPos -> - if (breakContexts.none { it.expectedPos == breakPos }) { - breakPositions[index] = null - } - } queuePositions.removeIf { queuePos -> breakContexts.none { it.expectedPos == queuePos } } From 22abb0f0047b2e94106b02e6f3fc5997bfd0eeb3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 17 May 2025 18:40:17 +0100 Subject: [PATCH 199/364] include the hit pos in the positions for break radius > 0 --- .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 66c000f3a..22de52ea0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -96,7 +96,7 @@ object PacketMine : Module( event.cancel() val pos = event.pos val positions = if (breakRadius > 0) { - arrayListOf().apply { + arrayListOf(pos).apply { BlockPos.iterateOutwards(pos, breakRadius, breakRadius, breakRadius).forEach { blockPos -> if (blockPos distSq pos <= breakRadius * breakRadius && (!flatten || blockPos.y >= player.blockPos.y)) { add(blockPos.toImmutable()) From 11147d245a1cc41ed301ff7305081d9e23e5caa1 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 17 May 2025 23:55:37 +0100 Subject: [PATCH 200/364] decoupled grouped area breaks and queue sorting --- .../module/modules/player/PacketMine.kt | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 22de52ea0..d8f309eec 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -27,6 +27,7 @@ import com.lambda.event.events.PlayerEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.simulation.BuildSimulator.simulate @@ -38,7 +39,6 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.distSq import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos -import java.util.* import java.util.concurrent.ConcurrentLinkedQueue object PacketMine : Module( @@ -68,12 +68,12 @@ object PacketMine : Module( private var itemDrops = 0 private val breakPositions = arrayOfNulls(2) - private val queuePositions = LinkedList() + private val queuePositions = LinkedHashSet>() private val queueSorted get() = when (queueOrder) { QueueOrder.Standard -> queuePositions - QueueOrder.Reversed -> queuePositions.asReversed() - } + QueueOrder.Reversed -> queuePositions.reversed() + }.flatten() private var reBreakPos: BlockPos? = null @@ -95,29 +95,29 @@ object PacketMine : Module( listen { event -> event.cancel() val pos = event.pos - val positions = if (breakRadius > 0) { - arrayListOf(pos).apply { - BlockPos.iterateOutwards(pos, breakRadius, breakRadius, breakRadius).forEach { blockPos -> - if (blockPos distSq pos <= breakRadius * breakRadius && (!flatten || blockPos.y >= player.blockPos.y)) { - add(blockPos.toImmutable()) - } + val positions = mutableListOf(pos).apply { + if (breakRadius <= 0) return@apply + BlockPos.iterateOutwards(pos, breakRadius, breakRadius, breakRadius).forEach { blockPos -> + if (blockPos distSq pos <= (breakRadius * breakRadius) && (!flatten || blockPos.y >= player.blockPos.y)) { + add(blockPos.toImmutable()) } } - } else { - listOf(pos) } - if ((breakPositions + queuePositions).any { pending -> positions.any { it == pending } }) return@listen + positions.removeIf { breakPos -> + breakPositions.any { it == breakPos } + } + if (positions.isEmpty()) return@listen val activeBreaking = if (queue) { - queuePositions.addAll(positions) + queuePositions.addLast(positions) breakPositions.toList() + queueSorted } else { queuePositions.clear() - queuePositions.addAll(positions) - queuePositions + if (breakConfig.doubleBreak) { + queuePositions.addLast(positions) + queuePositions.flatten() + if (breakConfig.doubleBreak) { breakPositions[1] ?: breakPositions[0] } else null } - requestBreakManager(activeBreaking.toList()) + requestBreakManager(activeBreaking) attackedThisTick = true } @@ -138,13 +138,11 @@ object PacketMine : Module( if (requestPositions.isEmpty()) return val breakContexts = breakContexts(requestPositions) if (!reBreaking) { - queuePositions.removeIf { queuePos -> - breakContexts.none { it.expectedPos == queuePos } - } + queuePositions.retainAllPositions(breakContexts) } val request = BreakRequest( breakContexts, build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onAccept = { queuePositions.remove(it); addBreak(it) }, + onAccept = { queuePositions.removePos(it); addBreak(it) }, onCancel = { removeBreak(it, true) }, onBreak = { removeBreak(it); breaks++ }, onReBreakStart = { reBreakPos = it }, @@ -159,13 +157,7 @@ object PacketMine : Module( .filterNotNull() .associateWith { TargetState.State(blockState(it).fluidState.blockState) } .toBlueprint() - .simulate( - player.eyePos, - interact = interact, - rotation = rotation, - inventory = inventory, - build = build - ) + .simulate(player.eyePos, interact, rotation, inventory, build) .filterIsInstance() .map { it.context } @@ -188,6 +180,28 @@ object PacketMine : Module( } } + private fun LinkedHashSet>.removePos(element: BlockPos): Boolean { + var anyRemoved = false + removeIf { + val removed = it.remove(element) + anyRemoved = anyRemoved or removed + return@removeIf removed && it.isEmpty() + } + return anyRemoved + } + + private fun LinkedHashSet>.retainAllPositions(positions: Collection): Boolean { + var modified = false + forEach { + modified = modified or it.retainAll { pos -> + positions.any { retain -> + retain.expectedPos == pos + } + } + } + return modified + } + enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } From 1b60759e75366bb24a7a4d9069b1cb7d661925ac Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 18 May 2025 05:40:54 +0100 Subject: [PATCH 201/364] fixed instability issue at slower rotation speeds when using a higher break radius in packetmine and renamed / moved onAccept and onBreak to onStart and onStop to and are now called when starting and stopping breaking a block. --- .../lambda/interaction/request/breaking/BreakInfo.kt | 3 --- .../interaction/request/breaking/BreakManager.kt | 8 +++++--- .../interaction/request/breaking/BreakRequest.kt | 4 ++-- .../request/breaking/BrokenBlockHandler.kt | 1 - .../com/lambda/module/modules/player/PacketMine.kt | 12 +++++++++--- .../main/kotlin/com/lambda/task/tasks/BuildTask.kt | 2 +- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 1712608a5..8a755b060 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -43,8 +43,6 @@ data class BreakInfo( var breakingTicks = 0 var soundsCooldown = 0.0f - var pending = false - var vanillaInstantBreakable = false val reBreakable get() = !vanillaInstantBreakable && isPrimary @@ -63,7 +61,6 @@ data class BreakInfo( @Synchronized fun internalOnBreak() { if (!isReBreaking) broken = true - request.onBreak?.invoke(context.expectedPos) item?.let { item -> request.onItemDrop?.invoke(item) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index f9997269f..57629a091 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -255,6 +255,7 @@ object BreakManager : RequestHandler( private fun SafeContext.populateFrom(request: BreakRequest) { // Sanitize the new breaks val newBreaks = request.contexts + .distinctBy { it.expectedPos } .filter { ctx -> canAccept(ctx, request.build.breaking) } .toMutableList() @@ -316,7 +317,6 @@ object BreakManager : RequestHandler( if (!rotated || tickStage !in request.build.breaking.breakStageMask) return false val breakInfo = initNewBreak(ctx, request) ?: return false - request.onAccept?.invoke(ctx.expectedPos) updateBreakProgress(breakInfo) breaksThisTick++ iterator.remove() @@ -337,7 +337,6 @@ object BreakManager : RequestHandler( while (iterator.hasNext()) { val ctx = iterator.next() initNewBreak(ctx, request) ?: return false - request.onAccept?.invoke(ctx.expectedPos) iterator.remove() if (atMaxBreakInfos(request.build.breaking)) return false } @@ -404,6 +403,7 @@ object BreakManager : RequestHandler( * @see startPending */ private fun SafeContext.onBlockBreak(info: BreakInfo) { + info.request.onStop?.invoke(info.context.expectedPos) when (info.breakConfig.breakConfirmation) { BreakConfirmationMode.None -> { destroyBlock(info) @@ -520,7 +520,7 @@ object BreakManager : RequestHandler( primaryBreak = reBreakResult.breakInfo.apply { type = Primary ReBreakManager.clearReBreak() - request.onAccept?.invoke(ctx.expectedPos) + request.onStart?.invoke(ctx.expectedPos) } return primaryBreak?.let { primary -> @@ -631,6 +631,7 @@ object BreakManager : RequestHandler( if (gamemode.isCreative) { lastPosStarted = ctx.expectedPos onBlockBreak(info) + info.request.onStart?.invoke(ctx.expectedPos) interaction.sendSequencedPacket(world) { sequence: Int -> PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) } @@ -638,6 +639,7 @@ object BreakManager : RequestHandler( return true } if (info.breaking) return false + info.request.onStart?.invoke(ctx.expectedPos) lastPosStarted = ctx.expectedPos diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 07585a9ea..be3c20932 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -35,9 +35,9 @@ data class BreakRequest( val rotation: RotationConfig, val hotbar: HotbarConfig, val pendingInteractions: MutableCollection, - val onAccept: ((BlockPos) -> Unit)? = null, + val onStart: ((BlockPos) -> Unit)? = null, + val onStop: ((BlockPos) -> Unit)? = null, val onCancel: ((BlockPos) -> Unit)? = null, - val onBreak: ((BlockPos) -> Unit)? = null, val onItemDrop: ((ItemEntity) -> Unit)? = null, val onReBreakStart: ((BlockPos) -> Unit)? = null, val onReBreak: ((BlockPos) -> Unit)? = null, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index a0635e3c5..05e8f6f1a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -131,7 +131,6 @@ object BrokenBlockHandler { * Adds the [info] to the [BrokenBlockHandler], and requesters, pending interaction collections. */ fun BreakInfo.startPending() { - pending = true pendingBreaks.add(this) pendingInteractions.add(context) } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index d8f309eec..756beaae7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -104,7 +104,7 @@ object PacketMine : Module( } } positions.removeIf { breakPos -> - breakPositions.any { it == breakPos } + breakPositions.any { it == breakPos } || queuePositions.any { it == pos } } if (positions.isEmpty()) return@listen val activeBreaking = if (queue) { @@ -142,9 +142,9 @@ object PacketMine : Module( } val request = BreakRequest( breakContexts, build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onAccept = { queuePositions.removePos(it); addBreak(it) }, + onStart = { queuePositions.removePos(it); addBreak(it) }, + onStop = { removeBreak(it); breaks++ }, onCancel = { removeBreak(it, true) }, - onBreak = { removeBreak(it); breaks++ }, onReBreakStart = { reBreakPos = it }, onReBreak = { reBreakPos = it }, onItemDrop = { _ -> itemDrops++ } @@ -202,6 +202,12 @@ object PacketMine : Module( return modified } + private fun LinkedHashSet>.any(predicate: (BlockPos) -> Boolean): Boolean { + if (isEmpty()) return false + forEach { if (it.any(predicate)) return true } + return false + } + enum class Page { Build, Rotation, Interaction, Inventory, Hotbar } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index abd3f5ce2..80a72cc27 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -157,7 +157,7 @@ class BuildTask @Ta5kBuilder constructor( val request = BreakRequest( requestContexts, build, rotation, hotbar, pendingInteractions = pendingInteractions, - onBreak = { breaks++ }, + onStop = { breaks++ }, onItemDrop = onItemDrop ) build.breaking.request(request) From cd1231dfa2072853ed39fc36ca8025bc4ac6aed3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 18 May 2025 05:50:58 +0100 Subject: [PATCH 202/364] switched back to LinkedList from LinkedHashMap --- .../com/lambda/module/modules/player/PacketMine.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 756beaae7..3cf0e39a1 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -39,6 +39,7 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.distSq import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos +import java.util.* import java.util.concurrent.ConcurrentLinkedQueue object PacketMine : Module( @@ -68,7 +69,7 @@ object PacketMine : Module( private var itemDrops = 0 private val breakPositions = arrayOfNulls(2) - private val queuePositions = LinkedHashSet>() + private val queuePositions = LinkedList>() private val queueSorted get() = when (queueOrder) { QueueOrder.Standard -> queuePositions @@ -180,7 +181,7 @@ object PacketMine : Module( } } - private fun LinkedHashSet>.removePos(element: BlockPos): Boolean { + private fun LinkedList>.removePos(element: BlockPos): Boolean { var anyRemoved = false removeIf { val removed = it.remove(element) @@ -190,7 +191,7 @@ object PacketMine : Module( return anyRemoved } - private fun LinkedHashSet>.retainAllPositions(positions: Collection): Boolean { + private fun LinkedList>.retainAllPositions(positions: Collection): Boolean { var modified = false forEach { modified = modified or it.retainAll { pos -> @@ -202,7 +203,7 @@ object PacketMine : Module( return modified } - private fun LinkedHashSet>.any(predicate: (BlockPos) -> Boolean): Boolean { + private fun LinkedList>.any(predicate: (BlockPos) -> Boolean): Boolean { if (isEmpty()) return false forEach { if (it.any(predicate)) return true } return false From 6715b8bcf1947fb1040848ebd9537238bd58c239 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 18 May 2025 05:50:58 +0100 Subject: [PATCH 203/364] switched back to LinkedList from LinkedHashSet --- .../com/lambda/module/modules/player/PacketMine.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 756beaae7..3cf0e39a1 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -39,6 +39,7 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.distSq import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos +import java.util.* import java.util.concurrent.ConcurrentLinkedQueue object PacketMine : Module( @@ -68,7 +69,7 @@ object PacketMine : Module( private var itemDrops = 0 private val breakPositions = arrayOfNulls(2) - private val queuePositions = LinkedHashSet>() + private val queuePositions = LinkedList>() private val queueSorted get() = when (queueOrder) { QueueOrder.Standard -> queuePositions @@ -180,7 +181,7 @@ object PacketMine : Module( } } - private fun LinkedHashSet>.removePos(element: BlockPos): Boolean { + private fun LinkedList>.removePos(element: BlockPos): Boolean { var anyRemoved = false removeIf { val removed = it.remove(element) @@ -190,7 +191,7 @@ object PacketMine : Module( return anyRemoved } - private fun LinkedHashSet>.retainAllPositions(positions: Collection): Boolean { + private fun LinkedList>.retainAllPositions(positions: Collection): Boolean { var modified = false forEach { modified = modified or it.retainAll { pos -> @@ -202,7 +203,7 @@ object PacketMine : Module( return modified } - private fun LinkedHashSet>.any(predicate: (BlockPos) -> Boolean): Boolean { + private fun LinkedList>.any(predicate: (BlockPos) -> Boolean): Boolean { if (isEmpty()) return false forEach { if (it.any(predicate)) return true } return false From 6689f032fda6973bbd724173aa4084975b3035e9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 19 May 2025 16:14:33 +0100 Subject: [PATCH 204/364] use active rotation for movement calculations as server rotation is outdated --- .../interaction/request/rotation/RotationManager.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 2ae60b521..01fd1f78b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -186,14 +186,14 @@ object RotationManager : RequestHandler( val movementYaw: Float? get() { if (activeRequest?.mode == RotationMode.Silent) return null - return serverRotation.yaw.toFloat() + return activeRotation.yaw.toFloat() } @JvmStatic val movementPitch: Float? get() { if (activeRequest?.mode == RotationMode.Silent) return null - return serverRotation.pitch.toFloat() + return activeRotation.pitch.toFloat() } @JvmStatic @@ -244,7 +244,7 @@ object RotationManager : RequestHandler( if (signForward == 0f && signStrafe == 0f) return@runSafe // Actual yaw used by the physics engine - var actualYaw = serverRotation.yaw + var actualYaw = activeRotation.yaw if (activeRequest?.mode == RotationMode.Silent) { actualYaw = player.yaw.toDouble() @@ -272,7 +272,7 @@ object RotationManager : RequestHandler( // Makes baritone movement safe // when yaw difference is too big to compensate it by modifying keyboard input val minYawDist = movementYawList - .map { serverRotation.yaw + it } // all possible movement directions (including diagonals) + .map { activeRotation.yaw + it } // all possible movement directions (including diagonals) .minOf { Rotation.angleDifference(it, baritoneYaw) } if (minYawDist > 5.0) { From b6e7131fe0c5807cf923a8834355b2e2c37774d9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 19 May 2025 16:33:42 +0100 Subject: [PATCH 205/364] check breakCooldown before start breaking --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 57629a091..24b79d2df 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -535,6 +535,7 @@ object BreakManager : RequestHandler( } else -> {} } + if (breakCooldown > 0) return false if (!startBreaking(info)) { info.nullify() info.internalOnCancel() From d58efae15fd75601e925cf2e602113e65920a894 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 19 May 2025 16:41:37 +0100 Subject: [PATCH 206/364] vanilla minecraft uses 6 tick break delay, adjusted the setting accordingly --- .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 8e5617d21..7092753d3 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -33,7 +33,7 @@ class BreakSettings( override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } - override val breakDelay by c.setting("Break Delay", 0, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() } + override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() } override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } From 128850352e003a7192ae6c680f5a01c3c5d2e9ff Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 20 May 2025 16:21:28 +0100 Subject: [PATCH 207/364] repeat the accept and start break cycle in case blocks are broken and new contexts can be accepted --- .../ClientPlayInteractionManagerMixin.java | 1 - .../com/lambda/config/groups/BreakSettings.kt | 2 +- .../request/breaking/BreakManager.kt | 81 ++++++++----------- .../request/hotbar/HotbarManager.kt | 1 + .../request/placing/PlaceManager.kt | 1 + .../request/rotation/RotationManager.kt | 1 + .../com/lambda/module/modules/player/Nuker.kt | 36 ++++----- .../module/modules/player/PacketMine.kt | 5 +- 8 files changed, 58 insertions(+), 70 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java index 3a4ba3ac5..825a29826 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -25,7 +25,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; import net.minecraft.screen.slot.SlotActionType; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 7092753d3..780016c81 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -34,7 +34,7 @@ class BreakSettings( override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() } - override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) + override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 24b79d2df..de9e1c635 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -55,8 +55,6 @@ import com.lambda.util.player.swingHand import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance import net.minecraft.entity.ItemEntity -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos @@ -65,6 +63,7 @@ object BreakManager : RequestHandler( 0, TickEvent.Pre, TickEvent.Input.Pre, + TickEvent.Input.Post, TickEvent.Player.Post, // ToDo: Post interact onOpen = { processRequest(activeRequest) } @@ -203,36 +202,38 @@ object BreakManager : RequestHandler( private fun SafeContext.processRequest(breakRequest: BreakRequest?) { pendingBreaks.cleanUp() - breakRequest?.let { request -> - if (request.fresh) populateFrom(request) + repeat(2) { + breakRequest?.let { request -> + if (request.fresh) populateFrom(request) - if (performInstantBreaks(request)) { - processNewBreaks(request) + if (performInstantBreaks(request)) { + processNewBreaks(request) + } } - } - // Reversed so that the breaking order feels natural to the user as the primary break is always the - // last break to be started - run { - breakInfos - .filterNotNull() - .filter { !it.isRedundant && it.updatedThisTick } - .also { - rotationRequest = it.firstOrNull { info -> info.breakConfig.rotateForBreak } - ?.let { info -> - val rotation = info.context.rotation - if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation - } - } - .asReversed() - .forEach { info -> - if (info.updatedProgressThisTick) return@forEach - if (!info.context.requestDependencies(info.request)) return@run - if (tickStage !in info.breakConfig.breakStageMask) return@forEach - if ((!rotated && info.isPrimary)) return@run - - updateBreakProgress(info) - } + // Reversed so that the breaking order feels natural to the user as the primary break is always the + // last break to be started + run { + breakInfos + .filterNotNull() + .filter { !it.isRedundant && it.updatedThisTick } + .also { + rotationRequest = it.firstOrNull { info -> info.breakConfig.rotateForBreak } + ?.let { info -> + val rotation = info.context.rotation + if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation + } + } + .asReversed() + .forEach { info -> + if (info.updatedProgressThisTick) return@forEach + if (!info.context.requestDependencies(info.request)) return@run + if (tickStage !in info.breakConfig.breakStageMask) return@forEach + if ((!rotated && info.isPrimary)) return@run + + updateBreakProgress(info) + } + } } if (instantBreaks.isEmpty() && breaks.isEmpty()) { @@ -330,7 +331,6 @@ object BreakManager : RequestHandler( * @return false if a context cannot be started or the maximum active breaks has been reached. * * @see initNewBreak - * @see atMaxBreakInfos */ private fun SafeContext.processNewBreaks(request: BreakRequest): Boolean { val iterator = breaks.iterator() @@ -338,7 +338,6 @@ object BreakManager : RequestHandler( val ctx = iterator.next() initNewBreak(ctx, request) ?: return false iterator.remove() - if (atMaxBreakInfos(request.build.breaking)) return false } return true } @@ -376,14 +375,6 @@ object BreakManager : RequestHandler( return primaryBreak } - /** - * @return if the [breakInfos] are at capacity and no new breaks can be started. - */ - private fun atMaxBreakInfos(breakConfig: BreakConfig): Boolean { - val possibleBreakingCount = if (breakConfig.doubleBreak) 2 else 1 - return breakInfos.take(possibleBreakingCount).all { it != null } - } - /** * Begins the post-break logic sequence for the given [info]. * @@ -504,9 +495,7 @@ object BreakManager : RequestHandler( breakCooldown = info.breakConfig.breakDelay lastPosStarted = ctx.expectedPos onBlockBreak(info) - interaction.sendSequencedPacket(world) { sequence -> - PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) - } + info.startBreakPacket(world, interaction) val swing = info.breakConfig.swing if (swing.isEnabled()) { swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) @@ -601,9 +590,7 @@ object BreakManager : RequestHandler( if (overBreakThreshold) { if (info.isPrimary) { onBlockBreak(info) - interaction.sendSequencedPacket(world) { sequence -> - PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence) - } + info.stopBreakPacket(world, interaction) } else { onBlockBreak(info) } @@ -633,9 +620,7 @@ object BreakManager : RequestHandler( lastPosStarted = ctx.expectedPos onBlockBreak(info) info.request.onStart?.invoke(ctx.expectedPos) - interaction.sendSequencedPacket(world) { sequence: Int -> - PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence) - } + info.startBreakPacket(world, interaction) breakCooldown = info.breakConfig.breakDelay return true } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 99d3e7853..be52b8074 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -40,6 +40,7 @@ object HotbarManager : RequestHandler( 1, TickEvent.Pre, TickEvent.Input.Pre, + TickEvent.Input.Post, TickEvent.Player.Post, // ToDo: Post interact onClose = { checkResetSwap() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index af809d443..72953a3b0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -61,6 +61,7 @@ object PlaceManager : RequestHandler( 0, TickEvent.Pre, TickEvent.Input.Pre, + TickEvent.Input.Post, TickEvent.Player.Post, // ToDo: Post interact onOpen = { activeRequest?.let { processRequest(it) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 01fd1f78b..b6d9d9f16 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -51,6 +51,7 @@ object RotationManager : RequestHandler( 1, TickEvent.Pre, TickEvent.Input.Pre, + TickEvent.Input.Post, TickEvent.Player.Post, // ToDo: Post interact ) { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index da3574bc3..5674465a0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -21,8 +21,8 @@ import com.lambda.interaction.construction.blueprint.TickingBlueprint.Companion. import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.Task import com.lambda.task.RootTask.run +import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState @@ -44,26 +44,26 @@ object Nuker : Module( init { onEnable { task = tickingBlueprint { - val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) - .asSequence() + val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) + .asSequence() + .map { it.blockPos } + .filter { !world.isAir(it) } + .filter { !flatten || it.y >= player.blockPos.y } + .filter { !onlyBreakInstant || blockState(it).getHardness(world, it) <= 1 } + .filter { blockState(it).getHardness(world, it) >= 0 } + .associateWith { TargetState.Air } + + if (fillFloor) { + val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width) .map { it.blockPos } - .filter { !world.isAir(it) } - .filter { !flatten || it.y >= player.blockPos.y } - .filter { !onlyBreakInstant || blockState(it).getHardness(world, it) <= 1 } - .filter { blockState(it).getHardness(world, it) >= 0 } - .associateWith { TargetState.Air } + .associateWith { TargetState.Solid } + return@tickingBlueprint selection + floor + } - if (fillFloor) { - val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width) - .map { it.blockPos } - .associateWith { TargetState.Solid } - return@tickingBlueprint selection + floor - } + selection + }.build() + // ToDo: Add build setting delegates - selection - } - // ToDo: Add build setting delegates - .build() task?.run() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 3cf0e39a1..fee1d8866 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -105,7 +105,8 @@ object PacketMine : Module( } } positions.removeIf { breakPos -> - breakPositions.any { it == breakPos } || queuePositions.any { it == pos } + breakPositions.any { it == breakPos } + || (queue && queuePositions.any { it == pos }) } if (positions.isEmpty()) return@listen val activeBreaking = if (queue) { @@ -150,7 +151,7 @@ object PacketMine : Module( onReBreak = { reBreakPos = it }, onItemDrop = { _ -> itemDrops++ } ) - breakConfig.request(request) + breakConfig.request(request, true) } private fun SafeContext.breakContexts(positions: Collection) = From f396c1ebfa60ca5a07965f521ee4135ea25b6aa9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 20 May 2025 16:39:47 +0100 Subject: [PATCH 208/364] add double break fudge factor setting --- .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt | 1 + .../com/lambda/interaction/request/breaking/BreakConfig.kt | 1 + .../com/lambda/interaction/request/breaking/BreakManager.kt | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 780016c81..fb6e5523c 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -33,6 +33,7 @@ class BreakSettings( override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } + override val doubleBreakFudgeFactor by c.setting("Double Break Fudge Factor", 1, 0..3, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { doubleBreak && vis() } override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() } override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index eca500c4e..5eadb507f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -31,6 +31,7 @@ abstract class BreakConfig( abstract val unsafeCancels: Boolean abstract val breakThreshold: Float abstract val doubleBreak: Boolean + abstract val doubleBreakFudgeFactor: Int abstract val breakDelay: Int abstract val breakStageMask: Set abstract val swing: SwingMode diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index de9e1c635..84877ca71 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -550,7 +550,9 @@ object BreakManager : RequestHandler( world, ctx.expectedPos, player.mainHandStack - ) * info.breakingTicks + ) * if (info.isSecondary) { + info.breakingTicks - info.breakConfig.doubleBreakFudgeFactor + } else info.breakingTicks val overBreakThreshold = progress >= info.getBreakThreshold() From d586e2ad0aa062e8c66ca483698d003636c4e91f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 20 May 2025 16:40:56 +0100 Subject: [PATCH 209/364] isSecondary or isRedundant --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 84877ca71..0d376d893 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -550,7 +550,7 @@ object BreakManager : RequestHandler( world, ctx.expectedPos, player.mainHandStack - ) * if (info.isSecondary) { + ) * if (info.isSecondary || info.isRedundant) { info.breakingTicks - info.breakConfig.doubleBreakFudgeFactor } else info.breakingTicks From e47a99fe842572f06605c675c7d5f578d19f07ad Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 22 May 2025 16:01:31 +0100 Subject: [PATCH 210/364] corrected hotbar keep ticks timing --- .../request/hotbar/HotbarManager.kt | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index be52b8074..1cef1f38f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -61,15 +61,15 @@ object HotbarManager : RequestHandler( listen(priority = Int.MIN_VALUE) { swapsThisTick = 0 if (swapDelay > 0) swapDelay-- - val slotInfo = activeRequest ?: return@listen + val activeInfo = activeRequest ?: return@listen - slotInfo.swapPauseAge++ - slotInfo.activeRequestAge++ - slotInfo.keepTicks-- - - if (slotInfo.keepTicks <= 0) { + if (activeInfo.keepTicks <= 0) { activeRequest = null } + + activeInfo.swapPauseAge++ + activeInfo.activeRequestAge++ + activeInfo.keepTicks-- } listen(priority = Int.MIN_VALUE) { @@ -80,11 +80,17 @@ object HotbarManager : RequestHandler( } override fun SafeContext.handleRequest(request: HotbarRequest) { - val hotbar = request.hotbar - maxSwapsThisTick = hotbar.swapsPerTick - swapDelay = swapDelay.coerceAtMost(hotbar.swapDelay) + activeRequest?.let { activeInfo -> + if (activeInfo.keepTicks <= 0) { + activeRequest = null + } + } + + val config = request.hotbar + maxSwapsThisTick = config.swapsPerTick + swapDelay = swapDelay.coerceAtMost(config.swapDelay) - if (tickStage !in hotbar.sequenceStageMask) return + if (tickStage !in config.sequenceStageMask) return if (request.slot != activeRequest?.slot) { if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return @@ -94,7 +100,7 @@ object HotbarManager : RequestHandler( } swapsThisTick++ - swapDelay = hotbar.swapDelay + swapDelay = config.swapDelay } else activeRequest?.let { current -> request.swapPauseAge = current.swapPauseAge if (current.swappedThisTick && current.keeping) return @@ -106,10 +112,12 @@ object HotbarManager : RequestHandler( } private fun SafeContext.checkResetSwap() { - activeRequest?.let { active -> - if (active.keepTicks <= 0) { + activeRequest?.let { activeInfo -> + if (activeInfo.keepTicks <= 0) { + if (tickStage in activeInfo.hotbar.sequenceStageMask) { + interaction.syncSelectedSlot() + } activeRequest = null - interaction.syncSelectedSlot() } } } From d9408e37731cdf4a7efcd38ff1f29f9876451d7e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 23 May 2025 01:09:17 +0100 Subject: [PATCH 211/364] aligned keep ticks timings --- .../lambda/config/groups/RotationSettings.kt | 4 +-- .../interaction/request/RequestHandler.kt | 4 +-- .../request/hotbar/HotbarManager.kt | 28 +++++++++---------- .../request/rotation/RotationManager.kt | 13 +++++++-- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 5dc0de2bc..88f87776f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -37,10 +37,10 @@ class RotationSettings( override var rotationMode by c.setting("Mode", RotationMode.Sync, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", vis) /** How many ticks to keep the rotation before resetting */ - override val keepTicks by c.setting("Keep Rotation", 3, 0..10, 1, "Ticks to keep rotation", " ticks") { rotate && vis() } + override val keepTicks by c.setting("Keep Rotation", 1, 1..10, 1, "Ticks to keep rotation", " ticks") { rotate && vis() } /** How many ticks to wait before resetting the rotation */ - override val decayTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } + override val decayTicks by c.setting("Reset Rotation", 1, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } /** * At what sub-tick stages rotations can be performed diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index b7240f864..e121dcf5b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -71,7 +71,7 @@ abstract class RequestHandler( * opens the handler for requests for the duration of the given event */ private inline fun openRequestsFor(instance: KClass, stage: T) { - listen(instance, priority = Int.MAX_VALUE - (accumulatedManagerPriority - stagePriority)) { + listen(instance, priority = (Int.MAX_VALUE - 1) - (accumulatedManagerPriority - stagePriority)) { tickStage = stage queuedRequest?.let { request -> handleRequest(request) @@ -83,7 +83,7 @@ abstract class RequestHandler( preEvent() } - listen(instance, priority = Int.MIN_VALUE + stagePriority) { + listen(instance, priority = (Int.MIN_VALUE + 1) + stagePriority) { onClose?.invoke(this) acceptingRequests = false } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 1cef1f38f..be46feb60 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -58,15 +58,19 @@ object HotbarManager : RequestHandler( override fun load(): String { super.load() + listen(priority = Int.MAX_VALUE) { + activeRequest?.let { activeInfo -> + if (activeInfo.keepTicks <= 0) { + activeRequest = null + } + } + } + listen(priority = Int.MIN_VALUE) { swapsThisTick = 0 if (swapDelay > 0) swapDelay-- val activeInfo = activeRequest ?: return@listen - if (activeInfo.keepTicks <= 0) { - activeRequest = null - } - activeInfo.swapPauseAge++ activeInfo.activeRequestAge++ activeInfo.keepTicks-- @@ -80,12 +84,6 @@ object HotbarManager : RequestHandler( } override fun SafeContext.handleRequest(request: HotbarRequest) { - activeRequest?.let { activeInfo -> - if (activeInfo.keepTicks <= 0) { - activeRequest = null - } - } - val config = request.hotbar maxSwapsThisTick = config.swapsPerTick swapDelay = swapDelay.coerceAtMost(config.swapDelay) @@ -113,12 +111,12 @@ object HotbarManager : RequestHandler( private fun SafeContext.checkResetSwap() { activeRequest?.let { activeInfo -> - if (activeInfo.keepTicks <= 0) { - if (tickStage in activeInfo.hotbar.sequenceStageMask) { - interaction.syncSelectedSlot() - } - activeRequest = null + if (activeInfo.keepTicks > 0) return + + if (tickStage in activeInfo.hotbar.sequenceStageMask) { + interaction.syncSelectedSlot() } + activeRequest = null } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index b6d9d9f16..df685ae3f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -73,6 +73,14 @@ object RotationManager : RequestHandler( override fun load(): String { super.load() + listen(priority = Int.MAX_VALUE) { + activeRequest?.let { + if (it.keepTicks <= 0 && it.decayTicks <= 0) { + activeRequest = null + } + } + } + listen(priority = Int.MIN_VALUE) { activeRequest?.let { request -> request.age++ @@ -116,9 +124,8 @@ object RotationManager : RequestHandler( // Tick and reset the context activeRequest?.let { - if (--it.keepTicks > 0) return@let - if (--it.decayTicks >= 0) return@let - activeRequest = null + if (it.keepTicks-- > 0) return@let + it.decayTicks-- } } From 3166c0c6241f59e9f9e065c6388563d7f4795a76 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 23 May 2025 02:20:57 +0100 Subject: [PATCH 212/364] *almost* complete silent swap with double break and fixed listener i forgot to add .Pre on --- .../construction/context/BreakContext.kt | 6 +++--- .../interaction/request/breaking/BreakManager.kt | 15 +++++++++------ .../interaction/request/hotbar/HotbarManager.kt | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 20056ecb5..4ab7cc656 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -84,8 +84,8 @@ data class BreakContext( withState(checkedState, expectedPos, sideColor, result.side) } - fun requestDependencies(request: BreakRequest): Boolean { - val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) - return hotbarRequest.done + fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean { + val request = HotbarRequest(hotbarIndex, breakRequest.hotbar, breakRequest.hotbar.keepTicks.coerceAtLeast(minKeepTicks)) + return request.hotbar.request(request, false).done } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 0d376d893..e9778e1a5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -47,7 +47,6 @@ import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState -import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.gamemode @@ -227,7 +226,12 @@ object BreakManager : RequestHandler( .asReversed() .forEach { info -> if (info.updatedProgressThisTick) return@forEach - if (!info.context.requestDependencies(info.request)) return@run + val minKeepTicks = if (info.isSecondary) { + val breakDelta = info.context.checkedState.calcBlockBreakingDelta(player, world, info.context.expectedPos) + val breakAmount = breakDelta * info.breakingTicks + if (breakAmount >= 1.0f) 1 else 0 + } else 0 + if (!info.context.requestDependencies(info.request, minKeepTicks)) return@run if (tickStage !in info.breakConfig.breakStageMask) return@forEach if ((!rotated && info.isPrimary)) return@run @@ -545,11 +549,10 @@ object BreakManager : RequestHandler( } info.breakingTicks++ - val progress = blockState.calcItemBlockBreakingDelta( + val progress = blockState.calcBlockBreakingDelta( player, world, - ctx.expectedPos, - player.mainHandStack + ctx.expectedPos ) * if (info.isSecondary || info.isRedundant) { info.breakingTicks - info.breakConfig.doubleBreakFudgeFactor } else info.breakingTicks @@ -637,7 +640,7 @@ object BreakManager : RequestHandler( blockState.onBlockBreakStart(world, ctx.expectedPos, player) } - val breakDelta = blockState.calcItemBlockBreakingDelta(player, world, ctx.expectedPos, player.mainHandStack) + val breakDelta = blockState.calcBlockBreakingDelta(player, world, ctx.expectedPos) if (notAir && breakDelta >= info.getBreakThreshold()) { onBlockBreak(info) } else { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index be46feb60..a4b9b1e0c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -58,7 +58,7 @@ object HotbarManager : RequestHandler( override fun load(): String { super.load() - listen(priority = Int.MAX_VALUE) { + listen(priority = Int.MAX_VALUE) { activeRequest?.let { activeInfo -> if (activeInfo.keepTicks <= 0) { activeRequest = null From 43f85c4af3e4e17eed95102f2b7e0b839558631a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 24 May 2025 14:47:22 +0100 Subject: [PATCH 213/364] enable min swap ticks 1 tick before the block is considered expected to be broken --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index e9778e1a5..8b7ed81f7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -228,7 +228,7 @@ object BreakManager : RequestHandler( if (info.updatedProgressThisTick) return@forEach val minKeepTicks = if (info.isSecondary) { val breakDelta = info.context.checkedState.calcBlockBreakingDelta(player, world, info.context.expectedPos) - val breakAmount = breakDelta * info.breakingTicks + val breakAmount = breakDelta * (info.breakingTicks + 1) if (breakAmount >= 1.0f) 1 else 0 } else 0 if (!info.context.requestDependencies(info.request, minKeepTicks)) return@run From bed22dac651c6541049ad64983aa70e255655409 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 27 May 2025 21:22:37 +0100 Subject: [PATCH 214/364] use calcItemBlockBreakingProgress when checking min keep ticks and slight hotbar manager rework --- .../com/lambda/config/groups/BreakSettings.kt | 2 +- .../lambda/config/groups/HotbarSettings.kt | 2 +- .../config/groups/InteractionSettings.kt | 2 +- .../interaction/request/RequestHandler.kt | 1 + .../request/breaking/BreakManager.kt | 8 +++- .../request/hotbar/HotbarManager.kt | 42 ++++++++++--------- .../module/modules/player/PacketMine.kt | 14 +++---- .../kotlin/com/lambda/threading/Threading.kt | 2 +- 8 files changed, 40 insertions(+), 33 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index fb6e5523c..466df2c4e 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -33,7 +33,7 @@ class BreakSettings( override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } - override val doubleBreakFudgeFactor by c.setting("Double Break Fudge Factor", 1, 0..3, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { doubleBreak && vis() } + override val doubleBreakFudgeFactor by c.setting("Double Break Fudge Factor", 3, 0..3, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { doubleBreak && vis() } override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() } override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt index ca8ed7aef..ed450eb1e 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt @@ -31,5 +31,5 @@ class HotbarSettings( override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", vis) override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() } override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) - override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which hotbar actions are performed", vis) + override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post), "The sub-tick timing at which hotbar actions are performed", vis) } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index 408c4ed3c..e6c188221 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -55,7 +55,7 @@ class InteractionSettings( } // Point scan - override val strictRayCast by c.setting("Strict Raycast", true, "Whether to include the environment to the ray cast context", vis) + override val strictRayCast by c.setting("Strict Raycast", false, "Whether to include the environment to the ray cast context", vis) override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible", vis) override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", vis) override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The strategy to select the best hit point", vis) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index e121dcf5b..12477e111 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -62,6 +62,7 @@ abstract class RequestHandler( listen(Int.MIN_VALUE) { activeThisTick = false + queuedRequest = null } return super.load() diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 8b7ed81f7..f7aebdfd5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -47,6 +47,7 @@ import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.gamemode @@ -227,7 +228,12 @@ object BreakManager : RequestHandler( .forEach { info -> if (info.updatedProgressThisTick) return@forEach val minKeepTicks = if (info.isSecondary) { - val breakDelta = info.context.checkedState.calcBlockBreakingDelta(player, world, info.context.expectedPos) + val breakDelta = info.context.checkedState.calcItemBlockBreakingDelta( + player, + world, + info.context.expectedPos, + player.inventory.getStack(info.context.hotbarIndex) + ) val breakAmount = breakDelta * (info.breakingTicks + 1) if (breakAmount >= 1.0f) 1 else 0 } else 0 diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index a4b9b1e0c..4c91b30fa 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -58,14 +58,6 @@ object HotbarManager : RequestHandler( override fun load(): String { super.load() - listen(priority = Int.MAX_VALUE) { - activeRequest?.let { activeInfo -> - if (activeInfo.keepTicks <= 0) { - activeRequest = null - } - } - } - listen(priority = Int.MIN_VALUE) { swapsThisTick = 0 if (swapDelay > 0) swapDelay-- @@ -90,18 +82,29 @@ object HotbarManager : RequestHandler( if (tickStage !in config.sequenceStageMask) return - if (request.slot != activeRequest?.slot) { - if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return + val sameButLonger = activeRequest?.let { active -> + request.slot == active.slot && request.keepTicks >= active.keepTicks + } ?: false + + if (sameButLonger) activeRequest?.let { current -> + request.swapPauseAge = current.swapPauseAge + } else run swap@ { + if (request.slot != activeRequest?.slot) { + if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return + + activeRequest?.let { current -> + if (current.swappedThisTick && current.keeping) return + } + + swapsThisTick++ + swapDelay = config.swapDelay + return@swap + } activeRequest?.let { current -> + request.swapPauseAge = current.swapPauseAge if (current.swappedThisTick && current.keeping) return } - - swapsThisTick++ - swapDelay = config.swapDelay - } else activeRequest?.let { current -> - request.swapPauseAge = current.swapPauseAge - if (current.swappedThisTick && current.keeping) return } activeRequest = request @@ -111,12 +114,11 @@ object HotbarManager : RequestHandler( private fun SafeContext.checkResetSwap() { activeRequest?.let { activeInfo -> - if (activeInfo.keepTicks > 0) return - - if (tickStage in activeInfo.hotbar.sequenceStageMask) { + val canStopSwap = swapsThisTick < maxSwapsThisTick + if (activeInfo.keepTicks <= 0 && tickStage in activeInfo.hotbar.sequenceStageMask && canStopSwap) { + activeRequest = null interaction.syncSelectedSlot() } - activeRequest = null } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index fee1d8866..31fb54ff3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -85,13 +85,6 @@ object PacketMine : Module( attackedThisTick = false } - //ToDo: run on every tick stage - listen { - if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen - val reBreak = reBreakPos ?: return@listen - requestBreakManager(listOf(reBreak), true) - } - listen { it.cancel() } listen { event -> event.cancel() @@ -124,7 +117,12 @@ object PacketMine : Module( } listen { - if (!attackedThisTick) requestBreakManager((breakPositions + queueSorted).toList()) + if (!attackedThisTick) { + requestBreakManager((breakPositions + queueSorted).toList()) + if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen + val reBreak = reBreakPos ?: return@listen + requestBreakManager(listOf(reBreak), true) + } } onDisable { diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index 50a06eb2a..aea0b269e 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -42,7 +42,7 @@ import java.util.concurrent.CompletableFuture * @return The result of the block execution if the context is safe, null otherwise. */ inline fun runSafe(block: SafeContext.() -> T) = - ClientContext().toSafe()?.let { block(it) } + ClientContext().toSafe()?.run(block) /** * This function is used to execute a block of code on a new thread running asynchronously to the game thread. From 61d652877ff7783b0f00fe2f747997a5926561f8 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 19:39:55 +0100 Subject: [PATCH 215/364] inventory manager start :doom:, and cleanup. Also moved the onStart call when in creative mode before onBlockBreak to keep proper order --- .../lambda/config/groups/InventoryConfig.kt | 50 ++------ .../lambda/config/groups/InventorySettings.kt | 20 ++- .../lambda/event/events/UpdateManagerEvent.kt | 9 +- .../request/breaking/BreakManager.kt | 4 +- .../request/hotbar/HotbarManager.kt | 3 +- .../request/inventory/InventoryManager.kt | 117 ++++++++++++++++++ .../request/inventory/InventoryRequest.kt | 30 +++++ .../request/placing/PlaceManager.kt | 2 +- .../request/rotation/RotationManager.kt | 2 +- 9 files changed, 179 insertions(+), 58 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt index 2fe47b03c..6cacd8a4a 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt @@ -20,26 +20,23 @@ package com.lambda.config.groups import com.lambda.interaction.material.ContainerSelection import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.item.ItemUtils +import com.lambda.interaction.request.RequestConfig +import com.lambda.interaction.request.inventory.InventoryRequest import net.minecraft.block.Block -import net.minecraft.item.Item -import net.minecraft.item.Items -import net.minecraft.item.ToolItem -import net.minecraft.item.ToolMaterial -import net.minecraft.item.ToolMaterials -interface InventoryConfig { - val disposables: Set - val swapWithDisposables: Boolean - val providerPriority: Priority - val storePriority: Priority +abstract class InventoryConfig( + prio: Int +) : RequestConfig(prio) { + abstract val disposables: Set + abstract val swapWithDisposables: Boolean + abstract val providerPriority: Priority + abstract val storePriority: Priority - val accessShulkerBoxes: Boolean - val accessEnderChest: Boolean - val accessChests: Boolean - val accessStashes: Boolean + abstract val accessShulkerBoxes: Boolean + abstract val accessEnderChest: Boolean + abstract val accessChests: Boolean + abstract val accessStashes: Boolean val containerSelection: ContainerSelection get() = selectContainer { val allowedContainers = mutableSetOf().apply { @@ -52,27 +49,6 @@ interface InventoryConfig { ofAnyType(*allowedContainers.toTypedArray()) } - val useWoodenTools: Boolean - val useStoneTools: Boolean - val useIronTools: Boolean - val useDiamondTools: Boolean - val useNetheriteTools: Boolean - val useGoldTools: Boolean - val useShears: Boolean - val useFlintAndSteel: Boolean - - val allowedTools get() = mutableSetOf().apply { - addAll(ItemUtils.tools) - if (!useWoodenTools) removeIf { it is ToolItem && it.material == ToolMaterials.WOOD } - if (!useStoneTools) removeIf { it is ToolItem && it.material == ToolMaterials.STONE } - if (!useIronTools) removeIf { it is ToolItem && it.material == ToolMaterials.IRON } - if (!useDiamondTools) removeIf { it is ToolItem && it.material == ToolMaterials.DIAMOND } - if (!useNetheriteTools) removeIf { it is ToolItem && it.material == ToolMaterials.NETHERITE } - if (!useGoldTools) removeIf { it is ToolItem && it.material == ToolMaterials.GOLD } - if (!useShears) removeIf { it == Items.SHEARS } - if (!useFlintAndSteel) removeIf { it == Items.FLINT_AND_STEEL } - } - enum class Priority { WithMinItems, WithMaxItems; diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 5c281a8de..00a755295 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -18,32 +18,30 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.interaction.request.inventory.InventoryManager +import com.lambda.interaction.request.inventory.InventoryRequest import com.lambda.util.item.ItemUtils class InventorySettings( c: Configurable, + prio: Int = 0, vis: () -> Boolean = { true }, -) : InventoryConfig { +) : InventoryConfig(prio) { val page by c.setting("Inventory Page", Page.Container, "The page to open when the module is enabled", vis) override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container} override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones") { vis() && page == Page.Container} - override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container} - override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container} + override val providerPriority by c.setting("Provider Priority", Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container} + override val storePriority by c.setting("Store Priority", Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container} override val accessShulkerBoxes by c.setting("Access Shulker Boxes", true, "Allow access to the player's shulker boxes") { vis() && page == Page.Access} override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest") { vis() && page == Page.Access} override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests") { vis() && page == Page.Access} override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes") { vis() && page == Page.Access} - override val useWoodenTools by c.setting("Use Wooden Tools", false, "Use wooden tools to mine blocks") { vis() && page == Page.Tools} - override val useStoneTools by c.setting("Use Stone Tools", false, "Use stone tools to mine blocks") { vis() && page == Page.Tools} - override val useIronTools by c.setting("Use Iron Tools", false, "Use iron tools to mine blocks") { vis() && page == Page.Tools} - override val useDiamondTools by c.setting("Use Diamond Tools", true, "Use diamond tools to mine blocks") { vis() && page == Page.Tools} - override val useNetheriteTools by c.setting("Use Netherite Tools", true, "Use netherite tools to mine blocks") { vis() && page == Page.Tools} - override val useGoldTools by c.setting("Use Gold Tools", false, "Use gold tools to mine blocks") { vis() && page == Page.Tools} - override val useShears by c.setting("Use Shears", true, "Use shears to mine blocks") { vis() && page == Page.Tools} - override val useFlintAndSteel by c.setting("Use Flint and Steel", true, "Use flint and steel to mine blocks?") { vis() && page == Page.Tools} + override fun requestInternal(request: InventoryRequest, queueIfClosed: Boolean) { + InventoryManager.request(request) + } enum class Page { Container, Access, Tools diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt index 201fdee31..65000f20f 100644 --- a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt @@ -20,8 +20,9 @@ package com.lambda.event.events import com.lambda.event.Event sealed class UpdateManagerEvent { - class Rotation : Event - class Hotbar : Event - class Break : Event - class Place : Event + data object Rotation : Event + data object Inventory : Event + data object Hotbar : Event + data object Break : Event + data object Place : Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index f7aebdfd5..d1cc786b0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -629,8 +629,8 @@ object BreakManager : RequestHandler( if (gamemode.isCreative) { lastPosStarted = ctx.expectedPos - onBlockBreak(info) info.request.onStart?.invoke(ctx.expectedPos) + onBlockBreak(info) info.startBreakPacket(world, interaction) breakCooldown = info.breakConfig.breakDelay return true @@ -683,5 +683,5 @@ object BreakManager : RequestHandler( return inRange && correctMaterial } - override fun preEvent(): Event = UpdateManagerEvent.Break().post() + override fun preEvent(): Event = UpdateManagerEvent.Break.post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 4c91b30fa..b8d59dab1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -42,7 +42,6 @@ object HotbarManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - // ToDo: Post interact onClose = { checkResetSwap() } ) { val serverSlot get() = runSafe { @@ -122,5 +121,5 @@ object HotbarManager : RequestHandler( } } - override fun preEvent(): Event = UpdateManagerEvent.Hotbar().post() + override fun preEvent(): Event = UpdateManagerEvent.Hotbar.post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt new file mode 100644 index 000000000..1b23db5bc --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.inventory + +import com.lambda.Lambda.mc +import com.lambda.context.SafeContext +import com.lambda.event.EventFlow.post +import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.RequestHandler +import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType + +object InventoryManager : RequestHandler( + 1, + TickEvent.Pre, + TickEvent.Input.Pre, + TickEvent.Input.Post, + TickEvent.Player.Post +) { + private var actionsThisTick = 0 + private var maxActionsThisTick = 0 + + override fun load(): String { + super.load() + + listen(priority = Int.MIN_VALUE) { + actionsThisTick = 0 + } + + return "Loaded Inventory Manager!" + } + + override fun SafeContext.handleRequest(request: InventoryRequest) { + if (actionsThisTick + request.actions.size >= maxActionsThisTick) return + if (request.actions.any { !it.canPerform() }) return + request.actions.forEach { action -> + actionsThisTick++ + if (!action.perform().done) return + } + } + + sealed class InventoryAction { + abstract val slot: Slot + var done = false + private set + + fun perform(): InventoryAction { + done = internalPerform() + return this + } + + abstract fun internalPerform(): Boolean + abstract fun canPerform(): Boolean + + data class Swap(override val slot: Slot, val to: Slot) : InventoryAction() { + override fun internalPerform() = clickSlot(slot.id, 0, SlotActionType.SWAP) + override fun canPerform() = slot.isNotEmpty || to.isNotEmpty + } + + data class QuickMove(override val slot: Slot) : InventoryAction() { + override fun internalPerform() = clickSlot(slot.id, 0, SlotActionType.QUICK_MOVE) + override fun canPerform() = slot.isNotEmpty + } + + data class Throw(override val slot: Slot) : InventoryAction() { + override fun internalPerform() = clickSlot(slot.id, 0, SlotActionType.THROW) + override fun canPerform() = slot.isNotEmpty + } + + data class Distribute(override val slot: Slot, val slots: List) : InventoryAction() { + override fun internalPerform(): Boolean { + TODO("Not yet implemented") + } + + override fun canPerform() = slot.isNotEmpty && slots.all { it.canInsert(slot.stack) } + } + data class Clone(override val slot: Slot) : InventoryAction() { + override fun internalPerform(): Boolean { + TODO("Not yet implemented") + } + + override fun canPerform() = slot.isNotEmpty && mc.player?.isCreative == true + } + + fun clickSlot(slotId: Int, button: Int, action: SlotActionType): Boolean { + mc.interactionManager?.let { interaction -> + mc.player?.playerScreenHandler?.syncId?.let { syncId -> + interaction.clickSlot(syncId, slotId, button, action, mc.player) + return true + } + } + return false + } + } + + private val Slot.isEmpty get() = !hasStack() + private val Slot.isNotEmpty get() = hasStack() + + override fun preEvent() = UpdateManagerEvent.Inventory.post() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt new file mode 100644 index 000000000..4090dde78 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.inventory + +import com.lambda.config.groups.InventoryConfig +import com.lambda.interaction.request.Request + +class InventoryRequest( + val actions: List, + inventory: InventoryConfig, + prio: Int +) : Request(prio, inventory) { + override val done: Boolean + get() = actions.all { it.done } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 72953a3b0..55e7f58c8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -365,5 +365,5 @@ object PlaceManager : RequestHandler( ) } - override fun preEvent(): Event = UpdateManagerEvent.Place().post() + override fun preEvent(): Event = UpdateManagerEvent.Place.post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index df685ae3f..e36a69b10 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -290,5 +290,5 @@ object RotationManager : RequestHandler( } } - override fun preEvent(): Event = UpdateManagerEvent.Rotation().post() + override fun preEvent(): Event = UpdateManagerEvent.Rotation.post() } From cd0cd7f6355966e7194779f07f581e8c64a651a2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 20:51:36 +0100 Subject: [PATCH 216/364] added todo --- .../com/lambda/interaction/request/inventory/InventoryManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt index 1b23db5bc..913b865c6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -27,6 +27,7 @@ import com.lambda.interaction.request.RequestHandler import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType +//ToDo: implement :doom: object InventoryManager : RequestHandler( 1, TickEvent.Pre, From 01df8aa066bd058731b6a0053497505897a53bb7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 21:48:16 +0100 Subject: [PATCH 217/364] Revert "added todo" This reverts commit cd0cd7f6355966e7194779f07f581e8c64a651a2. --- .../com/lambda/interaction/request/inventory/InventoryManager.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt index 913b865c6..1b23db5bc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -27,7 +27,6 @@ import com.lambda.interaction.request.RequestHandler import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType -//ToDo: implement :doom: object InventoryManager : RequestHandler( 1, TickEvent.Pre, From 7e5bcf6e3499db484f8abe7ff1a012bbf1505330 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 21:48:17 +0100 Subject: [PATCH 218/364] Revert "inventory manager start :doom:, and cleanup. Also moved the onStart call when in creative mode before onBlockBreak to keep proper order" This reverts commit 61d652877ff7783b0f00fe2f747997a5926561f8. --- .../lambda/config/groups/InventoryConfig.kt | 50 ++++++-- .../lambda/config/groups/InventorySettings.kt | 20 +-- .../lambda/event/events/UpdateManagerEvent.kt | 9 +- .../request/breaking/BreakManager.kt | 4 +- .../request/hotbar/HotbarManager.kt | 3 +- .../request/inventory/InventoryManager.kt | 117 ------------------ .../request/inventory/InventoryRequest.kt | 30 ----- .../request/placing/PlaceManager.kt | 2 +- .../request/rotation/RotationManager.kt | 2 +- 9 files changed, 58 insertions(+), 179 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt index 6cacd8a4a..2fe47b03c 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt @@ -20,23 +20,26 @@ package com.lambda.config.groups import com.lambda.interaction.material.ContainerSelection import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.request.RequestConfig -import com.lambda.interaction.request.inventory.InventoryRequest +import com.lambda.util.item.ItemUtils import net.minecraft.block.Block +import net.minecraft.item.Item +import net.minecraft.item.Items +import net.minecraft.item.ToolItem +import net.minecraft.item.ToolMaterial +import net.minecraft.item.ToolMaterials -abstract class InventoryConfig( - prio: Int -) : RequestConfig(prio) { - abstract val disposables: Set - abstract val swapWithDisposables: Boolean - abstract val providerPriority: Priority - abstract val storePriority: Priority +interface InventoryConfig { + val disposables: Set + val swapWithDisposables: Boolean + val providerPriority: Priority + val storePriority: Priority - abstract val accessShulkerBoxes: Boolean - abstract val accessEnderChest: Boolean - abstract val accessChests: Boolean - abstract val accessStashes: Boolean + val accessShulkerBoxes: Boolean + val accessEnderChest: Boolean + val accessChests: Boolean + val accessStashes: Boolean val containerSelection: ContainerSelection get() = selectContainer { val allowedContainers = mutableSetOf().apply { @@ -49,6 +52,27 @@ abstract class InventoryConfig( ofAnyType(*allowedContainers.toTypedArray()) } + val useWoodenTools: Boolean + val useStoneTools: Boolean + val useIronTools: Boolean + val useDiamondTools: Boolean + val useNetheriteTools: Boolean + val useGoldTools: Boolean + val useShears: Boolean + val useFlintAndSteel: Boolean + + val allowedTools get() = mutableSetOf().apply { + addAll(ItemUtils.tools) + if (!useWoodenTools) removeIf { it is ToolItem && it.material == ToolMaterials.WOOD } + if (!useStoneTools) removeIf { it is ToolItem && it.material == ToolMaterials.STONE } + if (!useIronTools) removeIf { it is ToolItem && it.material == ToolMaterials.IRON } + if (!useDiamondTools) removeIf { it is ToolItem && it.material == ToolMaterials.DIAMOND } + if (!useNetheriteTools) removeIf { it is ToolItem && it.material == ToolMaterials.NETHERITE } + if (!useGoldTools) removeIf { it is ToolItem && it.material == ToolMaterials.GOLD } + if (!useShears) removeIf { it == Items.SHEARS } + if (!useFlintAndSteel) removeIf { it == Items.FLINT_AND_STEEL } + } + enum class Priority { WithMinItems, WithMaxItems; diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 00a755295..5c281a8de 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -18,30 +18,32 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.interaction.request.inventory.InventoryManager -import com.lambda.interaction.request.inventory.InventoryRequest import com.lambda.util.item.ItemUtils class InventorySettings( c: Configurable, - prio: Int = 0, vis: () -> Boolean = { true }, -) : InventoryConfig(prio) { +) : InventoryConfig { val page by c.setting("Inventory Page", Page.Container, "The page to open when the module is enabled", vis) override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container} override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones") { vis() && page == Page.Container} - override val providerPriority by c.setting("Provider Priority", Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container} - override val storePriority by c.setting("Store Priority", Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container} + override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container} + override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container} override val accessShulkerBoxes by c.setting("Access Shulker Boxes", true, "Allow access to the player's shulker boxes") { vis() && page == Page.Access} override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest") { vis() && page == Page.Access} override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests") { vis() && page == Page.Access} override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes") { vis() && page == Page.Access} - override fun requestInternal(request: InventoryRequest, queueIfClosed: Boolean) { - InventoryManager.request(request) - } + override val useWoodenTools by c.setting("Use Wooden Tools", false, "Use wooden tools to mine blocks") { vis() && page == Page.Tools} + override val useStoneTools by c.setting("Use Stone Tools", false, "Use stone tools to mine blocks") { vis() && page == Page.Tools} + override val useIronTools by c.setting("Use Iron Tools", false, "Use iron tools to mine blocks") { vis() && page == Page.Tools} + override val useDiamondTools by c.setting("Use Diamond Tools", true, "Use diamond tools to mine blocks") { vis() && page == Page.Tools} + override val useNetheriteTools by c.setting("Use Netherite Tools", true, "Use netherite tools to mine blocks") { vis() && page == Page.Tools} + override val useGoldTools by c.setting("Use Gold Tools", false, "Use gold tools to mine blocks") { vis() && page == Page.Tools} + override val useShears by c.setting("Use Shears", true, "Use shears to mine blocks") { vis() && page == Page.Tools} + override val useFlintAndSteel by c.setting("Use Flint and Steel", true, "Use flint and steel to mine blocks?") { vis() && page == Page.Tools} enum class Page { Container, Access, Tools diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt index 65000f20f..201fdee31 100644 --- a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt @@ -20,9 +20,8 @@ package com.lambda.event.events import com.lambda.event.Event sealed class UpdateManagerEvent { - data object Rotation : Event - data object Inventory : Event - data object Hotbar : Event - data object Break : Event - data object Place : Event + class Rotation : Event + class Hotbar : Event + class Break : Event + class Place : Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d1cc786b0..f7aebdfd5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -629,8 +629,8 @@ object BreakManager : RequestHandler( if (gamemode.isCreative) { lastPosStarted = ctx.expectedPos - info.request.onStart?.invoke(ctx.expectedPos) onBlockBreak(info) + info.request.onStart?.invoke(ctx.expectedPos) info.startBreakPacket(world, interaction) breakCooldown = info.breakConfig.breakDelay return true @@ -683,5 +683,5 @@ object BreakManager : RequestHandler( return inRange && correctMaterial } - override fun preEvent(): Event = UpdateManagerEvent.Break.post() + override fun preEvent(): Event = UpdateManagerEvent.Break().post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index b8d59dab1..4c91b30fa 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -42,6 +42,7 @@ object HotbarManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, + // ToDo: Post interact onClose = { checkResetSwap() } ) { val serverSlot get() = runSafe { @@ -121,5 +122,5 @@ object HotbarManager : RequestHandler( } } - override fun preEvent(): Event = UpdateManagerEvent.Hotbar.post() + override fun preEvent(): Event = UpdateManagerEvent.Hotbar().post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt deleted file mode 100644 index 1b23db5bc..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.interaction.request.inventory - -import com.lambda.Lambda.mc -import com.lambda.context.SafeContext -import com.lambda.event.EventFlow.post -import com.lambda.event.events.TickEvent -import com.lambda.event.events.UpdateManagerEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.RequestHandler -import net.minecraft.screen.slot.Slot -import net.minecraft.screen.slot.SlotActionType - -object InventoryManager : RequestHandler( - 1, - TickEvent.Pre, - TickEvent.Input.Pre, - TickEvent.Input.Post, - TickEvent.Player.Post -) { - private var actionsThisTick = 0 - private var maxActionsThisTick = 0 - - override fun load(): String { - super.load() - - listen(priority = Int.MIN_VALUE) { - actionsThisTick = 0 - } - - return "Loaded Inventory Manager!" - } - - override fun SafeContext.handleRequest(request: InventoryRequest) { - if (actionsThisTick + request.actions.size >= maxActionsThisTick) return - if (request.actions.any { !it.canPerform() }) return - request.actions.forEach { action -> - actionsThisTick++ - if (!action.perform().done) return - } - } - - sealed class InventoryAction { - abstract val slot: Slot - var done = false - private set - - fun perform(): InventoryAction { - done = internalPerform() - return this - } - - abstract fun internalPerform(): Boolean - abstract fun canPerform(): Boolean - - data class Swap(override val slot: Slot, val to: Slot) : InventoryAction() { - override fun internalPerform() = clickSlot(slot.id, 0, SlotActionType.SWAP) - override fun canPerform() = slot.isNotEmpty || to.isNotEmpty - } - - data class QuickMove(override val slot: Slot) : InventoryAction() { - override fun internalPerform() = clickSlot(slot.id, 0, SlotActionType.QUICK_MOVE) - override fun canPerform() = slot.isNotEmpty - } - - data class Throw(override val slot: Slot) : InventoryAction() { - override fun internalPerform() = clickSlot(slot.id, 0, SlotActionType.THROW) - override fun canPerform() = slot.isNotEmpty - } - - data class Distribute(override val slot: Slot, val slots: List) : InventoryAction() { - override fun internalPerform(): Boolean { - TODO("Not yet implemented") - } - - override fun canPerform() = slot.isNotEmpty && slots.all { it.canInsert(slot.stack) } - } - data class Clone(override val slot: Slot) : InventoryAction() { - override fun internalPerform(): Boolean { - TODO("Not yet implemented") - } - - override fun canPerform() = slot.isNotEmpty && mc.player?.isCreative == true - } - - fun clickSlot(slotId: Int, button: Int, action: SlotActionType): Boolean { - mc.interactionManager?.let { interaction -> - mc.player?.playerScreenHandler?.syncId?.let { syncId -> - interaction.clickSlot(syncId, slotId, button, action, mc.player) - return true - } - } - return false - } - } - - private val Slot.isEmpty get() = !hasStack() - private val Slot.isNotEmpty get() = hasStack() - - override fun preEvent() = UpdateManagerEvent.Inventory.post() -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt deleted file mode 100644 index 4090dde78..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.interaction.request.inventory - -import com.lambda.config.groups.InventoryConfig -import com.lambda.interaction.request.Request - -class InventoryRequest( - val actions: List, - inventory: InventoryConfig, - prio: Int -) : Request(prio, inventory) { - override val done: Boolean - get() = actions.all { it.done } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 55e7f58c8..72953a3b0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -365,5 +365,5 @@ object PlaceManager : RequestHandler( ) } - override fun preEvent(): Event = UpdateManagerEvent.Place.post() + override fun preEvent(): Event = UpdateManagerEvent.Place().post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index e36a69b10..df685ae3f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -290,5 +290,5 @@ object RotationManager : RequestHandler( } } - override fun preEvent(): Event = UpdateManagerEvent.Rotation.post() + override fun preEvent(): Event = UpdateManagerEvent.Rotation().post() } From e1760ba1e2cb9e4b9d35251c7de8f6ccee46ee42 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 21:54:13 +0100 Subject: [PATCH 219/364] data objects for update manager events, moved on start callback above on block break for proper order, and cleaned up unused settings in inventory config --- .../lambda/config/groups/InventoryConfig.kt | 28 ------------------- .../lambda/event/events/UpdateManagerEvent.kt | 8 +++--- .../request/breaking/BreakManager.kt | 4 +-- .../request/hotbar/HotbarManager.kt | 2 +- .../request/placing/PlaceManager.kt | 2 +- .../request/rotation/RotationManager.kt | 2 +- 6 files changed, 9 insertions(+), 37 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt index 2fe47b03c..3c090e088 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt @@ -20,15 +20,8 @@ package com.lambda.config.groups import com.lambda.interaction.material.ContainerSelection import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.item.ItemUtils import net.minecraft.block.Block -import net.minecraft.item.Item -import net.minecraft.item.Items -import net.minecraft.item.ToolItem -import net.minecraft.item.ToolMaterial -import net.minecraft.item.ToolMaterials interface InventoryConfig { val disposables: Set @@ -52,27 +45,6 @@ interface InventoryConfig { ofAnyType(*allowedContainers.toTypedArray()) } - val useWoodenTools: Boolean - val useStoneTools: Boolean - val useIronTools: Boolean - val useDiamondTools: Boolean - val useNetheriteTools: Boolean - val useGoldTools: Boolean - val useShears: Boolean - val useFlintAndSteel: Boolean - - val allowedTools get() = mutableSetOf().apply { - addAll(ItemUtils.tools) - if (!useWoodenTools) removeIf { it is ToolItem && it.material == ToolMaterials.WOOD } - if (!useStoneTools) removeIf { it is ToolItem && it.material == ToolMaterials.STONE } - if (!useIronTools) removeIf { it is ToolItem && it.material == ToolMaterials.IRON } - if (!useDiamondTools) removeIf { it is ToolItem && it.material == ToolMaterials.DIAMOND } - if (!useNetheriteTools) removeIf { it is ToolItem && it.material == ToolMaterials.NETHERITE } - if (!useGoldTools) removeIf { it is ToolItem && it.material == ToolMaterials.GOLD } - if (!useShears) removeIf { it == Items.SHEARS } - if (!useFlintAndSteel) removeIf { it == Items.FLINT_AND_STEEL } - } - enum class Priority { WithMinItems, WithMaxItems; diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt index 201fdee31..ecb1fef96 100644 --- a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt @@ -20,8 +20,8 @@ package com.lambda.event.events import com.lambda.event.Event sealed class UpdateManagerEvent { - class Rotation : Event - class Hotbar : Event - class Break : Event - class Place : Event + data object Rotation : Event + data object Hotbar : Event + data object Break : Event + data object Place : Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index f7aebdfd5..d1cc786b0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -629,8 +629,8 @@ object BreakManager : RequestHandler( if (gamemode.isCreative) { lastPosStarted = ctx.expectedPos - onBlockBreak(info) info.request.onStart?.invoke(ctx.expectedPos) + onBlockBreak(info) info.startBreakPacket(world, interaction) breakCooldown = info.breakConfig.breakDelay return true @@ -683,5 +683,5 @@ object BreakManager : RequestHandler( return inRange && correctMaterial } - override fun preEvent(): Event = UpdateManagerEvent.Break().post() + override fun preEvent(): Event = UpdateManagerEvent.Break.post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 4c91b30fa..d37742b81 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -122,5 +122,5 @@ object HotbarManager : RequestHandler( } } - override fun preEvent(): Event = UpdateManagerEvent.Hotbar().post() + override fun preEvent(): Event = UpdateManagerEvent.Hotbar.post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 72953a3b0..55e7f58c8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -365,5 +365,5 @@ object PlaceManager : RequestHandler( ) } - override fun preEvent(): Event = UpdateManagerEvent.Place().post() + override fun preEvent(): Event = UpdateManagerEvent.Place.post() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index df685ae3f..e36a69b10 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -290,5 +290,5 @@ object RotationManager : RequestHandler( } } - override fun preEvent(): Event = UpdateManagerEvent.Rotation().post() + override fun preEvent(): Event = UpdateManagerEvent.Rotation.post() } From 647cd42edf707b80b9fd42d6d0c1762032053622 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 21:55:54 +0100 Subject: [PATCH 220/364] forgot to remove settings in inventory settings --- .../kotlin/com/lambda/config/groups/InventorySettings.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 5c281a8de..de5363dad 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -36,15 +36,6 @@ class InventorySettings( override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests") { vis() && page == Page.Access} override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes") { vis() && page == Page.Access} - override val useWoodenTools by c.setting("Use Wooden Tools", false, "Use wooden tools to mine blocks") { vis() && page == Page.Tools} - override val useStoneTools by c.setting("Use Stone Tools", false, "Use stone tools to mine blocks") { vis() && page == Page.Tools} - override val useIronTools by c.setting("Use Iron Tools", false, "Use iron tools to mine blocks") { vis() && page == Page.Tools} - override val useDiamondTools by c.setting("Use Diamond Tools", true, "Use diamond tools to mine blocks") { vis() && page == Page.Tools} - override val useNetheriteTools by c.setting("Use Netherite Tools", true, "Use netherite tools to mine blocks") { vis() && page == Page.Tools} - override val useGoldTools by c.setting("Use Gold Tools", false, "Use gold tools to mine blocks") { vis() && page == Page.Tools} - override val useShears by c.setting("Use Shears", true, "Use shears to mine blocks") { vis() && page == Page.Tools} - override val useFlintAndSteel by c.setting("Use Flint and Steel", true, "Use flint and steel to mine blocks?") { vis() && page == Page.Tools} - enum class Page { Container, Access, Tools } From 5a8588b9a801c1359a2fe8cbac90b08b17c2d1a3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 30 May 2025 22:09:29 +0100 Subject: [PATCH 221/364] call onStop when the server breaks the block before the client too --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d1cc786b0..3fd90f37e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -144,6 +144,7 @@ object BreakManager : RequestHandler( return@listen } destroyBlock(info) + info.request.onStop?.invoke(info.context.expectedPos) info.internalOnBreak() if (!info.callbacksCompleted) { info.startPending() From f8371e36b7d6e0869193403abb35f4f41abcc465 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 31 May 2025 14:25:28 +0100 Subject: [PATCH 222/364] avoid liquids setting --- .../com/lambda/config/groups/BreakSettings.kt | 35 ++++++++++--------- .../lambda/config/groups/HotbarSettings.kt | 8 ++--- .../config/groups/InteractionSettings.kt | 12 +++---- .../com/lambda/config/groups/PlaceSettings.kt | 16 ++++----- .../lambda/config/groups/RotationSettings.kt | 4 +-- .../construction/simulation/BuildSimulator.kt | 13 +++---- .../request/breaking/BreakConfig.kt | 1 + .../module/modules/player/PacketMine.kt | 2 +- 8 files changed, 47 insertions(+), 44 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 466df2c4e..796c94733 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -29,26 +29,27 @@ class BreakSettings( vis: () -> Boolean = { true } ) : BreakConfig(priority) { override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() } - override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", vis) - override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() } - override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken") { vis() } - override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() } + override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", visibility = vis) + override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes", visibility = vis) + override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken", visibility = vis) + override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis) override val doubleBreakFudgeFactor by c.setting("Double Break Fudge Factor", 3, 0..3, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { doubleBreak && vis() } - override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() } - override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", vis) - override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() } + override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis) + override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", visibility = vis) + override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis) override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } - override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() } - override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() } - override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() } - override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { vis() } - override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() } - override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() } - override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() } - override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() } + override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds", visibility = vis) + override val particles by c.setting("Particles", true, "Renders the breaking particles", visibility = vis) + override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages", visibility = vis) + override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", visibility = vis) + override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken", visibility = vis) + override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking", visibility = vis) + override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks", visibility = vis) + override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick", visibility = vis) override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis) + override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill", visibility = vis) override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } - override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() } - override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() } + override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis) + override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis) override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt index ed450eb1e..9de102178 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt @@ -27,9 +27,9 @@ class HotbarSettings( priority: Priority = 0, vis: () -> Boolean = { true } ) : HotbarConfig(priority) { - override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", vis) - override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", vis) + override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", visibility = vis) + override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", visibility = vis) override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() } - override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) - override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post), "The sub-tick timing at which hotbar actions are performed", vis) + override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", visibility = vis) + override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post), "The sub-tick timing at which hotbar actions are performed", visibility = vis) } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index e6c188221..f1a8ec0ac 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -28,7 +28,7 @@ class InteractionSettings( vis: () -> Boolean = { true }, ) : InteractionConfig { // Reach - private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", vis) + private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", visibility = vis) private val attackReachSetting = if (usage.entity) c.setting("Attack Reach", DEFAULT_ATTACK_REACH, 1.0..10.0, 0.01, "Maximum entity interaction distance") { vis() && !useDefaultReach } else null private val interactReachSetting = if (usage.block) c.setting("Interact Reach", DEFAULT_INTERACT_REACH, 1.0..10.0, 0.01, "Maximum block interaction distance") { vis() && !useDefaultReach } else null @@ -55,13 +55,13 @@ class InteractionSettings( } // Point scan - override val strictRayCast by c.setting("Strict Raycast", false, "Whether to include the environment to the ray cast context", vis) - override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible", vis) - override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", vis) - override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The strategy to select the best hit point", vis) + override val strictRayCast by c.setting("Strict Raycast", false, "Whether to include the environment to the ray cast context", visibility = vis) + override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible", visibility = vis) + override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", visibility = vis) + override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The strategy to select the best hit point", visibility = vis) // Swing - override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", vis) + override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", visibility = vis) companion object { const val DEFAULT_ATTACK_REACH = 3.0 diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt index 1fce32319..c9ff99ff7 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt @@ -27,14 +27,14 @@ class PlaceSettings( priority: Priority = 0, vis: () -> Boolean = { true } ) : PlaceConfig(priority) { - override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() } - override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces") { vis() } + override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", visibility = vis) + override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces", visibility = vis) override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() } - override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions are performed") { vis() } - override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() } - override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() } - override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() } - override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() } + override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions are performed", visibility = vis) + override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation", visibility = vis) + override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements", visibility = vis) + override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick", visibility = vis) + override val swing by c.setting("Swing", true, "Swings the players hand when placing", visibility = vis) override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing } - override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() } + override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds", visibility = vis) } diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 88f87776f..cec04b1e9 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -34,7 +34,7 @@ class RotationSettings( priority: Priority = 0, vis: () -> Boolean = { true } ) : RotationConfig(priority) { - override var rotationMode by c.setting("Mode", RotationMode.Sync, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", vis) + override var rotationMode by c.setting("Mode", RotationMode.Sync, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", visibility = vis) /** How many ticks to keep the rotation before resetting */ override val keepTicks by c.setting("Keep Rotation", 1, 1..10, 1, "Ticks to keep rotation", " ticks") { rotate && vis() } @@ -45,7 +45,7 @@ class RotationSettings( /** * At what sub-tick stages rotations can be performed */ - override val rotationStageMask by c.setting("Rotation Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick stages at which rotations can be performed", vis) + override val rotationStageMask by c.setting("Rotation Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick stages at which rotations can be performed", visibility = vis) /** Whether the rotation is instant */ var instant by c.setting("Instant Rotation", true, "Instantly rotate") { rotate && vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index f330d9d93..ca1ec73c4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -35,6 +35,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.request.breaking.BreakConfig import com.lambda.interaction.request.placing.PlaceConfig import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo @@ -96,7 +97,7 @@ object BuildSimulator { if (it.isEmpty()) return@let return@flatMap it } - checkBreakResults(pos, eye, build.placing, interact, rotation, inventory, build).let { + checkBreakResults(pos, eye, build.breaking, interact, rotation, inventory, build).let { if (it.isEmpty()) return@let return@flatMap it } @@ -404,7 +405,7 @@ object BuildSimulator { private fun SafeContext.checkBreakResults( pos: BlockPos, eye: Vec3d, - place: PlaceConfig, + breaking: BreakConfig, interact: InteractionConfig, rotation: RotationConfig, inventory: InventoryConfig, @@ -432,7 +433,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye, place, interact, rotation, inventory) + val submerge = checkPlaceResults(pos, TargetState.Solid, eye, build.placing, interact, rotation, inventory) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -443,13 +444,13 @@ object BuildSimulator { }.map { pos.offset(it) } /* block has liquids next to it that will leak when broken */ - if (adjacentLiquids.isNotEmpty()) { + if (adjacentLiquids.isNotEmpty() && build.breaking.avoidLiquids) { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { - checkPlaceResults(liquidPos, TargetState.Solid, eye, place, interact, rotation, inventory) + checkPlaceResults(liquidPos, TargetState.Solid, eye, build.placing, interact, rotation, inventory) } else { - checkBreakResults(liquidPos, eye, place, interact, rotation, inventory, build) + checkBreakResults(liquidPos, eye, build.breaking, interact, rotation, inventory, build) } acc.addAll(submerge) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 5eadb507f..d04dd27d8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -44,6 +44,7 @@ abstract class BreakConfig( abstract val maxPendingBreaks: Int abstract val breaksPerTick: Int abstract val suitableToolsOnly: Boolean + abstract val avoidLiquids: Boolean abstract val breakWeakBlocks: Boolean abstract val forceSilkTouch: Boolean abstract val forceFortunePickaxe: Boolean diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 31fb54ff3..3c437592c 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -58,7 +58,7 @@ object PacketMine : Module( private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak } private val breakRadius by setting("Break Radius", 0, 0..5, 1, "Selects and breaks all blocks within the break radius of the selected block") - private val flatten by setting("Flatten", false, "Wont allow breaking extra blocks under your players position") { breakRadius > 0 } + private val flatten by setting("Flatten", true, "Wont allow breaking extra blocks under your players position") { breakRadius > 0 } private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once") .onValueChange { _, to -> if (!to) queuePositions.clear() } private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue } From 40f56ab01867c8617a3c05793703e912bd5f5189 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 31 May 2025 16:58:42 +0100 Subject: [PATCH 223/364] decouple fudge factor from double break to prevent desync when the difference between client and server ticks are close enough to cause a small but critical mismatch in breaking progress --- .../com/lambda/config/groups/BreakSettings.kt | 8 ++-- .../request/breaking/BreakConfig.kt | 2 +- .../request/breaking/BreakManager.kt | 40 ++++++++++++------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 796c94733..500eadb40 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -28,12 +28,12 @@ class BreakSettings( priority: Priority = 0, vis: () -> Boolean = { true } ) : BreakConfig(priority) { - override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() } + override val breakMode by c.setting("Break Mode", BreakMode.Packet, visibility = vis) override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", visibility = vis) override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes", visibility = vis) - override val breakThreshold by c.setting("Break Threshold", 0.7f, 0.1f..1.0f, 0.02f, "The break amount at which the block is considered broken", visibility = vis) + override val breakThreshold by c.setting("Break Threshold", 0.75f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis) override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis) - override val doubleBreakFudgeFactor by c.setting("Double Break Fudge Factor", 3, 0..3, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { doubleBreak && vis() } + override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis) override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis) override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", visibility = vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis) @@ -48,7 +48,7 @@ class BreakSettings( override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick", visibility = vis) override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis) override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill", visibility = vis) - override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() } + override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", visibility = vis) override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis) override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis) override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index d04dd27d8..d41e7a637 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -31,7 +31,7 @@ abstract class BreakConfig( abstract val unsafeCancels: Boolean abstract val breakThreshold: Float abstract val doubleBreak: Boolean - abstract val doubleBreakFudgeFactor: Int + abstract val fudgeFactor: Int abstract val breakDelay: Int abstract val breakStageMask: Set abstract val swing: SwingMode diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 3fd90f37e..366ebd9e0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -58,6 +58,8 @@ import net.minecraft.entity.ItemEntity import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import kotlin.math.ceil +import kotlin.math.max object BreakManager : RequestHandler( 0, @@ -494,6 +496,7 @@ object BreakManager : RequestHandler( * @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress */ private fun SafeContext.updateBreakProgress(info: BreakInfo): Boolean { + val config = info.breakConfig info.updatedProgressThisTick = true val ctx = info.context val hitResult = ctx.result @@ -503,13 +506,13 @@ object BreakManager : RequestHandler( onBlockBreak(info) return true } - breakCooldown = info.breakConfig.breakDelay + breakCooldown = config.breakDelay lastPosStarted = ctx.expectedPos onBlockBreak(info) info.startBreakPacket(world, interaction) - val swing = info.breakConfig.swing + val swing = config.swing if (swing.isEnabled()) { - swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + swingHand(config.swingType, Hand.MAIN_HAND) } return true } @@ -541,9 +544,9 @@ object BreakManager : RequestHandler( info.internalOnCancel() return false } - val swing = info.breakConfig.swing + val swing = config.swing if (swing.isEnabled() && swing != BreakConfig.SwingMode.End) { - swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + swingHand(config.swingType, Hand.MAIN_HAND) } return true } @@ -560,9 +563,16 @@ object BreakManager : RequestHandler( player, world, ctx.expectedPos - ) * if (info.isSecondary || info.isRedundant) { - info.breakingTicks - info.breakConfig.doubleBreakFudgeFactor - } else info.breakingTicks + ).let { breakDelta -> + breakDelta * if (info.isSecondary || info.isRedundant) { + info.breakingTicks - config.fudgeFactor + } else { + val serverBreakTicks = ceil(1.0 / breakDelta).toInt() + val clientBreakTicks = ceil(config.breakThreshold / breakDelta).toInt() + val diff = serverBreakTicks - clientBreakTicks + info.breakingTicks - max(config.fudgeFactor - diff, 0) + } + } val overBreakThreshold = progress >= info.getBreakThreshold() @@ -573,7 +583,7 @@ object BreakManager : RequestHandler( return true } - if (info.breakConfig.sounds) { + if (config.sounds) { if (info.soundsCooldown % 4.0f == 0.0f) { val blockSoundGroup = blockState.soundGroup mc.soundManager.play( @@ -590,15 +600,15 @@ object BreakManager : RequestHandler( info.soundsCooldown++ } - if (info.breakConfig.particles) { + if (config.particles) { mc.particleManager.addBlockBreakingParticles(ctx.expectedPos, hitResult.side) } - if (info.breakConfig.breakingTexture) { + if (config.breakingTexture) { info.setBreakingTextureStage(player, world) } - val swing = info.breakConfig.swing + val swing = config.swing if (overBreakThreshold) { if (info.isPrimary) { onBlockBreak(info) @@ -606,10 +616,10 @@ object BreakManager : RequestHandler( } else { onBlockBreak(info) } - if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) - breakCooldown = info.breakConfig.breakDelay + if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) swingHand(config.swingType, Hand.MAIN_HAND) + breakCooldown = config.breakDelay } else { - if (swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType, Hand.MAIN_HAND) + if (swing == BreakConfig.SwingMode.Constant) swingHand(config.swingType, Hand.MAIN_HAND) } return true From 26b2d682f84e81e573ea9020487087f78313c7fe Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 31 May 2025 19:02:00 +0100 Subject: [PATCH 224/364] apply fudge factor to all breaks --- .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt | 2 +- .../com/lambda/interaction/request/breaking/BreakManager.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 500eadb40..b23b87eea 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -31,7 +31,7 @@ class BreakSettings( override val breakMode by c.setting("Break Mode", BreakMode.Packet, visibility = vis) override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", visibility = vis) override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes", visibility = vis) - override val breakThreshold by c.setting("Break Threshold", 0.75f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis) + override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis) override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis) override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis) override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 366ebd9e0..4ab0dd882 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -58,8 +58,8 @@ import net.minecraft.entity.ItemEntity import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import kotlin.math.abs import kotlin.math.ceil -import kotlin.math.max object BreakManager : RequestHandler( 0, @@ -570,7 +570,7 @@ object BreakManager : RequestHandler( val serverBreakTicks = ceil(1.0 / breakDelta).toInt() val clientBreakTicks = ceil(config.breakThreshold / breakDelta).toInt() val diff = serverBreakTicks - clientBreakTicks - info.breakingTicks - max(config.fudgeFactor - diff, 0) + info.breakingTicks - config.fudgeFactor.coerceAtMost(abs(diff)) } } From f12aab32d22fe50d88aceb18a60138cb75efbb87 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 31 May 2025 19:43:36 +0100 Subject: [PATCH 225/364] moved manager utils into companion object in reuqest handler --- .../interaction/request/ManagerUtils.kt | 25 ------------------- .../interaction/request/RequestHandler.kt | 7 +++++- 2 files changed, 6 insertions(+), 26 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt deleted file mode 100644 index 0d9a860cf..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.interaction.request - -import com.lambda.util.reflections.getInstances - -object ManagerUtils { - val managers = getInstances>() - val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 12477e111..e111abd10 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -22,8 +22,8 @@ import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.ManagerUtils.accumulatedManagerPriority import com.lambda.threading.runSafe +import com.lambda.util.reflections.getInstances import kotlin.reflect.KClass /** @@ -119,4 +119,9 @@ abstract class RequestHandler( abstract fun SafeContext.handleRequest(request: R) protected abstract fun preEvent(): Event + + companion object { + val managers = getInstances>() + val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } + } } From 36e0057be3547fe30a0213d6fd806b71131f4194 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 31 May 2025 20:48:10 +0100 Subject: [PATCH 226/364] Revert "moved manager utils into companion object in reuqest handler" This reverts commit f12aab32d22fe50d88aceb18a60138cb75efbb87. --- .../interaction/request/ManagerUtils.kt | 25 +++++++++++++++++++ .../interaction/request/RequestHandler.kt | 7 +----- 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt new file mode 100644 index 000000000..0d9a860cf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request + +import com.lambda.util.reflections.getInstances + +object ManagerUtils { + val managers = getInstances>() + val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index e111abd10..12477e111 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -22,8 +22,8 @@ import com.lambda.core.Loadable import com.lambda.event.Event import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.ManagerUtils.accumulatedManagerPriority import com.lambda.threading.runSafe -import com.lambda.util.reflections.getInstances import kotlin.reflect.KClass /** @@ -119,9 +119,4 @@ abstract class RequestHandler( abstract fun SafeContext.handleRequest(request: R) protected abstract fun preEvent(): Event - - companion object { - val managers = getInstances>() - val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } - } } From df89d64592f4c7371771f1f393b6e486489c2216 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 31 May 2025 15:53:32 -0400 Subject: [PATCH 227/364] Update FastBreak.kt --- .../lambda/module/modules/player/FastBreak.kt | 132 +++++++----------- 1 file changed, 47 insertions(+), 85 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt index 9b821ddfc..932025d05 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt @@ -23,10 +23,8 @@ import com.lambda.event.events.PlayerEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.DynamicAABB import com.lambda.graphics.renderer.esp.builders.buildFilled import com.lambda.graphics.renderer.esp.builders.buildOutline -import com.lambda.graphics.renderer.esp.global.DynamicESP import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.lerp @@ -45,46 +43,27 @@ object FastBreak : Module( ) { private val page by setting("Page", Page.Mining) - private val breakDelay by setting("Break Delay", 5, 0..5, 1, unit = "ticks", description = "The tick delay between breaking blocks", visibility = { page == Page.Mining }) - private val breakThreshold by setting("Break Threshold", 0.7f, 0.2f..1.0f, 0.1f, description = "The progress at which the block will break.", visibility = { page == Page.Mining }) + private val breakDelay by setting("Break Delay", 5, 0..5, 1, "The tick delay between breaking blocks", unit = " ticks") { page == Page.Mining } + private val breakThreshold by setting("Break Threshold", 0.7f, 0.2f..1.0f, 0.1f, "The progress at which the block will break.") { page == Page.Mining } - private val renderMode by setting("Render Mode", RenderMode.Out, "The animation style of the renders", visibility = { page == Page.Render }) - private val renderSetting by setting("Render Setting", RenderSetting.Both, "The different ways to draw the renders", visibility = { page == Page.Render && renderMode.isEnabled() }) + private val render by setting("Render", true, "Render block breaking progress") + private val renderMode by setting("Render Mode", RenderMode.Out, "The animation style of the renders") { page == Page.Render } + private val renderSetting by setting("Render Setting", RenderSetting.Both, "The different ways to draw the renders") { page == Page.Render && render } - private val fillColourMode by setting("Fill Mode", ColourMode.Dynamic, visibility = { page == Page.Render && renderSetting != RenderSetting.Outline }) - private val staticFillColour by setting("Static Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static fill of the box", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Static }) - private val startFillColour by setting("Start Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start fill of the box", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic }) - private val endFillColour by setting("End Fill Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end fill of the box", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic }) + private val fillColourMode by setting("Fill Mode", ColourMode.Dynamic) { page == Page.Render && renderSetting != RenderSetting.Outline } + private val staticFillColour by setting("Static Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static fill of the box") { page == Page.Render && render && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Static } + private val startFillColour by setting("Start Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start fill of the box") { page == Page.Render && render && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic } + private val endFillColour by setting("End Fill Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end fill of the box") { page == Page.Render && render && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic } - private val outlineColourMode by setting("Outline Mode", ColourMode.Dynamic, visibility = { page == Page.Render && renderSetting != RenderSetting.Fill }) - private val staticOutlineColour by setting("Static Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static outline of the box", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Static }) - private val startOutlineColour by setting("Start Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start outline of the box", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }) - private val endOutlineColour by setting("End Outline Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end outline of the box", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }) - private val outlineWidth by setting("Outline Width", 1f, 0f..3f, 0.1f, "the thickness of the outline", visibility = { page == Page.Render && renderMode.isEnabled() && renderSetting != RenderSetting.Fill }) + private val outlineColourMode by setting("Outline Mode", ColourMode.Dynamic) { page == Page.Render && renderSetting != RenderSetting.Fill } + private val staticOutlineColour by setting("Static Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Static } + private val startOutlineColour by setting("Start Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic } + private val endOutlineColour by setting("End Outline Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic } + private val outlineWidth by setting("Outline Width", 1f, 0f..3f, 0.1f, "the thickness of the outline") { page == Page.Render && render && renderSetting != RenderSetting.Fill } - private val renderer = DynamicESP private var boxSet = emptySet() - private enum class Page { - Mining, Render - } - - private enum class RenderMode { - Out, In, InOut, OutIn, Static, None; - - fun isEnabled(): Boolean = - this != None - } - - private enum class ColourMode { - Static, Dynamic - } - - private enum class RenderSetting { - Both, Fill, Outline - } - init { listen { if (it.packet !is PlayerActionC2SPacket @@ -112,70 +91,53 @@ object FastBreak : Module( .calcBlockBreakingDelta(player, world, it.pos) * (1 - breakThreshold) } - listen { - if (!renderMode.isEnabled()) return@listen - - val pos = interaction.currentBreakingPos - boxSet = world.getBlockState(pos).getOutlineShape(world, pos).boundingBoxes.toSet() - } - - listen { - if (!interaction.isBreakingBlock || !renderMode.isEnabled()) return@listen + listen { event -> + if (!render || !interaction.isBreakingBlock) return@listen val pos = interaction.currentBreakingPos val breakDelta = world.getBlockState(pos).calcBlockBreakingDelta(player, world, pos) - renderer.clear() - boxSet.forEach { box -> + world.getBlockState(pos).getOutlineShape(world, pos).boundingBoxes.forEach { val previousFactor = interaction.currentBreakingProgress - breakDelta val nextFactor = interaction.currentBreakingProgress - val currentFactor = lerp(mc.tickDelta, previousFactor, nextFactor) + val factor = lerp(mc.tickDelta, previousFactor, nextFactor).toDouble() - val fillColour = if (fillColourMode == ColourMode.Dynamic) { - lerp(currentFactor.toDouble(), startFillColour, endFillColour) - } else { - staticFillColour - } + val fillColour = fillColourMode.block(factor, if (fillColourMode == ColourMode.Static) staticFillColour else startFillColour, endFillColour) + val outlineColor = outlineColourMode.block(factor, if (fillColourMode == ColourMode.Static) staticOutlineColour else startOutlineColour, endOutlineColour) + val box = renderMode.block(factor, it) + .offset(pos) - val outlineColour = if (outlineColourMode == ColourMode.Dynamic) { - lerp(currentFactor.toDouble(), startOutlineColour, endOutlineColour) - } else { - staticOutlineColour - } + when (renderSetting) { + RenderSetting.Both -> { + event.renderer.buildFilled(box, fillColour) + event.renderer.buildOutline(box, outlineColor) + } - val renderBox = if (renderMode != RenderMode.Static) { - getLerpBox(box, currentFactor).offset(pos) - } else { - box.offset(pos) + RenderSetting.Fill -> event.renderer.buildFilled(box, fillColour) + RenderSetting.Outline -> event.renderer.buildOutline(box, outlineColor) } - - val dynamicAABB = DynamicAABB() - dynamicAABB.update(renderBox) - - if (renderSetting != RenderSetting.Outline) renderer.buildFilled(dynamicAABB, fillColour) - if (renderSetting != RenderSetting.Fill) renderer.buildOutline(dynamicAABB, outlineColour) } - renderer.upload() } } - private fun getLerpBox(box: Box, factor: Float): Box { - val boxCenter = Box(box.center, box.center) - return when (renderMode) { - RenderMode.Out -> lerp(factor.toDouble(), boxCenter, box) - RenderMode.In -> lerp(factor.toDouble(), box, boxCenter) - RenderMode.InOut -> { - if (factor >= 0.5f) lerp((factor.toDouble() - 0.5) * 2, boxCenter, box) - else lerp(factor.toDouble() * 2, box, boxCenter) - } - RenderMode.OutIn -> { - if (factor >= 0.5f) lerp((factor.toDouble() - 0.5) * 2, box, boxCenter) - else lerp(factor.toDouble() * 2, boxCenter, box) - } - else -> box - } + enum class Page { + Mining, Render } - fun SafeContext.interpolateProgress(min: Double = 0.0, max: Double = 1.0) = - transform(interaction.currentBreakingProgress.toDouble(), 0.0, 1.0, min, max) + enum class RenderSetting { + Both, Fill, Outline + } + + enum class ColourMode(val block: (Double, Color, Color) -> Color) { + Static({ _, start, _ -> start }), + Dynamic({ factor, start, end -> lerp(factor, start, end) }) + } + + val Box.ofCenter: Box get() = Box(center, center) + + enum class RenderMode(val block: (Double, Box) -> Box) { + Out({ factor, box -> lerp(factor, box.ofCenter, box)}), + In({ factor, box -> lerp(factor, box, box.ofCenter)}), + Static({ _, box -> box }); + } } From 6a51aca0d73062802490f821c5b2c6cc82725f66 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 7 Jun 2025 21:56:41 +0100 Subject: [PATCH 228/364] make fudge factor affect everything --- .../interaction/request/breaking/BreakManager.kt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 4ab0dd882..c7de19b45 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -58,8 +58,6 @@ import net.minecraft.entity.ItemEntity import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos -import kotlin.math.abs -import kotlin.math.ceil object BreakManager : RequestHandler( 0, @@ -563,16 +561,7 @@ object BreakManager : RequestHandler( player, world, ctx.expectedPos - ).let { breakDelta -> - breakDelta * if (info.isSecondary || info.isRedundant) { - info.breakingTicks - config.fudgeFactor - } else { - val serverBreakTicks = ceil(1.0 / breakDelta).toInt() - val clientBreakTicks = ceil(config.breakThreshold / breakDelta).toInt() - val diff = serverBreakTicks - clientBreakTicks - info.breakingTicks - config.fudgeFactor.coerceAtMost(abs(diff)) - } - } + ) * (info.breakingTicks - config.fudgeFactor) val overBreakThreshold = progress >= info.getBreakThreshold() From 52428ee8f61776a965a0619c4be6bcc974ae9a7a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 01:51:46 +0100 Subject: [PATCH 229/364] looser redundant break handling when considered broken --- .../request/breaking/BreakManager.kt | 37 ++++++++++--------- .../request/breaking/BrokenBlockHandler.kt | 2 +- .../request/breaking/ReBreakManager.kt | 5 ++- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index c7de19b45..2da9fd30f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -406,25 +406,29 @@ object BreakManager : RequestHandler( */ private fun SafeContext.onBlockBreak(info: BreakInfo) { info.request.onStop?.invoke(info.context.expectedPos) - when (info.breakConfig.breakConfirmation) { - BreakConfirmationMode.None -> { - destroyBlock(info) - info.internalOnBreak() - if (!info.callbacksCompleted) { + if (info.isRedundant) { + info.startPending() + } else { + when (info.breakConfig.breakConfirmation) { + BreakConfirmationMode.None -> { + destroyBlock(info) + info.internalOnBreak() + if (!info.callbacksCompleted) { + info.startPending() + } else { + ReBreakManager.offerReBreak(info) + } + } + BreakConfirmationMode.BreakThenAwait -> { + destroyBlock(info) + info.startPending() + } + BreakConfirmationMode.AwaitThenBreak -> { info.startPending() - } else { - ReBreakManager.offerReBreak(info) } } - BreakConfirmationMode.BreakThenAwait -> { - destroyBlock(info) - info.startPending() - } - BreakConfirmationMode.AwaitThenBreak -> { - info.startPending() - } + breaksThisTick++ } - breaksThisTick++ info.nullify() } @@ -508,8 +512,7 @@ object BreakManager : RequestHandler( lastPosStarted = ctx.expectedPos onBlockBreak(info) info.startBreakPacket(world, interaction) - val swing = config.swing - if (swing.isEnabled()) { + if (config.swing.isEnabled()) { swingHand(config.swingType, Hand.MAIN_HAND) } return true diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 05e8f6f1a..a55ca936a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -88,7 +88,7 @@ object BrokenBlockHandler { return@listen } - if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak) { + if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak || pending.isRedundant) { destroyBlock(pending) } pending.internalOnBreak() diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 12305b61a..27635e810 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -74,7 +74,10 @@ object ReBreakManager { val breakProgress = context.checkedState.calcBlockBreakingDelta(player, world, context.expectedPos) return@runSafe if (info.breakingTicks * breakProgress >= info.breakConfig.breakThreshold) { - if (!context.checkedState.isEmpty && !awaitThenBreak) { + if (context.checkedState.isEmpty) { + return@runSafe ReBreakResult.Ignored + } + if (!awaitThenBreak) { destroyBlock(info) } info.stopBreakPacket(world, interaction) From 6d40644e1c2a1d37e9e9c4dfcb597bc7275b72d2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 02:02:21 +0100 Subject: [PATCH 230/364] use isEmpty over isAir to account for waterloggable blocks --- .../interaction/request/breaking/BreakManager.kt | 15 ++++++++------- .../request/breaking/BrokenBlockHandler.kt | 6 ++---- .../request/breaking/ReBreakManager.kt | 2 +- .../src/main/kotlin/com/lambda/util/BlockUtils.kt | 4 ++++ 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 2da9fd30f..5fb204881 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -37,9 +37,7 @@ import com.lambda.interaction.request.breaking.BreakManager.activeRequest import com.lambda.interaction.request.breaking.BreakManager.processRequest import com.lambda.interaction.request.breaking.BreakType.Primary import com.lambda.interaction.request.breaking.BreakType.ReBreak -import com.lambda.interaction.request.breaking.BrokenBlockHandler.brokenState import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock -import com.lambda.interaction.request.breaking.BrokenBlockHandler.isBroken import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingBreaks import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending @@ -47,7 +45,10 @@ import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.brokenState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta +import com.lambda.util.BlockUtils.isBroken +import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.gamemode @@ -309,7 +310,7 @@ object BreakManager : RequestHandler( } } - return !blockState(ctx.expectedPos).isAir + return !blockState(ctx.expectedPos).isEmpty } /** @@ -553,7 +554,7 @@ object BreakManager : RequestHandler( } val blockState = blockState(ctx.expectedPos) - if (blockState.isAir) { + if (blockState.isEmpty) { info.nullify() info.internalOnCancel() return false @@ -644,13 +645,13 @@ object BreakManager : RequestHandler( lastPosStarted = ctx.expectedPos val blockState = blockState(ctx.expectedPos) - val notAir = !blockState.isAir - if (notAir && info.breakingTicks == 0) { + val notEmpty = !blockState.isEmpty + if (notEmpty && info.breakingTicks == 0) { blockState.onBlockBreakStart(world, ctx.expectedPos, player) } val breakDelta = blockState.calcBlockBreakingDelta(player, world, ctx.expectedPos) - if (notAir && breakDelta >= info.getBreakThreshold()) { + if (notEmpty && breakDelta >= info.getBreakThreshold()) { onBlockBreak(info) } else { info.apply { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index a55ca936a..c6ed1dd41 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -29,7 +29,9 @@ import com.lambda.interaction.request.breaking.BreakManager.lastPosStarted import com.lambda.interaction.request.breaking.BreakManager.matchesBlockItem import com.lambda.interaction.request.breaking.ReBreakManager.reBreak import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.brokenState import com.lambda.util.BlockUtils.fluidState +import com.lambda.util.BlockUtils.isBroken import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info import com.lambda.util.Communication.warn @@ -184,8 +186,4 @@ object BrokenBlockHandler { return setState } - - val BlockState.isEmpty get() = matches(fluidState.blockState) - val BlockState.brokenState: BlockState get() = fluidState.blockState - fun isBroken(oldState: BlockState, newState: BlockState) = !oldState.isEmpty && oldState.brokenState.matches(newState) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 27635e810..c43a708ce 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -23,8 +23,8 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock -import com.lambda.interaction.request.breaking.BrokenBlockHandler.isEmpty import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.player.swingHand import net.minecraft.util.Hand diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 6f09a1a62..d4e31e5e2 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -309,4 +309,8 @@ object BlockUtils { return speedMultiplier } + + val BlockState.isEmpty get() = matches(fluidState.blockState) + val BlockState.brokenState: BlockState get() = fluidState.blockState + fun isBroken(oldState: BlockState, newState: BlockState) = !oldState.isEmpty && oldState.brokenState.matches(newState) } From 339f00d3b4c1bf8d5d09068102f07f9cb276853e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 02:10:35 +0100 Subject: [PATCH 231/364] better input sanitizing in break manager --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 5fb204881..32349f4a0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -310,7 +310,10 @@ object BreakManager : RequestHandler( } } - return !blockState(ctx.expectedPos).isEmpty + val blockState = blockState(ctx.expectedPos) + val hardness = ctx.checkedState.getHardness(world, ctx.expectedPos) + + return !blockState.isEmpty && hardness != 600f && hardness != -1f } /** From 95fbfccaa909d1302af16a72a0466c2cb9e0fd54 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 02:13:23 +0100 Subject: [PATCH 232/364] removed unnecessary todos --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 1 - .../com/lambda/interaction/request/hotbar/HotbarManager.kt | 1 - .../com/lambda/interaction/request/placing/PlaceManager.kt | 1 - .../com/lambda/interaction/request/rotation/RotationManager.kt | 1 - 4 files changed, 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 32349f4a0..286c3d8e7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -66,7 +66,6 @@ object BreakManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - // ToDo: Post interact onOpen = { processRequest(activeRequest) } ), PositionBlocking { private var primaryBreak: BreakInfo? diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index d37742b81..b8d59dab1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -42,7 +42,6 @@ object HotbarManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - // ToDo: Post interact onClose = { checkResetSwap() } ) { val serverSlot get() = runSafe { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 55e7f58c8..599eec354 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -63,7 +63,6 @@ object PlaceManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - // ToDo: Post interact onOpen = { activeRequest?.let { processRequest(it) } } ), PositionBlocking { private var activeRequest: PlaceRequest? = null diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index e36a69b10..15e799cb4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -53,7 +53,6 @@ object RotationManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - // ToDo: Post interact ) { var activeRotation = Rotation.ZERO var serverRotation = Rotation.ZERO From 0c021c64248c3d86e3eca389fcdcea51558ba5fb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 05:44:08 +0100 Subject: [PATCH 233/364] added break renders, currently only static however, so render at 20 fps --- .../com/lambda/config/groups/BreakSettings.kt | 17 ++++++ .../request/breaking/BreakConfig.kt | 25 +++++++++ .../request/breaking/BreakManager.kt | 54 +++++++++++++++++++ .../module/modules/player/PacketMine.kt | 40 ++++++++++++++ 4 files changed, 136 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index b23b87eea..50ce2ca3f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -22,6 +22,7 @@ import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.breaking.BreakConfig import com.lambda.util.BlockUtils.allSigns +import java.awt.Color class BreakSettings( c: Configurable, @@ -52,4 +53,20 @@ class BreakSettings( override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis) override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis) override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe } + + override val renders by c.setting("Renders", true, "Enables the render settings for breaking progress", visibility = vis) + override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && renders } + override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && renders } + override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && renders && outline } + override val animation by c.setting("Animation", AnimationMode.Out, "The style of animation used for the box") { vis() && renders } + + override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && renders && fill } + override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60), "The color of the fill") { vis() && renders && !dynamicFillColor && fill } + override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60), "The color of the fill at the start of breaking") { vis() && renders && dynamicFillColor && fill } + override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60), "The color of the fill at the end of breaking") { vis() && renders && dynamicFillColor && fill } + + override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && renders && outline } + override val staticOutlineColor by c.setting("Outline Color", Color(255, 0, 0, 255), "The Color of the outline at the start of breaking") { vis() && renders && !dynamicOutlineColor && outline } + override val startOutlineColor by c.setting("Start Outline Color", Color(255, 0, 0, 255), "The color of the outline at the start of breaking") { vis() && renders && dynamicOutlineColor && outline } + override val endOutlineColor by c.setting("End Outline Color", Color(0, 255, 0, 255), "The color of the outline at the end of breaking") { vis() && renders && dynamicOutlineColor && outline } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index d41e7a637..6132b76ba 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -22,6 +22,7 @@ import com.lambda.event.Event import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig import net.minecraft.block.Block +import java.awt.Color abstract class BreakConfig( priority: Priority = 0 @@ -51,6 +52,22 @@ abstract class BreakConfig( abstract val minFortuneLevel: Int abstract val ignoredBlocks: Set + abstract val renders: Boolean + abstract val fill: Boolean + abstract val outline: Boolean + abstract val outlineWidth: Int + abstract val animation: AnimationMode + + abstract val dynamicFillColor: Boolean + abstract val staticFillColor: Color + abstract val startFillColor: Color + abstract val endFillColor: Color + + abstract val dynamicOutlineColor: Boolean + abstract val staticOutlineColor: Color + abstract val startOutlineColor: Color + abstract val endOutlineColor: Color + override fun requestInternal(request: BreakRequest, queueIfClosed: Boolean) { BreakManager.request(request, queueIfClosed) } @@ -75,4 +92,12 @@ abstract class BreakConfig( BreakThenAwait, AwaitThenBreak } + + enum class AnimationMode { + None, + Out, + In, + OutIn, + InOut, + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 286c3d8e7..d0426ed3a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -22,11 +22,14 @@ import com.lambda.event.Event import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent +import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe +import com.lambda.graphics.renderer.esp.builders.buildFilled +import com.lambda.graphics.renderer.esp.builders.buildOutline import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority @@ -51,6 +54,7 @@ import com.lambda.util.BlockUtils.isBroken import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block +import com.lambda.util.math.lerp import com.lambda.util.player.gamemode import com.lambda.util.player.swingHand import net.minecraft.client.sound.PositionedSoundInstance @@ -59,6 +63,7 @@ import net.minecraft.entity.ItemEntity import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box object BreakManager : RequestHandler( 0, @@ -169,6 +174,40 @@ object BreakManager : RequestHandler( ?.internalOnItemDrop(it.entity) } + listen { event -> + breakInfos + .filterNotNull() + .forEach { info -> + val config = info.breakConfig + if (!config.renders) return@listen + val breakDelta = info.context.checkedState.calcItemBlockBreakingDelta( + player, + world, + info.context.expectedPos, + player.inventory.getStack(info.context.hotbarIndex) + ) + val progress = (info.breakingTicks * breakDelta).let { + if (info.isPrimary) it * (2 - info.breakConfig.breakThreshold) + else it + }.toDouble() + val state = info.context.checkedState + val boxes = state.getOutlineShape(world, info.context.expectedPos).boundingBoxes.map { + it.offset(info.context.expectedPos) + } + + val fillColor = if (config.dynamicFillColor) lerp(progress, config.startFillColor, config.endFillColor) + else config.staticFillColor + val outlineColor = if (config.dynamicOutlineColor) lerp(progress, config.startOutlineColor, config.endOutlineColor) + else config.staticOutlineColor + + boxes.forEach boxes@ { box -> + val interpolated = interpolateBox(box, progress, info.breakConfig) + if (config.fill) event.renderer.buildFilled(interpolated, fillColor) + if (config.outline) event.renderer.buildOutline(interpolated, outlineColor) + } + } + } + listenUnsafe(priority = Int.MIN_VALUE) { breakInfos.forEach { it?.nullify() } breakCooldown = 0 @@ -689,5 +728,20 @@ object BreakManager : RequestHandler( return inRange && correctMaterial } + private fun interpolateBox(box: Box, progress: Double, config: BreakConfig): Box { + val boxCenter = Box(box.center, box.center) + return when (config.animation) { + BreakConfig.AnimationMode.Out -> lerp(progress, boxCenter, box) + BreakConfig.AnimationMode.In -> lerp(progress, box, boxCenter) + BreakConfig.AnimationMode.InOut -> + if (progress >= 0.5f) lerp((progress - 0.5) * 2, boxCenter, box) + else lerp(progress * 2, box, boxCenter) + BreakConfig.AnimationMode.OutIn -> + if (progress >= 0.5f) lerp((progress - 0.5) * 2, box, boxCenter) + else lerp(progress * 2, boxCenter, box) + else -> box + } + } + override fun preEvent(): Event = UpdateManagerEvent.Break.post() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 3c437592c..89d754305 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -24,8 +24,12 @@ import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent +import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.builders.buildFilled +import com.lambda.graphics.renderer.esp.builders.buildOutline +import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext @@ -37,8 +41,12 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.distSq +import com.lambda.util.math.lerp +import com.lambda.util.math.setAlpha import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import java.awt.Color import java.util.* import java.util.concurrent.ConcurrentLinkedQueue @@ -62,6 +70,14 @@ object PacketMine : Module( private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once") .onValueChange { _, to -> if (!to) queuePositions.clear() } private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue } + private val renderQueue by setting("Render Queue", true, "Adds renders to signify what block positions are queued") { queue } + private val renderSize by setting("Render Size", 0.3f, 0.01f..1f, 0.01f, "The scale of the queue renders") { queue && renderQueue } + private val renderMode by setting("Render Mode", RenderMode.State, "The style of the queue renders") { queue && renderQueue } + private val dynamicColor by setting("Dynamic Color", true, "Interpolates the color between start and end") { queue && renderQueue } + private val staticColor by setting("Color", Color(255, 0, 0, 60)) { queue && renderQueue && !dynamicColor } + private val startColor by setting("Start Color", Color(255, 255, 0, 60), "The color of the start (closest to breaking) of the queue") { queue && renderQueue && dynamicColor } + private val endColor by setting("End Color", Color(255, 0, 0, 60), "The color of the end (farthest from breaking) of the queue") { queue && renderQueue && dynamicColor } + private val pendingInteractionsList = ConcurrentLinkedQueue() @@ -125,6 +141,25 @@ object PacketMine : Module( } } + listen { event -> + if (!renderQueue) return@listen + queuePositions.forEachIndexed { index, positions -> + positions.forEach { pos -> + val color = if (dynamicColor) lerp(index / queuePositions.size.toDouble(), startColor, endColor) + else staticColor + val boxes = when (renderMode) { + RenderMode.State -> blockState(pos).getOutlineShape(world, pos).boundingBoxes + RenderMode.Box -> listOf(Box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)) + }.map { lerp(renderSize.toDouble(), Box(it.center, it.center), it).offset(pos) } + + boxes.forEach { box -> + event.renderer.buildFilled(box, color) + event.renderer.buildOutline(box, Color(color.rgb).setAlpha(1.0)) + } + } + } + } + onDisable { breakPositions[0] = null breakPositions[1] = null @@ -222,4 +257,9 @@ object PacketMine : Module( Standard, Reversed } + + private enum class RenderMode { + State, + Box + } } \ No newline at end of file From a6737f53e2074fbcc2f1bfbd5eb57d6c64a58f89 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 05:49:03 +0100 Subject: [PATCH 234/364] more specific break timeout warnings --- .../lambda/interaction/request/breaking/BrokenBlockHandler.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index c6ed1dd41..51134aa12 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -57,7 +57,8 @@ object BrokenBlockHandler { val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)) if (!loaded) return@let - info("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") + if (!info.broken) info("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") + else info("${info::class.simpleName}'s item drop at ${info.context.expectedPos.toShortString()} timed out") val awaitThenBreak = info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak if (!info.broken && awaitThenBreak) { From a49114588601482d0bc69cce2f6f7f0ac99ffefb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 8 Jun 2025 16:49:35 +0100 Subject: [PATCH 235/364] build outlines for queued blocks --- .../lambda/interaction/request/breaking/BrokenBlockHandler.kt | 1 - .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 51134aa12..056cb0923 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -37,7 +37,6 @@ import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.player.gamemode -import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock import net.minecraft.entity.ItemEntity import net.minecraft.util.math.ChunkSectionPos diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 89d754305..e4f0434f5 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -29,7 +29,6 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.builders.buildFilled import com.lambda.graphics.renderer.esp.builders.buildOutline -import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext @@ -154,7 +153,7 @@ object PacketMine : Module( boxes.forEach { box -> event.renderer.buildFilled(box, color) - event.renderer.buildOutline(box, Color(color.rgb).setAlpha(1.0)) + event.renderer.buildOutline(box, color.setAlpha(1.0)) } } } From f3b4776233c386c433198d84c9ae1c4edb76ab00 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 9 Jun 2025 00:21:52 +0100 Subject: [PATCH 236/364] break request dsl builder --- .../request/breaking/BreakRequest.kt | 75 +++++++++++++++++-- .../request/breaking/BrokenBlockHandler.kt | 4 +- .../module/modules/player/PacketMine.kt | 21 +++--- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 14 ++-- 4 files changed, 87 insertions(+), 27 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index be3c20932..663325311 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -29,20 +29,81 @@ import com.lambda.util.BlockUtils.blockState import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos +@DslMarker +annotation class BreakRequestBuilder + data class BreakRequest( val contexts: Collection, val build: BuildConfig, val rotation: RotationConfig, val hotbar: HotbarConfig, val pendingInteractions: MutableCollection, - val onStart: ((BlockPos) -> Unit)? = null, - val onStop: ((BlockPos) -> Unit)? = null, - val onCancel: ((BlockPos) -> Unit)? = null, - val onItemDrop: ((ItemEntity) -> Unit)? = null, - val onReBreakStart: ((BlockPos) -> Unit)? = null, - val onReBreak: ((BlockPos) -> Unit)? = null, private val prio: Priority = 0 ) : Request(prio, build.breaking) { + var onStart: ((BlockPos) -> Unit)? = null + var onStop: ((BlockPos) -> Unit)? = null + var onCancel: ((BlockPos) -> Unit)? = null + var onItemDrop: ((ItemEntity) -> Unit)? = null + var onReBreakStart: ((BlockPos) -> Unit)? = null + var onReBreak: ((BlockPos) -> Unit)? = null + override val done: Boolean get() = runSafe { contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true -} + + @BreakRequestBuilder + class RequestBuilder( + contexts: Collection, + build: BuildConfig, + rotation: RotationConfig, + hotbar: HotbarConfig, + pendingInteractions: MutableCollection, + prio: Priority = 0 + ) { + val request = BreakRequest(contexts, build, rotation, hotbar, pendingInteractions, prio) + + @BreakRequestBuilder + fun onStart(callback: (BlockPos) -> Unit) { + request.onStart = callback + } + + @BreakRequestBuilder + fun onStop(callback: (BlockPos) -> Unit) { + request.onStop = callback + } + + @BreakRequestBuilder + fun onCancel(callback: (BlockPos) -> Unit) { + request.onCancel = callback + } + + @BreakRequestBuilder + fun onItemDrop(callback: (ItemEntity) -> Unit) { + request.onItemDrop = callback + } + + @BreakRequestBuilder + fun onReBreakStart(callback: (BlockPos) -> Unit) { + request.onReBreakStart = callback + } + + @BreakRequestBuilder + fun onReBreak(callback: (BlockPos) -> Unit) { + request.onReBreak = callback + } + + @BreakRequestBuilder + fun build(): BreakRequest = request + } + + companion object { + fun breakRequest( + contexts: Collection, + build: BuildConfig, + rotation: RotationConfig, + hotbar: HotbarConfig, + pendingInteractions: MutableCollection, + prio: Priority = 0, + builder: RequestBuilder.() -> Unit + ) = RequestBuilder(contexts, build, rotation, hotbar, pendingInteractions, prio).apply(builder).build() + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 056cb0923..184be5e44 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -56,8 +56,8 @@ object BrokenBlockHandler { val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)) if (!loaded) return@let - if (!info.broken) info("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") - else info("${info::class.simpleName}'s item drop at ${info.context.expectedPos.toShortString()} timed out") + if (!info.broken) warn("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") + else warn("${info::class.simpleName}'s item drop at ${info.context.expectedPos.toShortString()} timed out") val awaitThenBreak = info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak if (!info.broken && awaitThenBreak) { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index e4f0434f5..3bbf228ae 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -35,7 +35,7 @@ import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.breaking.BreakRequest +import com.lambda.interaction.request.breaking.BreakRequest.Companion.breakRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState @@ -78,7 +78,7 @@ object PacketMine : Module( private val endColor by setting("End Color", Color(255, 0, 0, 60), "The color of the end (farthest from breaking) of the queue") { queue && renderQueue && dynamicColor } - private val pendingInteractionsList = ConcurrentLinkedQueue() + private val pendingInteractions = ConcurrentLinkedQueue() private var breaks = 0 private var itemDrops = 0 @@ -174,15 +174,14 @@ object PacketMine : Module( if (!reBreaking) { queuePositions.retainAllPositions(breakContexts) } - val request = BreakRequest( - breakContexts, build, rotation, hotbar, pendingInteractions = pendingInteractionsList, - onStart = { queuePositions.removePos(it); addBreak(it) }, - onStop = { removeBreak(it); breaks++ }, - onCancel = { removeBreak(it, true) }, - onReBreakStart = { reBreakPos = it }, - onReBreak = { reBreakPos = it }, - onItemDrop = { _ -> itemDrops++ } - ) + val request = breakRequest(breakContexts, build, rotation, hotbar, pendingInteractions) { + onStart { queuePositions.removePos(it); addBreak(it) } + onStop { removeBreak(it); breaks++ } + onCancel { removeBreak(it, true) } + onReBreakStart { reBreakPos = it } + onReBreak { reBreakPos = it } + onItemDrop { _ -> itemDrops++ } + } breakConfig.request(request, true) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 80a72cc27..02bff4d08 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -43,7 +43,7 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer -import com.lambda.interaction.request.breaking.BreakRequest +import com.lambda.interaction.request.breaking.BreakRequest.Companion.breakRequest import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotation.RotationConfig @@ -154,12 +154,12 @@ class BuildTask @Ta5kBuilder constructor( requestContexts.addAll(breakResults.map { it.context }) } - val request = BreakRequest( - requestContexts, build, rotation, hotbar, - pendingInteractions = pendingInteractions, - onStop = { breaks++ }, - onItemDrop = onItemDrop - ) + val request = breakRequest(requestContexts, build, rotation, hotbar, pendingInteractions) { + onStop { breaks++ } + onItemDrop?.let { onItemDrop -> + onItemDrop { onItemDrop(it) } + } + } build.breaking.request(request) return@listen } From 5fd5a058b960649d5ab05e9d1fb8db98bf3643aa Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 9 Jun 2025 01:08:01 +0100 Subject: [PATCH 237/364] arraylist over linkedlist to avoid excessive collection copying --- .../lambda/module/modules/player/PacketMine.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 3bbf228ae..627883d03 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -48,6 +48,7 @@ import net.minecraft.util.math.Box import java.awt.Color import java.util.* import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.collections.ArrayList object PacketMine : Module( "PacketMine", @@ -84,11 +85,11 @@ object PacketMine : Module( private var itemDrops = 0 private val breakPositions = arrayOfNulls(2) - private val queuePositions = LinkedList>() + private val queuePositions = ArrayList>() private val queueSorted get() = when (queueOrder) { QueueOrder.Standard -> queuePositions - QueueOrder.Reversed -> queuePositions.reversed() + QueueOrder.Reversed -> queuePositions.asReversed() }.flatten() private var reBreakPos: BlockPos? = null @@ -118,17 +119,18 @@ object PacketMine : Module( } if (positions.isEmpty()) return@listen val activeBreaking = if (queue) { - queuePositions.addLast(positions) + queuePositions.add(positions) breakPositions.toList() + queueSorted } else { queuePositions.clear() - queuePositions.addLast(positions) + queuePositions.add(positions) queuePositions.flatten() + if (breakConfig.doubleBreak) { breakPositions[1] ?: breakPositions[0] } else null } requestBreakManager(activeBreaking) attackedThisTick = true + queuePositions.trimToSize() } listen { @@ -213,7 +215,7 @@ object PacketMine : Module( } } - private fun LinkedList>.removePos(element: BlockPos): Boolean { + private fun ArrayList>.removePos(element: BlockPos): Boolean { var anyRemoved = false removeIf { val removed = it.remove(element) @@ -223,7 +225,7 @@ object PacketMine : Module( return anyRemoved } - private fun LinkedList>.retainAllPositions(positions: Collection): Boolean { + private fun ArrayList>.retainAllPositions(positions: Collection): Boolean { var modified = false forEach { modified = modified or it.retainAll { pos -> @@ -235,7 +237,7 @@ object PacketMine : Module( return modified } - private fun LinkedList>.any(predicate: (BlockPos) -> Boolean): Boolean { + private fun ArrayList>.any(predicate: (BlockPos) -> Boolean): Boolean { if (isEmpty()) return false forEach { if (it.any(predicate)) return true } return false From 341acb73db862d2004c7c218ef0b6d4e4afe4e75 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 9 Jun 2025 14:55:14 +0100 Subject: [PATCH 238/364] fixed issues with break radius / queue. Added onUpdate callback for break requests --- .../interaction/request/breaking/BreakManager.kt | 5 ++++- .../interaction/request/breaking/BreakRequest.kt | 7 +++++++ .../com/lambda/module/modules/player/PacketMine.kt | 14 ++++++++------ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index d0426ed3a..27b955625 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -315,7 +315,10 @@ object BreakManager : RequestHandler( .filterNotNull() .forEach { info -> newBreaks.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> - if (!info.updatedThisTick) info.updateInfo(ctx, request) + if (!info.updatedThisTick) { + info.updateInfo(ctx, request) + info.request.onUpdate?.invoke(info.context.expectedPos) + } newBreaks.remove(ctx) return@forEach } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 663325311..e963b2120 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -41,6 +41,7 @@ data class BreakRequest( private val prio: Priority = 0 ) : Request(prio, build.breaking) { var onStart: ((BlockPos) -> Unit)? = null + var onUpdate: ((BlockPos) -> Unit)? = null var onStop: ((BlockPos) -> Unit)? = null var onCancel: ((BlockPos) -> Unit)? = null var onItemDrop: ((ItemEntity) -> Unit)? = null @@ -66,6 +67,11 @@ data class BreakRequest( request.onStart = callback } + @BreakRequestBuilder + fun onUpdate(callback: (BlockPos) -> Unit) { + request.onUpdate = callback + } + @BreakRequestBuilder fun onStop(callback: (BlockPos) -> Unit) { request.onStop = callback @@ -96,6 +102,7 @@ data class BreakRequest( } companion object { + @BreakRequestBuilder fun breakRequest( contexts: Collection, build: BuildConfig, diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 627883d03..617f5925e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -46,7 +46,6 @@ import com.lambda.util.world.raycast.InteractionMask import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import java.awt.Color -import java.util.* import java.util.concurrent.ConcurrentLinkedQueue import kotlin.collections.ArrayList @@ -105,17 +104,19 @@ object PacketMine : Module( listen { event -> event.cancel() val pos = event.pos - val positions = mutableListOf(pos).apply { - if (breakRadius <= 0) return@apply + val positions = mutableListOf().apply { + if (breakRadius <= 0) { + add(pos) + return@apply + } BlockPos.iterateOutwards(pos, breakRadius, breakRadius, breakRadius).forEach { blockPos -> - if (blockPos distSq pos <= (breakRadius * breakRadius) && (!flatten || blockPos.y >= player.blockPos.y)) { + if (blockPos distSq pos <= (breakRadius * breakRadius) && (!flatten || (blockPos.y >= player.blockPos.y || blockPos == pos))) { add(blockPos.toImmutable()) } } } positions.removeIf { breakPos -> - breakPositions.any { it == breakPos } - || (queue && queuePositions.any { it == pos }) + queue && queuePositions.any { it == breakPos } } if (positions.isEmpty()) return@listen val activeBreaking = if (queue) { @@ -178,6 +179,7 @@ object PacketMine : Module( } val request = breakRequest(breakContexts, build, rotation, hotbar, pendingInteractions) { onStart { queuePositions.removePos(it); addBreak(it) } + onUpdate { queuePositions.removePos(it) } onStop { removeBreak(it); breaks++ } onCancel { removeBreak(it, true) } onReBreakStart { reBreakPos = it } From 295d0f066db69ae85be29ab1f4924e1c7bec074f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 16 Jun 2025 01:34:46 +0100 Subject: [PATCH 239/364] added desyncFix setting, although its functionality is broken until player simulation is progressed --- .../com/lambda/config/groups/BreakSettings.kt | 1 + .../request/breaking/BreakConfig.kt | 1 + .../request/breaking/BreakManager.kt | 45 ++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 50ce2ca3f..5013da63d 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -35,6 +35,7 @@ class BreakSettings( override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis) override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis) override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis) + override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position", visibility = vis) override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis) override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", visibility = vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 6132b76ba..6461ca975 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -33,6 +33,7 @@ abstract class BreakConfig( abstract val breakThreshold: Float abstract val doubleBreak: Boolean abstract val fudgeFactor: Int + abstract val desyncFix: Boolean abstract val breakDelay: Int abstract val breakStageMask: Set abstract val swing: SwingMode diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 27b955625..9a8b6aab9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -56,14 +56,21 @@ import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block import com.lambda.util.math.lerp import com.lambda.util.player.gamemode +import com.lambda.util.player.prediction.buildPlayerPrediction import com.lambda.util.player.swingHand +import net.minecraft.block.BlockState +import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance +import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.entity.ItemEntity +import net.minecraft.item.ItemStack +import net.minecraft.registry.tag.FluidTags import net.minecraft.sound.SoundCategory import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box +import net.minecraft.world.BlockView object BreakManager : RequestHandler( 0, @@ -180,10 +187,11 @@ object BreakManager : RequestHandler( .forEach { info -> val config = info.breakConfig if (!config.renders) return@listen - val breakDelta = info.context.checkedState.calcItemBlockBreakingDelta( + val breakDelta = info.context.checkedState.calcBreakDelta( player, world, info.context.expectedPos, + info.breakConfig, player.inventory.getStack(info.context.hotbarIndex) ) val progress = (info.breakingTicks * breakDelta).let { @@ -268,10 +276,11 @@ object BreakManager : RequestHandler( .forEach { info -> if (info.updatedProgressThisTick) return@forEach val minKeepTicks = if (info.isSecondary) { - val breakDelta = info.context.checkedState.calcItemBlockBreakingDelta( + val breakDelta = info.context.checkedState.calcBreakDelta( player, world, info.context.expectedPos, + info.breakConfig, player.inventory.getStack(info.context.hotbarIndex) ) val breakAmount = breakDelta * (info.breakingTicks + 1) @@ -605,10 +614,11 @@ object BreakManager : RequestHandler( } info.breakingTicks++ - val progress = blockState.calcBlockBreakingDelta( + val progress = blockState.calcBreakDelta( player, world, - ctx.expectedPos + ctx.expectedPos, + config ) * (info.breakingTicks - config.fudgeFactor) val overBreakThreshold = progress >= info.getBreakThreshold() @@ -694,7 +704,7 @@ object BreakManager : RequestHandler( blockState.onBlockBreakStart(world, ctx.expectedPos, player) } - val breakDelta = blockState.calcBlockBreakingDelta(player, world, ctx.expectedPos) + val breakDelta = blockState.calcBreakDelta(player, world, ctx.expectedPos, info.breakConfig) if (notEmpty && breakDelta >= info.getBreakThreshold()) { onBlockBreak(info) } else { @@ -722,6 +732,31 @@ object BreakManager : RequestHandler( return true } + private fun BlockState.calcBreakDelta( + player: ClientPlayerEntity, + world: BlockView, + pos: BlockPos, + config: BreakConfig, + item: ItemStack? = null + ) = runSafe { + var delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.mainHandStack) + // This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, its broken + if (config.desyncFix) { + val nextTickPrediction = buildPlayerPrediction().next() + if (player.isOnGround && !nextTickPrediction.onGround) { + delta /= 5.0f + } + + val affectedThisTick = player.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(player) + val simulatedPlayer = nextTickPrediction.predictionEntity.player + val affectedNextTick = simulatedPlayer.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(simulatedPlayer) + if (!affectedThisTick && affectedNextTick) { + delta /= 5.0f + } + } + delta + } ?: 0f + /** * @return if the [ItemEntity] matches the [BreakInfo]'s expected item drop. */ From bb4f6cbe5ea891a1cbb56e8798c6ae97f52a853e Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 16 Jun 2025 18:50:57 +0100 Subject: [PATCH 240/364] baritone selection mode in nuker because i needed it --- .../kotlin/com/lambda/module/modules/player/Nuker.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 5674465a0..a8cc5765f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -24,6 +24,7 @@ import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.build +import com.lambda.util.BaritoneUtils import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState import net.minecraft.util.math.BlockPos @@ -38,6 +39,7 @@ object Nuker : Module( private val flatten by setting("Flatten", true) private val onlyBreakInstant by setting("Only Break Instant", true) private val fillFloor by setting("Fill Floor", false) + private val baritoneSelection by setting("Baritone Selection", false, "Restricts nuker to your baritone selection") private var task: Task<*>? = null @@ -51,6 +53,16 @@ object Nuker : Module( .filter { !flatten || it.y >= player.blockPos.y } .filter { !onlyBreakInstant || blockState(it).getHardness(world, it) <= 1 } .filter { blockState(it).getHardness(world, it) >= 0 } + .filter { pos -> + if (!baritoneSelection) true + else BaritoneUtils.primary.selectionManager.selections.any { + val min = it.min() + val max = it.max() + pos.x >= min.x && pos.x <= max.x + && pos.y >= min.y && pos.y <= max.y + && pos.z >= min.z && pos.z <= max.z + } + } .associateWith { TargetState.Air } if (fillFloor) { From 2c65434cf3617ccfb0fbe53f1eb478855674ff8f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 26 Jun 2025 18:02:50 +0100 Subject: [PATCH 241/364] added dsl for pre-processing info and expanded the pre-processors; removed FacingProcessor.kt as I feel it cant be used to accurately refine the possible placement sides --- .../processing/PlacementProcessor.kt | 4 +- .../processing/PreprocessingInfo.kt | 67 +++++++++++++++++++ .../processing/PreprocessingStep.kt | 28 -------- .../processing/ProcessorRegistry.kt | 21 +++--- .../processors/AttachmentPreprocessor.kt | 43 ++++++++++++ .../processing/processors/AxisPreprocessor.kt | 17 ++--- .../processors/BlockFaceProcessor.kt | 22 +++--- .../processors/BlockHalfProcessor.kt | 45 +++++++++++++ ...ocessor.kt => HopperFacingPreprocessor.kt} | 24 ++++--- .../processing/processors/SlabProcessor.kt | 24 +++---- .../construction/simulation/BuildSimulator.kt | 57 ++++------------ .../construction/verify/ScanMode.kt | 6 +- .../construction/verify/SurfaceScan.kt | 1 - .../lambda/module/modules/debug/StateInfo.kt | 48 +++++++++++++ 14 files changed, 278 insertions(+), 129 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingStep.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt rename common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/{FacingProcessor.kt => HopperFacingPreprocessor.kt} (57%) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt index 31fde9586..0718cf964 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt @@ -20,7 +20,7 @@ package com.lambda.interaction.construction.processing import net.minecraft.block.BlockState abstract class PlacementProcessor { - abstract fun acceptState(state: BlockState): Boolean - abstract fun preProcess(state: BlockState): PreprocessingStep + abstract fun acceptsState(state: BlockState): Boolean + abstract fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt new file mode 100644 index 000000000..f4edffe13 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2024 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.construction.processing + +import com.lambda.interaction.construction.verify.SurfaceScan +import net.minecraft.state.property.Property +import net.minecraft.util.math.Direction + +@DslMarker +private annotation class InfoAccumulator + +@InfoAccumulator +data class PreprocessingInfoAccumulator( + private var surfaceScan: SurfaceScan = SurfaceScan.DEFAULT, + private val ignore: MutableSet> = mutableSetOf(), + private val sides: MutableSet = Direction.entries.toMutableSet(), +) { + @InfoAccumulator + fun offerSurfaceScan(scan: SurfaceScan) { + if (scan.mode.priority > surfaceScan.mode.priority) { + surfaceScan = scan + } + } + + @InfoAccumulator + fun addIgnores(ignores: Set>) { + ignore.addAll(ignores) + } + + @InfoAccumulator + fun retainSides(predicate: (Direction) -> Boolean) { + this.sides.retainAll(predicate) + } + + @InfoAccumulator + fun retainSides(vararg sides: Direction) { + this.sides.retainAll(sides.toSet()) + } + + @InfoAccumulator + fun complete() = PreprocessingInfo(surfaceScan, ignore, sides) +} + +data class PreprocessingInfo( + val surfaceScan: SurfaceScan, + val ignore: Set>, + val sides: Set +) { + companion object { + val DEFAULT = PreprocessingInfo(SurfaceScan.DEFAULT, emptySet(), emptySet()) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingStep.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingStep.kt deleted file mode 100644 index bd1e566c0..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingStep.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program 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. - * - * This program 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 this program. If not, see . - */ - -package com.lambda.interaction.construction.processing - -import com.lambda.interaction.construction.verify.SurfaceScan -import net.minecraft.state.property.Property -import net.minecraft.util.math.Direction - -data class PreprocessingStep( - val surfaceScan: SurfaceScan = SurfaceScan.DEFAULT, - val ignore: Set> = emptySet(), - val sides: Set = Direction.entries.toSet(), -) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt index 085cb86c2..5e9566b4d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt @@ -21,23 +21,24 @@ import com.lambda.core.Loadable import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.reflections.getInstances import net.minecraft.block.BlockState -import net.minecraft.block.Blocks object ProcessorRegistry : Loadable { private val processors = getInstances() - private val processorCache = mutableMapOf() + private val processorCache = mutableMapOf() override fun load() = "Loaded ${processors.size} pre processors" - fun TargetState.findProcessorForState(): PreprocessingStep = + fun TargetState.getProcessingInfo(): PreprocessingInfo = (this as? TargetState.State)?.let { state -> processorCache.getOrPut(state.blockState) { - (processors.find { it.acceptState(state.blockState) } ?: DefaultProcessor).preProcess(state.blockState) - } - } ?: DefaultProcessor.preProcess(Blocks.AIR.defaultState) + val infoAccumulator = PreprocessingInfoAccumulator() + + processors.forEach { + if (!it.acceptsState(state.blockState)) return@forEach + it.preProcess(state.blockState, infoAccumulator) + } - object DefaultProcessor : PlacementProcessor() { - override fun acceptState(state: BlockState) = true - override fun preProcess(state: BlockState) = PreprocessingStep() - } + return infoAccumulator.complete() + } + } ?: PreprocessingInfo.DEFAULT } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt new file mode 100644 index 000000000..cd0df53e3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.construction.processing.processors + +import com.lambda.interaction.construction.processing.PlacementProcessor +import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import net.minecraft.block.BlockState +import net.minecraft.block.enums.Attachment +import net.minecraft.state.property.Properties +import net.minecraft.util.math.Direction + +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") +object AttachmentPreprocessor : PlacementProcessor() { + override fun acceptsState(state: BlockState) = + state.properties.contains(Properties.ATTACHMENT) + + override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + val attachment = state.get(Properties.ATTACHMENT) ?: return + with (accumulator) { + when (attachment) { + Attachment.FLOOR -> retainSides(Direction.DOWN) + Attachment.CEILING -> retainSides(Direction.UP) + else -> retainSides { Direction.Type.HORIZONTAL.contains(it) } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt index 0ff14d216..754aa960e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt @@ -18,19 +18,20 @@ package com.lambda.interaction.construction.processing.processors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingStep +import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.state.property.Properties -import net.minecraft.util.math.Direction +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") object AxisPreprocessor : PlacementProcessor() { - override fun acceptState(state: BlockState) = + override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.AXIS).isPresent - override fun preProcess(state: BlockState): PreprocessingStep { - val axis = state.getOrEmpty(Properties.AXIS).get() - return PreprocessingStep( - sides = Direction.entries.filter { it.axis == axis }.toSet() - ) + override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + val axis = state.get(Properties.AXIS) + accumulator.retainSides { side -> + side.axis == axis + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt index 359709d5e..0b9f354d6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt @@ -18,24 +18,26 @@ package com.lambda.interaction.construction.processing.processors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingStep +import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.block.enums.BlockFace import net.minecraft.state.property.Properties import net.minecraft.util.math.Direction -import kotlin.jvm.optionals.getOrNull +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") object BlockFaceProcessor : PlacementProcessor() { - override fun acceptState(state: BlockState) = + override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.BLOCK_FACE).isPresent - override fun preProcess(state: BlockState): PreprocessingStep { - val property = state.getOrEmpty(Properties.BLOCK_FACE).getOrNull() ?: return PreprocessingStep() - val sides = when (property) { - BlockFace.FLOOR -> setOf(Direction.DOWN) - BlockFace.CEILING -> setOf(Direction.UP) - BlockFace.WALL -> Direction.Type.HORIZONTAL.stream().toList().toSet() + override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + val property = state.get(Properties.BLOCK_FACE) ?: return + with (accumulator) { + when (property) { + BlockFace.FLOOR -> retainSides(Direction.DOWN) + BlockFace.CEILING -> retainSides(Direction.UP) + BlockFace.WALL -> retainSides { Direction.Type.HORIZONTAL.contains(it) } + } } - return PreprocessingStep(sides = sides) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt new file mode 100644 index 000000000..ba72e3698 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.construction.processing.processors + +import com.lambda.interaction.construction.processing.PlacementProcessor +import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.verify.ScanMode +import com.lambda.interaction.construction.verify.SurfaceScan +import net.minecraft.block.BlockState +import net.minecraft.block.enums.BlockHalf +import net.minecraft.state.property.Properties +import net.minecraft.util.math.Direction + +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") +object BlockHalfProcessor : PlacementProcessor() { + override fun acceptsState(state: BlockState) = + state.getOrEmpty(Properties.BLOCK_HALF).isPresent + + override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + val slab = state.get(Properties.BLOCK_HALF) ?: return + + val surfaceScan = when (slab) { + BlockHalf.BOTTOM -> SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Y) + BlockHalf.TOP -> SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Y) + } + + accumulator.offerSurfaceScan(surfaceScan) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/FacingProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/HopperFacingPreprocessor.kt similarity index 57% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/FacingProcessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/HopperFacingPreprocessor.kt index 62e791d4f..bf3318aa3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/FacingProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/HopperFacingPreprocessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,18 +18,22 @@ package com.lambda.interaction.construction.processing.processors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingStep +import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.state.property.Properties +import net.minecraft.util.math.Direction -object FacingProcessor : PlacementProcessor() { - override fun acceptState(state: BlockState) = - state.getOrEmpty(Properties.FACING).isPresent +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") +object HopperFacingPreprocessor : PlacementProcessor() { + override fun acceptsState(state: BlockState) = + state.properties.contains(Properties.HOPPER_FACING) - override fun preProcess(state: BlockState): PreprocessingStep { - // Needs two sets of blocks: native and opposite! - return PreprocessingStep( - sides = setOf(state.getOrEmpty(Properties.FACING).get()) - ) + override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + val facing = state.get(Properties.HOPPER_FACING) ?: return + when { + facing.axis == Direction.Axis.Y -> accumulator.retainSides { it.axis == Direction.Axis.Y } + else -> accumulator.retainSides(facing) + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt index ac5794690..2a280359e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt @@ -18,7 +18,7 @@ package com.lambda.interaction.construction.processing.processors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingStep +import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan import net.minecraft.block.BlockState @@ -27,24 +27,20 @@ import net.minecraft.block.enums.SlabType import net.minecraft.state.property.Properties import net.minecraft.util.math.Direction +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") object SlabProcessor : PlacementProcessor() { - override fun acceptState(state: BlockState) = state.block is SlabBlock + override fun acceptsState(state: BlockState) = state.block is SlabBlock - override fun preProcess(state: BlockState): PreprocessingStep { - val slab = state.getOrEmpty(Properties.SLAB_TYPE).get() + override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + val slab = state.get(Properties.SLAB_TYPE) ?: return val surfaceScan = when (slab) { - SlabType.BOTTOM -> SurfaceScan( - ScanMode.LESSER_HALF, Direction.Axis.Y - ) - SlabType.TOP -> SurfaceScan( - ScanMode.GREATER_HALF, Direction.Axis.Y - ) - SlabType.DOUBLE -> SurfaceScan( - ScanMode.FULL, Direction.Axis.Y - ) + SlabType.BOTTOM -> SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Y) + SlabType.TOP -> SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Y) + SlabType.DOUBLE -> SurfaceScan(ScanMode.FULL, Direction.Axis.Y) } - return PreprocessingStep(surfaceScan) + accumulator.offerSurfaceScan(surfaceScan) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index ca1ec73c4..8ea414f8e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -24,7 +24,7 @@ import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.PlaceContext -import com.lambda.interaction.construction.processing.ProcessorRegistry.findProcessorForState +import com.lambda.interaction.construction.processing.ProcessorRegistry.getProcessingInfo import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult @@ -164,10 +164,10 @@ object BuildSimulator { if (target.isAir() || !targetPosState.isReplaceable) return acc - val preprocessing = target.findProcessorForState() + val preprocessing = target.getProcessingInfo() preprocessing.sides.forEach { neighbor -> - val hitPos = if (!place.airPlace.isEnabled() && targetPosState.isAir || targetPosState.isLiquid) + val hitPos = if (!place.airPlace.isEnabled() && (targetPosState.isAir || targetPosState.isLiquid)) pos.offset(neighbor) else pos val hitSide = neighbor.opposite @@ -415,7 +415,7 @@ object BuildSimulator { val state = blockState(pos) /* is a block that will be destroyed by breaking adjacent blocks */ - if (build.breaking.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (breaking.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { acc.add(BuildResult.Ignored(pos)) return acc } @@ -444,13 +444,13 @@ object BuildSimulator { }.map { pos.offset(it) } /* block has liquids next to it that will leak when broken */ - if (adjacentLiquids.isNotEmpty() && build.breaking.avoidLiquids) { + if (adjacentLiquids.isNotEmpty() && breaking.avoidLiquids) { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { checkPlaceResults(liquidPos, TargetState.Solid, eye, build.placing, interact, rotation, inventory) } else { - checkBreakResults(liquidPos, eye, build.breaking, interact, rotation, inventory, build) + checkBreakResults(liquidPos, eye, breaking, interact, rotation, inventory, build) } acc.addAll(submerge) } @@ -488,7 +488,7 @@ object BuildSimulator { state, targetState, player.inventory.selectedSlot, - instantBreakable(state, pos, build.breaking.breakThreshold) + instantBreakable(state, pos, breaking.breakThreshold) ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -533,7 +533,7 @@ object BuildSimulator { val blockHit = bestHit.hit.blockResult ?: return acc val target = lookAt(bestHit.targetRotation, 0.001) val request = RotationRequest(target, rotation) - val instant = instantBreakable(state, pos, build.breaking.breakThreshold) + val instant = instantBreakable(state, pos, breaking.breakThreshold) val breakContext = BreakContext( eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant @@ -544,46 +544,15 @@ object BuildSimulator { return acc } -// val bestTools = findBestToolForBreaking(state, inventory.allowedTools) -// -// /* there is no good tool for the job */ -// if (bestTools.isEmpty()) { -// /* The current selected item cant mine the block */ -// Hand.entries.forEach { -// val stack = player.getStackInHand(it) -// if (stack.isEmpty) return@forEach -// if (stack.item.canMine(state, world, pos, player)) return@forEach -// acc.add(BreakResult.ItemCantMine(pos, state, stack.item, inventory)) -// return acc -// } -// // ToDo: Switch to non destroyable item -// acc.add(BreakResult.Break(pos, breakContext)) -// return acc -// } -// -// val toolSelection = if (build.breaking.forceSilkTouch) { -// selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.SILK_TOUCH) } -// } else if (build.breaking.forceFortunePickaxe) { -// selectStack { isOneOfItems(bestTools) and hasEnchantment(Enchantments.FORTUNE, build.breaking.minFortuneLevel) } -// } else { -// bestTools.select() -// } -// val silentSwapSelection = selectContainer { -// matches(toolSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) -// } -// val fullSelection = selectContainer { -// matches(toolSelection) and matches(inventory.containerSelection) -// } - val stackSelection = selectStack( block = { run { - if (build.breaking.suitableToolsOnly) isSuitableForBreaking(state) + if (breaking.suitableToolsOnly) isSuitableForBreaking(state) else StackSelection.EVERYTHING - } and if (build.breaking.forceSilkTouch) { + } and if (breaking.forceSilkTouch) { hasEnchantment(Enchantments.SILK_TOUCH) - } else if (build.breaking.forceFortunePickaxe) { - hasEnchantment(Enchantments.FORTUNE, build.breaking.minFortuneLevel) + } else if (breaking.forceFortunePickaxe) { + hasEnchantment(Enchantments.FORTUNE, breaking.minFortuneLevel) } else StackSelection.EVERYTHING }, sorter = compareByDescending { @@ -613,7 +582,7 @@ object BuildSimulator { if (toolPair == null) return acc breakContext.hotbarIndex = player.hotbar.indexOf(toolPair.first) - breakContext.instantBreak = instantBreakable(state, pos, toolPair.first, build.breaking.breakThreshold) + breakContext.instantBreak = instantBreakable(state, pos, toolPair.first, breaking.breakThreshold) acc.add(BreakResult.Break(pos, breakContext)) return acc } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt index 44b190fdc..6bcaf31d4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt @@ -17,6 +17,8 @@ package com.lambda.interaction.construction.verify -enum class ScanMode { - GREATER_HALF, LESSER_HALF, FULL +enum class ScanMode(val priority: Int) { + GREATER_HALF(1), + LESSER_HALF(1), + FULL(0) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/SurfaceScan.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/SurfaceScan.kt index 78586b7f7..2b6842fba 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/SurfaceScan.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/SurfaceScan.kt @@ -26,5 +26,4 @@ data class SurfaceScan( companion object { val DEFAULT = SurfaceScan(ScanMode.FULL, Direction.Axis.Y) } - } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt new file mode 100644 index 000000000..960376b5e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.module.modules.debug + +import com.lambda.event.events.KeyboardEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.Communication.info +import com.lambda.util.KeyCode +import net.minecraft.util.hit.BlockHitResult + +object StateInfo : Module( + "State Info", + "Prints the target block's state into chat", + setOf(ModuleTag.DEBUG) +) { + private val printBind by setting("Print", KeyCode.UNBOUND, "The bind used to print the info to chat") + + init { + listen { event -> + if (!event.isPressed) return@listen + if (event.keyCode != printBind.keyCode) return@listen + val crosshair = mc.crosshairTarget ?: return@listen + if (crosshair !is BlockHitResult) return@listen + + val targetBlock = blockState(crosshair.blockPos) + val text = "$targetBlock" + info(text) + } + } +} \ No newline at end of file From 3feb026566cc6303a1dfa42c0c4c271a30904e76 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 27 Jun 2025 22:39:10 +0100 Subject: [PATCH 242/364] simple module for printing every property coupled with every state implementing said property in the game to a .txt file --- .../module/modules/debug/PropertyPrinter.kt | 52 +++++++++++++++++++ .../lambda/module/modules/debug/StateInfo.kt | 35 +++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt new file mode 100644 index 000000000..f472a02b5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.module.modules.debug + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.FolderRegister +import com.lambda.util.extension.resolveFile +import net.minecraft.block.Block +import net.minecraft.block.Blocks + +object PropertyPrinter : Module( + "PropertyPrinter", + "Prints all properties coupled with all the states that use them into a text file", + setOf(ModuleTag.DEBUG) +) { + init { + onEnable { + val file = FolderRegister.lambda.resolve("property-print").resolveFile("property-print.txt") + file.parentFile.mkdirs() + file.writeText("") + StateInfo.propertyFields.forEach properties@ { property -> + file.appendText("${property.value.name}\n") + Blocks::class.java.declaredFields.forEach blocks@ { field -> + field.isAccessible = true + val block = field.get(null) + if (!Block::class.java.isAssignableFrom(block::class.java)) return@blocks + if ((block as Block).defaultState.properties.contains(property.key)) { + file.appendText(" $block\n") + } + } + file.appendText("\n\n\n\n\n") + } + disable() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt index 960376b5e..adcc1be0a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt @@ -24,6 +24,9 @@ import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.info import com.lambda.util.KeyCode +import net.minecraft.block.BlockState +import net.minecraft.state.property.Properties +import net.minecraft.state.property.Property import net.minecraft.util.hit.BlockHitResult object StateInfo : Module( @@ -33,16 +36,42 @@ object StateInfo : Module( ) { private val printBind by setting("Print", KeyCode.UNBOUND, "The bind used to print the info to chat") + val propertyFields = Properties::class.java.declaredFields + .filter { Property::class.java.isAssignableFrom(it.type) } + .associateBy { it.get(null) as Property<*> } + init { listen { event -> if (!event.isPressed) return@listen if (event.keyCode != printBind.keyCode) return@listen val crosshair = mc.crosshairTarget ?: return@listen if (crosshair !is BlockHitResult) return@listen + info(blockState(crosshair.blockPos).betterToString()) + } + } + + private fun BlockState.betterToString(): String { + val stringBuilder = StringBuilder() + stringBuilder.append(this.owner.toString() + "\n") + + if (entries.isNotEmpty()) { + stringBuilder.append(" [\n") - val targetBlock = blockState(crosshair.blockPos) - val text = "$targetBlock" - info(text) + stringBuilder.append( + entries.entries.joinToString("\n") { (property, value) -> + val fieldName = propertyFields[property]?.name ?: property.toString() + " $fieldName = ${nameValue(property, value)}" + } + ) + + stringBuilder.append("\n ]") } + + return stringBuilder.toString() + } + + @Suppress("UNCHECKED_CAST") + private fun ?> nameValue(property: Property, value: Comparable<*>): String { + return property.name(value as T) } } \ No newline at end of file From 10bd7931009d2053add78e77f20fabb8e86a8cae Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 27 Jun 2025 23:35:13 +0100 Subject: [PATCH 243/364] added property ignores and more pre-processors --- .../processing/PlacementProcessor.kt | 5 +- ...processingInfo.kt => PreProcessingInfo.kt} | 19 +++-- .../processing/ProcessorRegistry.kt | 82 ++++++++++++++++++- .../AttachmentPreProcessor.kt} | 8 +- .../AxisPreProcessor.kt} | 10 +-- .../BlockFacePreProcessor.kt} | 10 +-- .../BlockHalfPreProcessor.kt} | 8 +- .../preprocessors/DoorHingePreProcessor.kt | 57 +++++++++++++ .../HopperFacingPreProcessor.kt} | 8 +- .../SlabPreProcessor.kt} | 15 ++-- .../src/main/resources/lambda.accesswidener | 1 + 11 files changed, 180 insertions(+), 43 deletions(-) rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{PreprocessingInfo.kt => PreProcessingInfo.kt} (72%) rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{processors/AttachmentPreprocessor.kt => preprocessors/AttachmentPreProcessor.kt} (86%) rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{processors/AxisPreprocessor.kt => preprocessors/AxisPreProcessor.kt} (81%) rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{processors/BlockFaceProcessor.kt => preprocessors/BlockFacePreProcessor.kt} (84%) rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{processors/BlockHalfProcessor.kt => preprocessors/BlockHalfPreProcessor.kt} (86%) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{processors/HopperFacingPreprocessor.kt => preprocessors/HopperFacingPreProcessor.kt} (84%) rename common/src/main/kotlin/com/lambda/interaction/construction/processing/{processors/SlabProcessor.kt => preprocessors/SlabPreProcessor.kt} (78%) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt index 0718cf964..40152ea08 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt @@ -21,6 +21,5 @@ import net.minecraft.block.BlockState abstract class PlacementProcessor { abstract fun acceptsState(state: BlockState): Boolean - abstract fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) -} - + abstract fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt similarity index 72% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt index f4edffe13..46d56a2f8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreprocessingInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.construction.processing +import com.lambda.interaction.construction.processing.ProcessorRegistry.postProcessedProperties import com.lambda.interaction.construction.verify.SurfaceScan import net.minecraft.state.property.Property import net.minecraft.util.math.Direction @@ -25,10 +26,11 @@ import net.minecraft.util.math.Direction private annotation class InfoAccumulator @InfoAccumulator -data class PreprocessingInfoAccumulator( +data class PreProcessingInfoAccumulator( private var surfaceScan: SurfaceScan = SurfaceScan.DEFAULT, - private val ignore: MutableSet> = mutableSetOf(), + private val ignore: MutableSet> = postProcessedProperties.toMutableSet(), private val sides: MutableSet = Direction.entries.toMutableSet(), + var shouldBeOmitted: Boolean = false ) { @InfoAccumulator fun offerSurfaceScan(scan: SurfaceScan) { @@ -38,8 +40,8 @@ data class PreprocessingInfoAccumulator( } @InfoAccumulator - fun addIgnores(ignores: Set>) { - ignore.addAll(ignores) + fun addIgnores(vararg properties: Property<*>) { + ignore.addAll(properties) } @InfoAccumulator @@ -53,15 +55,16 @@ data class PreprocessingInfoAccumulator( } @InfoAccumulator - fun complete() = PreprocessingInfo(surfaceScan, ignore, sides) + fun complete() = PreProcessingInfo(surfaceScan, ignore, sides, shouldBeOmitted) } -data class PreprocessingInfo( +data class PreProcessingInfo( val surfaceScan: SurfaceScan, val ignore: Set>, - val sides: Set + val sides: Set, + val shouldBeOmitted: Boolean ) { companion object { - val DEFAULT = PreprocessingInfo(SurfaceScan.DEFAULT, emptySet(), emptySet()) + val DEFAULT = PreProcessingInfo(SurfaceScan.DEFAULT, emptySet(), emptySet(), false) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt index 5e9566b4d..1cf27fef3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt @@ -21,17 +21,91 @@ import com.lambda.core.Loadable import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.reflections.getInstances import net.minecraft.block.BlockState +import net.minecraft.state.property.Properties object ProcessorRegistry : Loadable { private val processors = getInstances() - private val processorCache = mutableMapOf() + private val processorCache = mutableMapOf() + + val postProcessedProperties = setOf( + Properties.EXTENDED, + Properties.EYE, + Properties.HAS_BOOK, + Properties.HAS_BOTTLE_0, Properties.HAS_BOTTLE_1, Properties.HAS_BOTTLE_2, + Properties.HAS_RECORD, + Properties.INVERTED, + Properties.LIT, + Properties.LOCKED, + Properties.OCCUPIED, + Properties.OPEN, + Properties.POWERED, + Properties.SIGNAL_FIRE, + Properties.SNOWY, + Properties.TRIGGERED, + Properties.UNSTABLE, + Properties.WATERLOGGED, + Properties.BERRIES, + Properties.BLOOM, + Properties.SHRIEKING, + Properties.CAN_SUMMON, + Properties.FLOWER_AMOUNT, + Properties.EAST_WALL_SHAPE, Properties.SOUTH_WALL_SHAPE, Properties.WEST_WALL_SHAPE, Properties.NORTH_WALL_SHAPE, + Properties.EAST_WIRE_CONNECTION, Properties.SOUTH_WIRE_CONNECTION, Properties.WEST_WIRE_CONNECTION, Properties.NORTH_WIRE_CONNECTION, + Properties.AGE_1, + Properties.AGE_2, + Properties.AGE_3, + Properties.AGE_4, + Properties.AGE_5, + Properties.AGE_7, + Properties.AGE_15, + Properties.AGE_25, + Properties.BITES, + Properties.CANDLES, + Properties.DELAY, + Properties.EGGS, + Properties.HATCH, + Properties.LAYERS, + Properties.LEVEL_3, + Properties.LEVEL_8, + Properties.LEVEL_1_8, + Properties.HONEY_LEVEL, + Properties.LEVEL_15, + Properties.MOISTURE, + Properties.NOTE, + Properties.PICKLES, + Properties.POWER, + Properties.STAGE, + Properties.CHARGES, + Properties.CHEST_TYPE, + Properties.COMPARATOR_MODE, + Properties.INSTRUMENT, + Properties.STAIR_SHAPE, + Properties.TILT, + Properties.THICKNESS, + Properties.SCULK_SENSOR_PHASE, + Properties.SLOT_0_OCCUPIED, Properties.SLOT_1_OCCUPIED, Properties.SLOT_2_OCCUPIED, Properties.SLOT_3_OCCUPIED, Properties.SLOT_4_OCCUPIED, Properties.SLOT_5_OCCUPIED, + Properties.DUSTED, + Properties.CRAFTING, + Properties.TRIAL_SPAWNER_STATE, + Properties.DISARMED, + Properties.ATTACHED, + Properties.DRAG, + Properties.ENABLED, + Properties.IN_WALL, + Properties.UP, + Properties.DOWN, + Properties.NORTH, + Properties.EAST, + Properties.SOUTH, + Properties.WEST, + ) override fun load() = "Loaded ${processors.size} pre processors" - fun TargetState.getProcessingInfo(): PreprocessingInfo = + fun TargetState.getProcessingInfo(): PreProcessingInfo = (this as? TargetState.State)?.let { state -> processorCache.getOrPut(state.blockState) { - val infoAccumulator = PreprocessingInfoAccumulator() + val infoAccumulator = PreProcessingInfoAccumulator() processors.forEach { if (!it.acceptsState(state.blockState)) return@forEach @@ -40,5 +114,5 @@ object ProcessorRegistry : Loadable { return infoAccumulator.complete() } - } ?: PreprocessingInfo.DEFAULT + } ?: PreProcessingInfo.DEFAULT } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt similarity index 86% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt index cd0df53e3..1d43851c4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AttachmentPreprocessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package com.lambda.interaction.construction.processing.processors +package com.lambda.interaction.construction.processing.preprocessors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.block.enums.Attachment import net.minecraft.state.property.Properties @@ -26,11 +26,11 @@ import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") -object AttachmentPreprocessor : PlacementProcessor() { +object AttachmentPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.properties.contains(Properties.ATTACHMENT) - override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { val attachment = state.get(Properties.ATTACHMENT) ?: return with (accumulator) { when (attachment) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt similarity index 81% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt index 754aa960e..4c917b6c7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/AxisPreprocessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,20 +15,20 @@ * along with this program. If not, see . */ -package com.lambda.interaction.construction.processing.processors +package com.lambda.interaction.construction.processing.preprocessors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.state.property.Properties // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") -object AxisPreprocessor : PlacementProcessor() { +object AxisPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.AXIS).isPresent - override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { val axis = state.get(Properties.AXIS) accumulator.retainSides { side -> side.axis == axis diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt index 0b9f354d6..5be67cc79 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockFaceProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package com.lambda.interaction.construction.processing.processors +package com.lambda.interaction.construction.processing.preprocessors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.block.enums.BlockFace import net.minecraft.state.property.Properties @@ -26,11 +26,11 @@ import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") -object BlockFaceProcessor : PlacementProcessor() { +object BlockFacePreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.BLOCK_FACE).isPresent - override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { val property = state.get(Properties.BLOCK_FACE) ?: return with (accumulator) { when (property) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt similarity index 86% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt index ba72e3698..6f32e424c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/BlockHalfProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package com.lambda.interaction.construction.processing.processors +package com.lambda.interaction.construction.processing.preprocessors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan import net.minecraft.block.BlockState @@ -28,11 +28,11 @@ import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") -object BlockHalfProcessor : PlacementProcessor() { +object BlockHalfPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.BLOCK_HALF).isPresent - override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { val slab = state.get(Properties.BLOCK_HALF) ?: return val surfaceScan = when (slab) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt new file mode 100644 index 000000000..f08104490 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.construction.processing.preprocessors + +import com.lambda.interaction.construction.processing.PlacementProcessor +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator +import com.lambda.interaction.construction.verify.ScanMode +import com.lambda.interaction.construction.verify.SurfaceScan +import com.lambda.threading.runSafe +import net.minecraft.block.BlockState +import net.minecraft.block.enums.DoorHinge +import net.minecraft.state.property.Properties +import net.minecraft.util.math.Direction + +// Collected using reflections and then accessed from a collection in ProcessorRegistry +@Suppress("unused") +object DoorHingePreProcessor : PlacementProcessor() { + override fun acceptsState(state: BlockState) = + state.properties.contains(Properties.DOOR_HINGE) + + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) = + runSafe { + val side = state.get(Properties.DOOR_HINGE) ?: return@runSafe + val scanner = when (state.get(Properties.HORIZONTAL_FACING) ?: return@runSafe) { + Direction.NORTH -> + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.X) + else SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.X) + Direction.EAST -> + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Z) + else SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Z) + Direction.SOUTH -> + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.X) + else SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.X) + Direction.DOWN, + Direction.UP, + Direction.WEST -> + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Z) + else SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Z) + } + accumulator.offerSurfaceScan(scanner) + } ?: Unit +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/HopperFacingPreprocessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/HopperFacingPreprocessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt index bf3318aa3..447d50586 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/HopperFacingPreprocessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt @@ -15,21 +15,21 @@ * along with this program. If not, see . */ -package com.lambda.interaction.construction.processing.processors +package com.lambda.interaction.construction.processing.preprocessors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.state.property.Properties import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") -object HopperFacingPreprocessor : PlacementProcessor() { +object HopperFacingPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.properties.contains(Properties.HOPPER_FACING) - override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { val facing = state.get(Properties.HOPPER_FACING) ?: return when { facing.axis == Direction.Axis.Y -> accumulator.retainSides { it.axis == Direction.Axis.Y } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt similarity index 78% rename from common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt index 2a280359e..d110ef7d9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/processors/SlabProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package com.lambda.interaction.construction.processing.processors +package com.lambda.interaction.construction.processing.preprocessors import com.lambda.interaction.construction.processing.PlacementProcessor -import com.lambda.interaction.construction.processing.PreprocessingInfoAccumulator +import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan import net.minecraft.block.BlockState @@ -29,16 +29,19 @@ import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") -object SlabProcessor : PlacementProcessor() { +object SlabPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.block is SlabBlock - override fun preProcess(state: BlockState, accumulator: PreprocessingInfoAccumulator) { + override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { val slab = state.get(Properties.SLAB_TYPE) ?: return val surfaceScan = when (slab) { SlabType.BOTTOM -> SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Y) SlabType.TOP -> SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Y) - SlabType.DOUBLE -> SurfaceScan(ScanMode.FULL, Direction.Axis.Y) + SlabType.DOUBLE -> { + accumulator.addIgnores(Properties.SLAB_TYPE) + SurfaceScan(ScanMode.FULL, Direction.Axis.Y) + } } accumulator.offerSurfaceScan(surfaceScan) diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 8950d4a23..135db0071 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -20,6 +20,7 @@ accessible method net/minecraft/item/BlockItem place (Lnet/minecraft/item/ItemPl accessible method net/minecraft/item/BlockItem placeFromNbt (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState; accessible method net/minecraft/item/BlockItem postPlacement (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Z accessible method net/minecraft/item/BlockItem getPlaceSound (Lnet/minecraft/block/BlockState;)Lnet/minecraft/sound/SoundEvent; +accessible field net/minecraft/state/State owner Ljava/lang/Object; # Entity accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; From 2ab41427b99940b8dbd802c9511e53a0e60762e2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 2 Jul 2025 18:33:44 +0100 Subject: [PATCH 244/364] initial post-processing logic and interaction manager. Only has two handled properties currently. --- .../baritone/MixinBaritonePlayerContext.java | 2 +- .../mixin/baritone/MixinLookBehavior.java | 2 +- .../mixin/entity/ClientPlayerEntityMixin.java | 4 +- .../com/lambda/mixin/entity/EntityMixin.java | 2 +- .../mixin/entity/LivingEntityMixin.java | 2 +- .../mixin/entity/PlayerEntityMixin.java | 3 +- .../com/lambda/mixin/render/CameraMixin.java | 3 +- .../render/LivingEntityRendererMixin.java | 2 +- .../com/lambda/config/groups/BuildConfig.kt | 1 + .../com/lambda/config/groups/BuildSettings.kt | 1 + .../lambda/config/groups/InteractionConfig.kt | 45 +- .../config/groups/InteractionSettings.kt | 8 +- .../lambda/config/groups/RotationSettings.kt | 4 +- .../com/lambda/config/groups/Targeting.kt | 6 +- .../lambda/event/events/PlayerPacketEvent.kt | 2 +- .../com/lambda/event/events/RotationEvent.kt | 4 +- .../lambda/event/events/UpdateManagerEvent.kt | 1 + .../interaction/MovementConfiguration.kt | 2 +- .../lambda/interaction/PlayerPacketManager.kt | 4 +- .../interaction/blockplace/PlaceFinder.kt | 2 +- .../construction/context/BreakContext.kt | 5 +- .../construction/context/BuildContext.kt | 9 +- .../construction/context/InteractContext.kt | 81 +++ .../construction/context/PlaceContext.kt | 7 +- .../processing/PreProcessingInfo.kt | 36 +- .../processing/ProcessorRegistry.kt | 2 +- .../preprocessors/AttachmentPreProcessor.kt | 2 +- .../preprocessors/BlockFacePreProcessor.kt | 2 +- .../construction/result/InteractResult.kt | 41 ++ .../construction/result/PlaceResult.kt | 5 +- .../interaction/construction/result/Rank.kt | 1 + .../construction/simulation/BuildSimulator.kt | 547 ++++++++++++------ .../construction/simulation/Simulation.kt | 2 +- .../construction/verify/StateMatcher.kt | 3 +- .../construction/verify/TargetState.kt | 15 +- .../request/breaking/BreakManager.kt | 5 +- .../request/breaking/BreakRequest.kt | 2 +- .../request/breaking/BrokenBlockHandler.kt | 3 +- .../interacting/InteractedBlockHandler.kt | 89 +++ .../request/interacting/InteractionInfo.kt | 28 + .../request/interacting/InteractionManager.kt | 116 ++++ .../request/interacting/InteractionRequest.kt | 43 ++ .../request/placing/PlaceConfig.kt | 3 +- .../request/placing/PlaceManager.kt | 4 +- .../request/placing/PlaceRequest.kt | 4 +- .../request/placing/PlacedBlockHandler.kt | 32 +- .../{rotation => rotating}/Rotation.kt | 2 +- .../{rotation => rotating}/RotationConfig.kt | 2 +- .../{rotation => rotating}/RotationManager.kt | 6 +- .../{rotation => rotating}/RotationMode.kt | 2 +- .../{rotation => rotating}/RotationRequest.kt | 4 +- .../visibilty/PlaceDirection.kt | 4 +- .../visibilty/PointSelection.kt | 6 +- .../visibilty/RequestedHit.kt | 8 +- .../visibilty/RequestedHitDsl.kt | 2 +- .../visibilty/RotationTarget.kt | 12 +- .../visibilty/RotationTargets.kt | 12 +- .../visibilty/VisibilityChecker.kt | 9 +- .../lambda/module/modules/combat/Criticals.kt | 4 +- .../module/modules/combat/CrystalAura.kt | 8 +- .../lambda/module/modules/combat/KillAura.kt | 4 +- .../module/modules/debug/PropertyPrinter.kt | 2 +- .../lambda/module/modules/movement/Speed.kt | 10 +- .../module/modules/movement/TargetStrafe.kt | 2 +- .../lambda/module/modules/player/Freecam.kt | 15 +- .../module/modules/player/InventoryMove.kt | 28 +- .../lambda/module/modules/player/Replay.kt | 28 +- .../lambda/module/modules/player/Scaffold.kt | 24 +- .../lambda/module/modules/render/Particles.kt | 10 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 24 +- .../com/lambda/task/tasks/OpenContainer.kt | 4 +- .../com/lambda/task/tasks/PlaceContainer.kt | 2 +- .../main/kotlin/com/lambda/util/BlockUtils.kt | 5 +- .../com/lambda/util/extension/Entity.kt | 2 +- .../kotlin/com/lambda/util/math/Linear.kt | 2 +- .../com/lambda/util/player/MovementUtils.kt | 2 +- .../player/prediction/PredictionEntity.kt | 2 +- .../util/player/prediction/PredictionTick.kt | 2 +- .../lambda/util/world/raycast/RayCastUtils.kt | 3 +- common/src/test/kotlin/PlaceDirectionTest.kt | 12 +- common/src/test/kotlin/RotationTest.kt | 12 +- 81 files changed, 1073 insertions(+), 390 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/Rotation.kt (99%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/RotationConfig.kt (98%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/RotationManager.kt (98%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/RotationMode.kt (95%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/RotationRequest.kt (93%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/PlaceDirection.kt (97%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/PointSelection.kt (87%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/RequestedHit.kt (94%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/RequestedHitDsl.kt (95%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/RotationTarget.kt (82%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/RotationTargets.kt (92%) rename common/src/main/kotlin/com/lambda/interaction/request/{rotation => rotating}/visibilty/VisibilityChecker.kt (97%) diff --git a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java index 0190fe979..88a9157ff 100644 --- a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java +++ b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java @@ -20,7 +20,7 @@ import baritone.Baritone; import baritone.api.utils.Rotation; import baritone.utils.player.BaritonePlayerContext; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import com.lambda.util.BaritoneUtils; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; diff --git a/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java b/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java index 62246595b..1f46fcd28 100644 --- a/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java +++ b/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java @@ -21,7 +21,7 @@ import baritone.api.event.events.RotationMoveEvent; import baritone.api.utils.Rotation; import baritone.behavior.LookBehavior; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import com.lambda.util.BaritoneUtils; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index 3375d4097..92ec9bd1e 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -23,14 +23,12 @@ import com.lambda.event.events.PlayerEvent; import com.lambda.event.events.TickEvent; import com.lambda.interaction.PlayerPacketManager; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import com.lambda.module.modules.player.PortalGui; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.DeathScreen; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.input.Input; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.MovementType; diff --git a/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java index ed86c816d..6b424da24 100644 --- a/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java @@ -21,7 +21,7 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.EntityEvent; import com.lambda.event.events.PlayerEvent; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import com.lambda.util.math.Vec2d; import net.minecraft.entity.Entity; import net.minecraft.entity.MovementType; diff --git a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java index 0092e7eb3..9343953fb 100644 --- a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java @@ -20,7 +20,7 @@ import com.lambda.Lambda; import com.lambda.event.EventFlow; import com.lambda.event.events.MovementEvent; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import net.minecraft.entity.LivingEntity; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; diff --git a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java index e395153cf..97560a19b 100644 --- a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java @@ -20,8 +20,7 @@ import com.lambda.Lambda; import com.lambda.event.EventFlow; import com.lambda.event.events.MovementEvent; -import com.lambda.interaction.request.rotation.RotationManager; -import net.minecraft.client.MinecraftClient; +import com.lambda.interaction.request.rotating.RotationManager; import net.minecraft.entity.player.PlayerEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/common/src/main/java/com/lambda/mixin/render/CameraMixin.java b/common/src/main/java/com/lambda/mixin/render/CameraMixin.java index c8043acd8..07d9cda8b 100644 --- a/common/src/main/java/com/lambda/mixin/render/CameraMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/CameraMixin.java @@ -17,12 +17,11 @@ package com.lambda.mixin.render; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import com.lambda.module.modules.player.Freecam; import com.lambda.module.modules.render.CameraTweaks; import net.minecraft.client.render.Camera; import net.minecraft.entity.Entity; -import net.minecraft.util.math.MathHelper; import net.minecraft.world.BlockView; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java index 58956ca30..0d9b533ee 100644 --- a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java @@ -18,7 +18,7 @@ package com.lambda.mixin.render; import com.lambda.Lambda; -import com.lambda.interaction.request.rotation.RotationManager; +import com.lambda.interaction.request.rotating.RotationManager; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.LivingEntityRenderer; import net.minecraft.client.util.math.MatrixStack; diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 2edb7dc36..792b1efdf 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -22,6 +22,7 @@ interface BuildConfig { val pathing: Boolean val stayInRange: Boolean val collectDrops: Boolean + val interactionsPerTick: Int val maxPendingInteractions: Int val interactionTimeout: Int diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 68d1e1c4c..df918e81d 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -35,6 +35,7 @@ class BuildSettings( override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.General } override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.General && pathing } override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.General } + override val interactionsPerTick by c.setting("Interactions Per Tick", 5, 1..30, 1, "The amount of interactions that can happen per tick", visibility = vis) override val maxPendingInteractions by c.setting("Max Pending Interactions", 20, 1..30, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt index 04e28b4c2..cbe9d2bf3 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -17,50 +17,75 @@ package com.lambda.config.groups -import com.lambda.interaction.request.rotation.visibilty.PointSelection +import com.lambda.interaction.request.RequestConfig +import com.lambda.interaction.request.interacting.InteractionManager +import com.lambda.interaction.request.interacting.InteractionRequest +import com.lambda.interaction.request.rotating.visibilty.PointSelection -interface InteractionConfig { +abstract class InteractionConfig( + priority: Int +) : RequestConfig(priority) { /** * Maximum entity interaction distance */ - val attackReach: Double + abstract val attackReach: Double /** * Maximum block interaction distance */ - val interactReach: Double + abstract val interactReach: Double + + /** + * Rotates the player for block interactions. For example, right-clicking a chest to open it. + */ + abstract val rotate: Boolean /** * Maximum possible interaction distance * * Equals to `max(attackReach, placeReach)` if both are present. Equals to one of them otherwise */ - val scanReach: Double + abstract val scanReach: Double /** * Whether to include the environment to the ray cast context. * * if false: skips walls for entities, skips entities for blocks. */ - val strictRayCast: Boolean + abstract val strictRayCast: Boolean /** * Whether to check if an AABB side is visible. */ - val checkSideVisibility: Boolean + abstract val checkSideVisibility: Boolean /** * Grid divisions count per surface of the hit box. */ - val resolution: Int + abstract val resolution: Int /** * The way to select the best point. */ - val pointSelection: PointSelection + abstract val pointSelection: PointSelection + + /** + * The method of confirming the interaction had taken place server side + */ + abstract val interactConfirmationMode: InteractConfirmationMode /** * Whether to swing the hand when interacting. */ - val swingHand: Boolean + abstract val swingHand: Boolean + + override fun requestInternal(request: InteractionRequest, queueIfClosed: Boolean) { + InteractionManager.request(request, queueIfClosed) + } + + enum class InteractConfirmationMode { + None, + InteractThenAwait, + AwaitThenInteract + } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index f1a8ec0ac..471a9e1da 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -18,7 +18,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.interaction.request.rotation.visibilty.PointSelection +import com.lambda.interaction.request.rotating.visibilty.PointSelection import com.lambda.util.world.raycast.InteractionMask import kotlin.math.max @@ -26,12 +26,14 @@ class InteractionSettings( c: Configurable, private val usage: InteractionMask, vis: () -> Boolean = { true }, -) : InteractionConfig { +) : InteractionConfig(0) { // Reach private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", visibility = vis) private val attackReachSetting = if (usage.entity) c.setting("Attack Reach", DEFAULT_ATTACK_REACH, 1.0..10.0, 0.01, "Maximum entity interaction distance") { vis() && !useDefaultReach } else null private val interactReachSetting = if (usage.block) c.setting("Interact Reach", DEFAULT_INTERACT_REACH, 1.0..10.0, 0.01, "Maximum block interaction distance") { vis() && !useDefaultReach } else null + override val rotate by c.setting("Rotate For Interactions", true, "Rotates the player for block interactions. For example, right-clicking a chest to open it", visibility = vis) + override val attackReach: Double get() { check(usage.entity) { "Given interaction config has no attack reach implementation" @@ -60,6 +62,8 @@ class InteractionSettings( override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", visibility = vis) override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The strategy to select the best hit point", visibility = vis) + override val interactConfirmationMode by c.setting("Interact Confirmation Mode", InteractConfirmationMode.InteractThenAwait, "The style of confirmation used when interacting", visibility = vis) + // Swing override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", visibility = vis) diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index cec04b1e9..29c9138a9 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -20,8 +20,8 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.interaction.request.rotation.RotationMode +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationMode import kotlin.math.PI import kotlin.math.abs import kotlin.math.cos diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index c3a147b91..60fd49471 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -20,9 +20,9 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.context.SafeContext import com.lambda.friend.FriendManager.isFriend -import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.Rotation.Companion.rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.Rotation.Companion.dist +import com.lambda.interaction.request.rotating.Rotation.Companion.rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo import com.lambda.threading.runSafe import com.lambda.util.extension.fullHealth import com.lambda.util.math.distSq diff --git a/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt b/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt index 6025f04ab..1ccfaff7c 100644 --- a/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt @@ -20,7 +20,7 @@ package com.lambda.event.events import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket import net.minecraft.util.math.Vec3d diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index 6fe769595..befe5e1c3 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -18,9 +18,7 @@ package com.lambda.event.events import com.lambda.event.Event -import com.lambda.event.callback.Cancellable -import com.lambda.event.callback.ICancellable -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotating.RotationRequest import net.minecraft.client.input.Input sealed class RotationEvent { diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt index ecb1fef96..9d1771cdf 100644 --- a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt @@ -24,4 +24,5 @@ sealed class UpdateManagerEvent { data object Hotbar : Event data object Break : Event data object Place : Event + data object Interact: Event } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt b/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt index 4974998c4..8a4593418 100644 --- a/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt +++ b/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt @@ -17,7 +17,7 @@ package com.lambda.interaction -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import net.minecraft.util.math.Vec3d data class MovementConfiguration( diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index a6657e5cf..275a592c1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -21,8 +21,8 @@ import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.EventFlow.postChecked import com.lambda.event.events.PlayerPacketEvent -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.RotationManager import com.lambda.threading.runSafe import com.lambda.util.collections.LimitedOrderedSet import com.lambda.util.math.approximate diff --git a/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt b/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt index 2ee36c1e0..06ab2ba2d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt +++ b/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt @@ -20,7 +20,7 @@ package com.lambda.interaction.blockplace import com.lambda.context.SafeContext import com.lambda.interaction.blockplace.PlaceInteraction.canPlaceAt import com.lambda.interaction.blockplace.PlaceInteraction.isClickable -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.distSq import com.lambda.util.math.getHitVec diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 4ab7cc656..abb62ca84 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.construction.context -import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude @@ -25,7 +24,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.block.FallingBlock @@ -77,8 +76,6 @@ data class BreakContext( } } - override fun shouldRotate(config: BuildConfig) = config.breaking.rotateForBreak - override fun SafeContext.buildRenderer() { withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) withState(checkedState, expectedPos, sideColor, result.side) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 5f3230588..9f85c357a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -17,13 +17,10 @@ package com.lambda.interaction.construction.context -import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotating.RotationRequest import net.minecraft.block.BlockState -import net.minecraft.item.ItemStack import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d @@ -38,8 +35,4 @@ interface BuildContext : Comparable, Drawable { val expectedPos: BlockPos val checkedState: BlockState val hotbarIndex: Int - - fun shouldRotate(config: BuildConfig): Boolean - - data class LocalizedStack(val container: MaterialContainer, val stack: ItemStack) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt new file mode 100644 index 000000000..9c5cd1165 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.construction.context + +import com.lambda.config.groups.InteractionConfig +import com.lambda.context.SafeContext +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DirectionMask.exclude +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.hotbar.HotbarManager +import com.lambda.interaction.request.hotbar.HotbarRequest +import com.lambda.interaction.request.interacting.InteractionRequest +import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.util.BlockUtils +import com.lambda.util.BlockUtils.blockState +import net.minecraft.block.BlockState +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d +import java.awt.Color + +class InteractContext( + override val pov: Vec3d, + override val result: BlockHitResult, + override val rotation: RotationRequest, + override val distance: Double, + override val expectedState: BlockState, + override val targetState: TargetState, + override val expectedPos: BlockPos, + override val checkedState: BlockState, + override var hotbarIndex: Int, + val interaction: InteractionConfig, +) : BuildContext { + private val baseColor = Color(35, 254, 79, 25) + private val sideColor = Color(35, 254, 79, 100) + + override fun compareTo(other: BuildContext) = + when { + other is InteractContext -> compareBy { + BlockUtils.fluids.indexOf(it.checkedState.fluidState.fluid) + }.thenByDescending { + it.checkedState.fluidState.level + }.thenBy { + it.rotation.target.angleDistance + }.thenBy { + it.hotbarIndex == HotbarManager.serverSlot + }.thenBy { + it.distance + }.compare(this, other) + + else -> 1 + } + + override fun SafeContext.buildRenderer() { + withState(expectedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side.opposite)) + withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) + } + + fun requestDependencies(request: InteractionRequest): Boolean { + val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) + val validRotation = if (request.interact.rotate) { + request.rotation.request(rotation, false).done + } else true + return hotbarRequest.done && validRotation + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index cd87f247c..2d11be61d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.construction.context import com.lambda.Lambda.mc -import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude @@ -26,7 +25,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceRequest -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState @@ -47,7 +46,7 @@ data class PlaceContext( override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, - val currentDirIsInvalid: Boolean = false + val currentDirIsInvalid: Boolean = false, ) : BuildContext { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) @@ -78,8 +77,6 @@ data class PlaceContext( withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) } - override fun shouldRotate(config: BuildConfig) = config.placing.rotateForPlace - fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) val validRotation = if (request.build.placing.rotateForPlace) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt index 46d56a2f8..f9cb95030 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt @@ -26,12 +26,12 @@ import net.minecraft.util.math.Direction private annotation class InfoAccumulator @InfoAccumulator -data class PreProcessingInfoAccumulator( - private var surfaceScan: SurfaceScan = SurfaceScan.DEFAULT, - private val ignore: MutableSet> = postProcessedProperties.toMutableSet(), - private val sides: MutableSet = Direction.entries.toMutableSet(), - var shouldBeOmitted: Boolean = false -) { +class PreProcessingInfoAccumulator( + override var surfaceScan: SurfaceScan = SurfaceScan.DEFAULT, + override val ignore: MutableSet> = postProcessedProperties.toMutableSet(), + override val sides: MutableSet = Direction.entries.toMutableSet(), + override var shouldBeOmitted: Boolean = false +) : PreProcessingInfo { @InfoAccumulator fun offerSurfaceScan(scan: SurfaceScan) { if (scan.mode.priority > surfaceScan.mode.priority) { @@ -55,16 +55,26 @@ data class PreProcessingInfoAccumulator( } @InfoAccumulator - fun complete() = PreProcessingInfo(surfaceScan, ignore, sides, shouldBeOmitted) + fun omitPlacement() { + shouldBeOmitted = true + } + + @InfoAccumulator + fun complete() = this as PreProcessingInfo } -data class PreProcessingInfo( - val surfaceScan: SurfaceScan, - val ignore: Set>, - val sides: Set, +interface PreProcessingInfo { + val surfaceScan: SurfaceScan + val ignore: Set> + val sides: Set val shouldBeOmitted: Boolean -) { + companion object { - val DEFAULT = PreProcessingInfo(SurfaceScan.DEFAULT, emptySet(), emptySet(), false) + val DEFAULT = object : PreProcessingInfo { + override val surfaceScan = SurfaceScan.DEFAULT + override val ignore = setOf>() + override val sides = setOf() + override val shouldBeOmitted = false + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt index 1cf27fef3..7de710d7f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt @@ -112,7 +112,7 @@ object ProcessorRegistry : Loadable { it.preProcess(state.blockState, infoAccumulator) } - return infoAccumulator.complete() + infoAccumulator.complete() } } ?: PreProcessingInfo.DEFAULT } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt index 1d43851c4..e64e64390 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt @@ -36,7 +36,7 @@ object AttachmentPreProcessor : PlacementProcessor() { when (attachment) { Attachment.FLOOR -> retainSides(Direction.DOWN) Attachment.CEILING -> retainSides(Direction.UP) - else -> retainSides { Direction.Type.HORIZONTAL.contains(it) } + else -> retainSides { it in Direction.Type.HORIZONTAL } } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt index 5be67cc79..38cabc348 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt @@ -36,7 +36,7 @@ object BlockFacePreProcessor : PlacementProcessor() { when (property) { BlockFace.FLOOR -> retainSides(Direction.DOWN) BlockFace.CEILING -> retainSides(Direction.UP) - BlockFace.WALL -> retainSides { Direction.Type.HORIZONTAL.contains(it) } + BlockFace.WALL -> retainSides { it in Direction.Type.HORIZONTAL } } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt new file mode 100644 index 000000000..4847128b4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.construction.result + +import com.lambda.context.SafeContext +import com.lambda.interaction.construction.context.InteractContext +import net.minecraft.util.math.BlockPos + +sealed class InteractResult : BuildResult() { + data class Interact( + override val blockPos: BlockPos, + override val context: InteractContext + ) : Contextual, Drawable, InteractResult() { + override val rank = Rank.INTERACT_SUCCESS + + override fun SafeContext.buildRenderer() { + with(context) { buildRenderer() } + } + + override fun compareTo(other: ComparableResult) = + when (other) { + is Interact -> context.compareTo(other.context) + else -> super.compareTo(other) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 860dbe7d8..1b98142a0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -50,12 +50,11 @@ sealed class PlaceResult : BuildResult() { with(context) { buildRenderer() } } - override fun compareTo(other: ComparableResult): Int { - return when (other) { + override fun compareTo(other: ComparableResult) = + when (other) { is Place -> context.compareTo(other.context) else -> super.compareTo(other) } - } } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt index da6b5a599..2bf302366 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.construction.result enum class Rank { // solvable BREAK_SUCCESS, + INTERACT_SUCCESS, PLACE_SUCCESS, WRONG_ITEM, BREAK_ITEM_CANT_MINE, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 8ea414f8e..65adda4e7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -23,10 +23,13 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.InteractContext import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.construction.processing.PreProcessingInfo import com.lambda.interaction.construction.processing.ProcessorRegistry.getProcessingInfo import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.InteractResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer @@ -37,18 +40,18 @@ import com.lambda.interaction.material.container.ContainerManager.containerWithM import com.lambda.interaction.material.container.MaterialContainer import com.lambda.interaction.request.breaking.BreakConfig import com.lambda.interaction.request.placing.PlaceConfig -import com.lambda.interaction.request.rotation.Rotation.Companion.rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.PlaceDirection -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces -import com.lambda.interaction.request.rotation.visibilty.lookAt -import com.lambda.interaction.request.rotation.visibilty.lookAtBlock -import com.lambda.interaction.request.rotation.visibilty.lookInDirection +import com.lambda.interaction.request.rotating.Rotation.Companion.rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationManager +import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.interaction.request.rotating.visibilty.PlaceDirection +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.CheckedHit +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.scanSurfaces +import com.lambda.interaction.request.rotating.visibilty.lookAt +import com.lambda.interaction.request.rotating.visibilty.lookAtBlock +import com.lambda.interaction.request.rotating.visibilty.lookInDirection import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -68,10 +71,12 @@ import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition import net.minecraft.enchantment.Enchantments import net.minecraft.item.BlockItem +import net.minecraft.item.Item import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys +import net.minecraft.state.property.Properties import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos @@ -90,14 +95,16 @@ object BuildSimulator { build: BuildConfig = TaskFlowModule.build, ) = runSafe { structure.entries.flatMap { (pos, target) -> - checkRequirements(pos, target, build)?.let { - return@flatMap setOf(it) + val preProcessing = target.getProcessingInfo() + checkRequirements(pos, target, eye, preProcessing, build, interact, rotation, inventory).let { + if (it.isEmpty()) return@let + return@flatMap it } - checkPlaceResults(pos, target, eye, build.placing, interact, rotation, inventory).let { + checkPlaceResults(pos, target, eye, preProcessing, build.placing, interact, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } - checkBreakResults(pos, eye, build.breaking, interact, rotation, inventory, build).let { + checkBreakResults(pos, eye, preProcessing, build.breaking, interact, rotation, inventory, build).let { if (it.isEmpty()) return@let return@flatMap it } @@ -109,51 +116,73 @@ object BuildSimulator { private fun SafeContext.checkRequirements( pos: BlockPos, target: TargetState, - build: BuildConfig - ): BuildResult? {/* the chunk is not loaded */ + eye: Vec3d, + preProcessing: PreProcessingInfo, + build: BuildConfig, + interact: InteractionConfig, + rotation: RotationConfig, + inventory: InventoryConfig + ): Set { + val acc = mutableSetOf() + + /* the chunk is not loaded */ if (!world.isChunkLoaded(pos)) { - return BuildResult.ChunkNotLoaded(pos) + acc.add(BuildResult.ChunkNotLoaded(pos)) + return acc } val state = blockState(pos) /* block is already in the correct state */ if (target.matches(state, pos, world)) { - return BuildResult.Done(pos) + acc.add(BuildResult.Done(pos)) + return acc } /* block should be ignored */ if (state.block in build.breaking.ignoredBlocks && target.type == TargetState.Type.AIR) { - return BuildResult.Ignored(pos) + acc.add(BuildResult.Ignored(pos)) + return acc } /* the player is in the wrong game mode to alter the block state */ if (player.isBlockBreakingRestricted(world, pos, gamemode)) { - return BuildResult.Restricted(pos) + acc.add(BuildResult.Restricted(pos)) + return acc } /* the player has no permissions to alter the block state */ if (state.block is OperatorBlock && !player.isCreativeLevelTwoOp) { - return BuildResult.NoPermission(pos, state) + acc.add(BuildResult.NoPermission(pos, state)) + return acc } /* block is outside the world so it cant be altered */ if (!world.worldBorder.contains(pos) || world.isOutOfHeightLimit(pos)) { - return BuildResult.OutOfWorld(pos) + acc.add(BuildResult.OutOfWorld(pos)) + return acc + } + + /* the state requires post-processing */ + if (target.matches(state, pos, world, ignoredProperties = preProcessing.ignore)) { + acc.addAll(checkPostProcessResults(target, state, pos, eye, preProcessing, interact, build.placing, rotation, inventory)) + if (acc.isNotEmpty()) return acc } /* block is unbreakable, so it cant be broken or replaced */ if (state.getHardness(world, pos) < 0 && !gamemode.isCreative) { - return BuildResult.Unbreakable(pos, state) + acc.add(BuildResult.Unbreakable(pos, state)) + return acc } - return null + return acc } private fun SafeContext.checkPlaceResults( pos: BlockPos, target: TargetState, eye: Vec3d, + preProcessing: PreProcessingInfo, place: PlaceConfig, interact: InteractionConfig, rotation: RotationConfig, @@ -164,9 +193,7 @@ object BuildSimulator { if (target.isAir() || !targetPosState.isReplaceable) return acc - val preprocessing = target.getProcessingInfo() - - preprocessing.sides.forEach { neighbor -> + preProcessing.sides.forEach { neighbor -> val hitPos = if (!place.airPlace.isEnabled() && (targetPosState.isAir || targetPosState.isLiquid)) pos.offset(neighbor) else pos @@ -191,10 +218,10 @@ object BuildSimulator { val sides = if (interact.checkSideVisibility) { box.getVisibleSurfaces(eye).intersect(setOf(hitSide)) } else { - Direction.entries.toSet() + setOf(hitSide) } - scanSurfaces(box, sides, interact.resolution, preprocessing.surfaceScan) { _, vec -> + scanSurfaces(box, sides, interact.resolution, preProcessing.surfaceScan) { _, vec -> val distSquared = eye distSq vec if (distSquared > reachSq) { misses.add(vec) @@ -238,173 +265,365 @@ object BuildSimulator { return@forEach } - interact.pointSelection.select(validHits)?.let { checkedHit -> - val optimalStack = target.getStack(world, pos) - - // ToDo: For each hand and sneak or not? - val fakePlayer = copyPlayer(player).apply { - setPos(eye.x, eye.y - standingEyeHeight, eye.z) - this.rotation = RotationManager.serverRotation - } + checkPlaceOn(pos, validHits, target, eye, preProcessing, place, rotation, interact, inventory)?.let { placeResult -> + acc.add(placeResult) + } + } - val checkedResult = checkedHit.hit + return acc + } - val usageContext = ItemUsageContext( - fakePlayer, - Hand.MAIN_HAND, - checkedResult.blockResult, - ) - val cachePos = CachedBlockPosition( - usageContext.world, usageContext.blockPos, false - ) - val canBePlacedOn = optimalStack.canPlaceOn( - usageContext.world.registryManager.get(RegistryKeys.BLOCK), - cachePos, - ) - if (!player.abilities.allowModifyWorld && !canBePlacedOn) { - acc.add(PlaceResult.IllegalUsage(pos)) - return@forEach - } + private fun SafeContext.checkPostProcessResults( + targetState: TargetState, + state: BlockState, + pos: BlockPos, + eye: Vec3d, + preProcessing: PreProcessingInfo, + interact: InteractionConfig, + place: PlaceConfig, + rotation: RotationConfig, + inventory: InventoryConfig + ): Set { + if (targetState !is TargetState.State) return emptySet() - var context = ItemPlacementContext(usageContext) + val acc = mutableSetOf() - if (context.blockPos != pos) { - acc.add(PlaceResult.UnexpectedPosition(pos, context.blockPos)) - return@forEach + val interactBlock: (BlockState, Set?, Item?, Boolean) -> Unit = + { expectedState, side, item, placing -> + interactWithBlock( + pos, + state, + expectedState, + targetState, + eye, + side, + preProcessing, + item ?: player.inventory.mainHandStack.item, + placing, + interact, + place, + rotation, + inventory + )?.let { result -> + acc.add(result) } + } - if (!optimalStack.item.isEnabled(world.enabledFeatures)) { - acc.add(PlaceResult.BlockFeatureDisabled(pos, optimalStack)) - return@forEach + val mismatchedProperties = state.properties.filter { state.get(it) != targetState.blockState.get(it) } + mismatchedProperties.forEach { property -> + when (property) { + Properties.EYE -> run eye@ { + if (state.get(Properties.EYE)) return@eye + val expectedState = state.with(Properties.EYE, true) + interactBlock(expectedState, null, null, false) } - - if (!context.canPlace()) { - acc.add(PlaceResult.CantReplace(pos, context)) - return@forEach + Properties.OPEN -> run open@ { + val expectedState = state.with(Properties.OPEN, !state.get(Properties.OPEN)) + interactBlock(expectedState, null, null, false) } + } + } - val blockItem = optimalStack.item as? BlockItem ?: run { - acc.add(PlaceResult.NotItemBlock(pos, optimalStack)) - return@forEach + if (acc.isEmpty()) { + acc.add(BuildResult.Done(pos)) + } + + return acc + } + + private fun SafeContext.interactWithBlock( + pos: BlockPos, + state: BlockState, + expectedState: BlockState, + targetState: TargetState, + eye: Vec3d, + sides: Set?, + preProcessing: PreProcessingInfo, + item: Item, + placing: Boolean, + interact: InteractionConfig, + place: PlaceConfig, + rotation: RotationConfig, + inventory: InventoryConfig + ): BuildResult? { + val boxes = state.getOutlineShape(world, pos).boundingBoxes.map { it.offset(pos) } + val validHits = mutableListOf() + val blockedHits = mutableSetOf() + val misses = mutableSetOf() + val airPlace = placing && place.airPlace.isEnabled() + + boxes.forEach { box -> + val refinedSides = if (interact.checkSideVisibility) { + box.getVisibleSurfaces(eye).let { visibleSides -> + sides?.let { specific -> + visibleSides.intersect(specific) + } ?: visibleSides.toSet() } + } else sides ?: Direction.entries.toSet() - val checked = blockItem.getPlacementContext(context) - if (checked == null) { - acc.add(PlaceResult.ScaffoldExceeded(pos, context)) - return@forEach - } else { - context = checked + scanSurfaces(box, refinedSides, interact.resolution, preProcessing.surfaceScan) { hitSide, vec -> + val distSquared = eye distSq vec + if (distSquared > interact.interactReach.pow(2)) { + misses.add(vec) + return@scanSurfaces } - lateinit var resultState: BlockState - var rot = fakePlayer.rotation + val newRotation = eye.rotationTo(vec) - val simulatePlaceState = placeState@ { - resultState = blockItem.getPlacementState(context) - ?: return@placeState PlaceResult.BlockedByEntity(pos) + val hit = if (interact.strictRayCast) { + val rayCast = newRotation.rayCast(interact.interactReach, eye) + when { + rayCast != null && (!airPlace || eye distSq rayCast.pos <= distSquared) -> + rayCast.blockResult + + airPlace -> { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, hitSide, pos, false) + } - if (!target.matches(resultState, pos, world)) { - return@placeState PlaceResult.NoIntegrity( - pos, resultState, context, (target as? TargetState.State)?.blockState - ) - } else { - return@placeState null + else -> null } + } else { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, hitSide, pos, false) + } ?: return@scanSurfaces + + val checked = CheckedHit(hit, newRotation, interact.interactReach) + if (hit.blockResult?.blockPos != pos) { + blockedHits.add(vec) + return@scanSurfaces } - val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> - if (!place.rotateForPlace) { - acc.add(basePlaceResult) - return@forEach - } - true - } ?: false - - if (place.rotateForPlace) run rotate@ { - if (!place.axisRotate) { - fakePlayer.rotation = checkedHit.targetRotation - simulatePlaceState()?.let { rotatedPlaceResult -> - acc.add(rotatedPlaceResult) - return@forEach - } - rot = fakePlayer.rotation - return@rotate - } + validHits.add(checked) + } + } - fakePlayer.rotation = player.rotation - simulatePlaceState() ?: run { - rot = fakePlayer.rotation - return@rotate - } + if (validHits.isEmpty()) { + return if (misses.isNotEmpty()) { + BuildResult.OutOfReach(pos, eye, misses) + } else { + //ToDo: Must clean up surface scan usage / renders. Added temporary direction until changes are made + BuildResult.NotVisible(pos, pos, Direction.UP, eye.distanceTo(pos.vecOf(Direction.UP))) + } + } - if (currentDirIsInvalid) { - PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> - fakePlayer.rotation = direction.rotation - when (val placeResult = simulatePlaceState()) { - is PlaceResult.BlockedByEntity -> { - acc.add(placeResult) - return@forEach - } - - is PlaceResult.NoIntegrity -> { - if (index != PlaceDirection.entries.lastIndex) return@direction - acc.add(placeResult) - return@forEach - } - - else -> { - rot = fakePlayer.rotation - return@rotate - } - } - } + return if (placing) checkPlaceOn(pos, validHits, targetState, eye, preProcessing, place, rotation, interact, inventory) + else checkInteractOn(pos, validHits, state, expectedState, targetState, item, eye, rotation, interact, inventory) + } + + private fun SafeContext.checkInteractOn( + pos: BlockPos, + validHits: MutableList, + currentState: BlockState, + expectedState: BlockState, + targetState: TargetState, + item: Item, + eye: Vec3d, + rotation: RotationConfig, + interact: InteractionConfig, + inventory: InventoryConfig + ): BuildResult? { + interact.pointSelection.select(validHits)?.let { checkedHit -> + val checkedResult = checkedHit.hit + val rotationTarget = lookAt(checkedHit.targetRotation, 0.001) + val context = InteractContext( + eye, + checkedResult.blockResult ?: return null, + RotationRequest(rotationTarget, rotation), + eye.distanceTo(checkedResult.pos), + expectedState, + targetState, + pos, + currentState, + player.inventory.selectedSlot, + interact + ) + + val stackSelection = item.select() + val hotbarCandidates = selectContainer { + matches(stackSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) + }.let { predicate -> + stackSelection.containerWithMaterial(inventory, predicate) + } + + if (hotbarCandidates.isEmpty()) { + return BuildResult.WrongItemSelection(pos, context, stackSelection, player.mainHandStack, inventory) + } else { + context.hotbarIndex = player.hotbar.indexOf(hotbarCandidates.first().matchingStacks(stackSelection).first()) + } + + return InteractResult.Interact(pos, context) + } + + return null + } + + private fun SafeContext.checkPlaceOn( + pos: BlockPos, + validHits: MutableList, + target: TargetState, + eye: Vec3d, + preProcessing: PreProcessingInfo, + place: PlaceConfig, + rotation: RotationConfig, + interact: InteractionConfig, + inventory: InventoryConfig + ): BuildResult? { + interact.pointSelection.select(validHits)?.let { checkedHit -> + val optimalStack = target.getStack(world, pos) + + // ToDo: For each hand and sneak or not? + val fakePlayer = copyPlayer(player).apply { + setPos(eye.x, eye.y - standingEyeHeight, eye.z) + this.rotation = RotationManager.serverRotation + } + + val checkedResult = checkedHit.hit + + val usageContext = ItemUsageContext( + fakePlayer, + Hand.MAIN_HAND, + checkedResult.blockResult, + ) + val cachePos = CachedBlockPosition( + usageContext.world, usageContext.blockPos, false + ) + val canBePlacedOn = optimalStack.canPlaceOn( + usageContext.world.registryManager.get(RegistryKeys.BLOCK), + cachePos, + ) + if (!player.abilities.allowModifyWorld && !canBePlacedOn) { + return PlaceResult.IllegalUsage(pos) + } + + var context = ItemPlacementContext(usageContext) + + if (context.blockPos != pos) { + return PlaceResult.UnexpectedPosition(pos, context.blockPos) + } + + if (!optimalStack.item.isEnabled(world.enabledFeatures)) { + return PlaceResult.BlockFeatureDisabled(pos, optimalStack) + } + + if (!context.canPlace()) { + return PlaceResult.CantReplace(pos, context) + } + + val blockItem = optimalStack.item as? BlockItem ?: run { + return PlaceResult.NotItemBlock(pos, optimalStack) + } + + val checked = blockItem.getPlacementContext(context) + if (checked == null) { + return PlaceResult.ScaffoldExceeded(pos, context) + } else { + context = checked + } + + lateinit var resultState: BlockState + var rot = fakePlayer.rotation + + val simulatePlaceState = placeState@ { + resultState = blockItem.getPlacementState(context) + ?: return@placeState PlaceResult.BlockedByEntity(pos) + + if (!target.matches(resultState, pos, world, preProcessing.ignore)) { + return@placeState PlaceResult.NoIntegrity( + pos, resultState, context, (target as? TargetState.State)?.blockState + ) + } else { + return@placeState null + } + } + + val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> + if (!place.rotateForPlace) { + return basePlaceResult + } + true + } ?: false + + run rotate@ { + if (!place.axisRotate) { + fakePlayer.rotation = checkedHit.targetRotation + simulatePlaceState()?.let { rotatedPlaceResult -> + return rotatedPlaceResult } + rot = fakePlayer.rotation + return@rotate } - val blockHit = checkedResult.blockResult ?: return@forEach - val hitBlock = blockState(blockHit.blockPos).block - val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks + fakePlayer.rotation = player.rotation + simulatePlaceState() ?: run { + rot = fakePlayer.rotation + return@rotate + } - val rotationRequest = if (place.axisRotate) { - lookInDirection(PlaceDirection.fromRotation(rot)) - } else lookAt(rot, 0.001) + if (!currentDirIsInvalid) return@rotate - val placeContext = PlaceContext( - eye, - blockHit, - RotationRequest(rotationRequest, rotation), - eye.distanceTo(blockHit.pos), - resultState, - blockState(blockHit.blockPos.offset(blockHit.side)), - player.inventory.selectedSlot, - context.blockPos, - target, - shouldSneak, - false, - currentDirIsInvalid - ) + PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> + fakePlayer.rotation = direction.rotation + when (val placeResult = simulatePlaceState()) { + is PlaceResult.BlockedByEntity -> { + return placeResult + } - val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) - if (target is TargetState.Stack && !target.itemStack.equal(currentHandStack)) { - acc.add(BuildResult.WrongStack(pos, placeContext, target.itemStack, inventory)) - return@forEach - } + is PlaceResult.NoIntegrity -> { + if (index != PlaceDirection.entries.lastIndex) return@direction + return placeResult + } - if (optimalStack.item != currentHandStack.item) { - acc.add(BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), currentHandStack, inventory)) - return@forEach + else -> { + rot = fakePlayer.rotation + return@rotate + } + } } + } - acc.add(PlaceResult.Place(pos, placeContext)) + val blockHit = checkedResult.blockResult ?: return null + val hitBlock = blockState(blockHit.blockPos).block + val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks + + val rotationRequest = if (place.axisRotate) { + lookInDirection(PlaceDirection.fromRotation(rot)) + } else lookAt(rot, 0.001) + + val placeContext = PlaceContext( + eye, + blockHit, + RotationRequest(rotationRequest, rotation), + eye.distanceTo(blockHit.pos), + resultState, + blockState(blockHit.blockPos.offset(blockHit.side)), + player.inventory.selectedSlot, + context.blockPos, + target, + shouldSneak, + false, + currentDirIsInvalid, + ) + + val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) + if (target is TargetState.Stack && !target.itemStack.equal(currentHandStack)) { + return BuildResult.WrongStack(pos, placeContext, target.itemStack, inventory) } + + if (optimalStack.item != currentHandStack.item) { + return BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), currentHandStack, inventory) + } + + return PlaceResult.Place(pos, placeContext) } - return acc + return null } private fun SafeContext.checkBreakResults( pos: BlockPos, eye: Vec3d, + preProcessing: PreProcessingInfo, breaking: BreakConfig, interact: InteractionConfig, rotation: RotationConfig, @@ -433,7 +652,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye, build.placing, interact, rotation, inventory) + val submerge = checkPlaceResults(pos, TargetState.Solid, eye, preProcessing, build.placing, interact, rotation, inventory) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -448,9 +667,9 @@ object BuildSimulator { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { - checkPlaceResults(liquidPos, TargetState.Solid, eye, build.placing, interact, rotation, inventory) + checkPlaceResults(liquidPos, TargetState.Solid, eye, preProcessing, build.placing, interact, rotation, inventory) } else { - checkBreakResults(liquidPos, eye, breaking, interact, rotation, inventory, build) + checkBreakResults(liquidPos, eye, preProcessing, breaking, interact, rotation, inventory, build) } acc.addAll(submerge) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index 3a8cea455..b42edf8c0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -25,7 +25,7 @@ import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.simulation.BuildSimulator.simulate -import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt index af2aaec11..dd86ea93b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt @@ -20,10 +20,11 @@ package com.lambda.interaction.construction.verify import net.minecraft.block.BlockState import net.minecraft.client.world.ClientWorld import net.minecraft.item.ItemStack +import net.minecraft.state.property.Property import net.minecraft.util.math.BlockPos interface StateMatcher { - fun matches(state: BlockState, pos: BlockPos, world: ClientWorld): Boolean + fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection> = emptySet()): Boolean fun getStack(world: ClientWorld, pos: BlockPos): ItemStack fun isAir(): Boolean } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index 7581a3c4a..bb706373c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -26,6 +26,7 @@ import net.minecraft.block.BlockState import net.minecraft.client.world.ClientWorld import net.minecraft.item.ItemStack import net.minecraft.item.Items +import net.minecraft.state.property.Property import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -38,7 +39,7 @@ sealed class TargetState(val type: Type) : StateMatcher { data object Air : TargetState(Type.AIR) { override fun toString() = "Air" - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = state.isAir override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = @@ -50,7 +51,7 @@ sealed class TargetState(val type: Type) : StateMatcher { data object Solid : TargetState(Type.SOLID) { override fun toString() = "Solid" - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = state.isSolidBlock(world, pos) override fun getStack(world: ClientWorld, pos: BlockPos) = @@ -64,7 +65,7 @@ sealed class TargetState(val type: Type) : StateMatcher { data class Support(val direction: Direction) : TargetState(Type.SUPPORT) { override fun toString() = "Support for ${direction.name}" - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = world.getBlockState(pos.offset(direction)).isSolidBlock(world, pos.offset(direction)) || state.isSolidBlock(world, pos) @@ -79,8 +80,8 @@ sealed class TargetState(val type: Type) : StateMatcher { data class State(val blockState: BlockState) : TargetState(Type.STATE) { override fun toString() = "State of $blockState" - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = - state.matches(blockState) + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = + state.matches(blockState, ignoredProperties) override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = blockState.block.getPickStack(world, pos, blockState) @@ -91,7 +92,7 @@ sealed class TargetState(val type: Type) : StateMatcher { data class Block(val block: net.minecraft.block.Block) : TargetState(Type.BLOCK) { override fun toString() = "Block of ${block.name.string.capitalize()}" - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = state.block == block override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = @@ -106,7 +107,7 @@ sealed class TargetState(val type: Type) : StateMatcher { private val block = itemStack.item.block - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = state.block == block override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 9a8b6aab9..df527bda3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -44,8 +44,9 @@ import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingBreaks import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending +import com.lambda.interaction.request.interacting.InteractionManager import com.lambda.interaction.request.placing.PlaceManager -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.brokenState @@ -231,7 +232,7 @@ object BreakManager : RequestHandler( * @see processRequest */ override fun SafeContext.handleRequest(request: BreakRequest) { - if (activeRequest != null || PlaceManager.activeThisTick || request.contexts.isEmpty()) return + if (activeRequest != null || PlaceManager.activeThisTick || InteractionManager.activeThisTick || request.contexts.isEmpty()) return activeRequest = request processRequest(request) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index e963b2120..320517672 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -23,7 +23,7 @@ import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig -import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import net.minecraft.entity.ItemEntity diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 184be5e44..2324f1bb6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -59,8 +59,7 @@ object BrokenBlockHandler { if (!info.broken) warn("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") else warn("${info::class.simpleName}'s item drop at ${info.context.expectedPos.toShortString()} timed out") - val awaitThenBreak = info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak - if (!info.broken && awaitThenBreak) { + if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) { world.setBlockState(info.context.expectedPos, info.context.checkedState) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt new file mode 100644 index 000000000..2a42873a2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.interacting + +import com.lambda.Lambda.mc +import com.lambda.config.groups.InteractionConfig +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.matches +import com.lambda.util.Communication.info +import com.lambda.util.Communication.warn +import com.lambda.util.collections.LimitedDecayQueue +import net.minecraft.block.BlockState +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos + +object InteractedBlockHandler { + val pendingInteractions = LimitedDecayQueue( + TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L + ) { + info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") + if (it.interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) { + mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + } + it.pendingInteractionsList.remove(it.context) + } + + init { + listen(priority = Int.MIN_VALUE) { event -> + pendingInteractions + .firstOrNull { it.context.expectedPos == event.pos } + ?.let { info -> + removePendingInteract(info) + + if (!matchesTargetState(event.pos, info.context.expectedState, event.newState)) + return@listen + + if (info.interact.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract) + with (info.context) { + checkedState.onUse(world, player, Hand.MAIN_HAND, result) + } + } + } + + listenUnsafe { + pendingInteractions.clear() + } + } + + fun addPendingInteract(info: InteractionInfo) { + pendingInteractions.add(info) + info.pendingInteractionsList.add(info.context) + } + + fun removePendingInteract(info: InteractionInfo) { + pendingInteractions.remove(info) + info.pendingInteractionsList.remove(info.context) + } + + fun setPendingConfigs(request: InteractionRequest) { + pendingInteractions.setSizeLimit(request.build.maxPendingInteractions) + pendingInteractions.setDecayTime(request.build.interactionTimeout * 50L) + } + + private fun matchesTargetState(pos: BlockPos, targetState: BlockState, newState: BlockState) = + if (targetState.matches(newState)) true + else { + this@InteractedBlockHandler.warn("Interaction at ${pos.toShortString()} was rejected with $newState instead of $targetState") + false + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt new file mode 100644 index 000000000..8d854478a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.interacting + +import com.lambda.config.groups.InteractionConfig +import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.construction.context.InteractContext + +data class InteractionInfo( + val context: InteractContext, + val pendingInteractionsList: MutableCollection, + val interact: InteractionConfig +) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt new file mode 100644 index 000000000..47d4ab9e5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.interacting + +import com.lambda.config.groups.InteractionConfig +import com.lambda.context.SafeContext +import com.lambda.event.EventFlow.post +import com.lambda.event.events.MovementEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.breaking.BreakManager +import com.lambda.interaction.request.interacting.InteractedBlockHandler.addPendingInteract +import com.lambda.interaction.request.interacting.InteractedBlockHandler.pendingInteractions +import com.lambda.interaction.request.interacting.InteractedBlockHandler.setPendingConfigs +import com.lambda.interaction.request.interacting.InteractionManager.activeRequest +import com.lambda.interaction.request.interacting.InteractionManager.processRequest +import com.lambda.interaction.request.placing.PlaceManager +import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket +import net.minecraft.util.Hand + +object InteractionManager : RequestHandler( + 0, + TickEvent.Pre, + TickEvent.Input.Pre, + TickEvent.Input.Post, + TickEvent.Player.Post, + onOpen = { activeRequest?.let { processRequest(it) } } +) { + private var activeRequest: InteractionRequest? = null + private var potentialInteractions = mutableListOf() + + private var interactionsThisTick = 0 + private var maxInteractionsThisTick = 0 + + init { + listen(priority = Int.MIN_VALUE) { + activeRequest = null + interactionsThisTick = 0 + potentialInteractions.clear() + } + + listen(priority = Int.MIN_VALUE) { + if (potentialInteractions.isNotEmpty()) { + it.input.sneaking = false + } + } + } + + override fun SafeContext.handleRequest(request: InteractionRequest) { + if (activeRequest != null || BreakManager.activeThisTick || PlaceManager.activeThisTick) return + + activeRequest = request + processRequest(request) + if (interactionsThisTick > 0) activeThisTick = true + } + + fun SafeContext.processRequest(request: InteractionRequest) { + pendingInteractions.cleanUp() + + if (request.fresh) populateFrom(request) + + if (player.isSneaking) return + + val iterator = potentialInteractions.iterator() + while (iterator.hasNext()) { + if (interactionsThisTick + 1 > maxInteractionsThisTick) break + val interact = request.interact + val ctx = iterator.next() + + if (!ctx.requestDependencies(request)) return + + if (interact.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) { + addPendingInteract(InteractionInfo(ctx, request.pendingInteractionsList, interact)) + } + if (interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) { + interaction.interactBlock(player, Hand.MAIN_HAND, ctx.result) + } else { + interaction.sendSequencedPacket(world) { sequence -> + PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, ctx.result, sequence) + } + } + request.onInteract?.invoke(ctx.expectedPos) + interactionsThisTick++ + iterator.remove() + } + } + + private fun populateFrom(request: InteractionRequest) { + setPendingConfigs(request) + potentialInteractions = request.contexts.toMutableList() + + val pendingLimit = (request.build.maxPendingInteractions - pendingPlacements.size).coerceAtLeast(0) + maxInteractionsThisTick = (request.build.interactionsPerTick.coerceAtMost(pendingLimit)) + } + + override fun preEvent() = UpdateManagerEvent.Interact.post() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt new file mode 100644 index 000000000..ccb991481 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.interaction.request.interacting + +import com.lambda.Lambda.mc +import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.request.Request +import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.util.BlockUtils.matches +import net.minecraft.util.math.BlockPos + +data class InteractionRequest( + val contexts: Collection, + val onInteract: ((BlockPos) -> Unit)?, + val pendingInteractionsList: MutableCollection, + val interact: InteractionConfig, + val build: BuildConfig, + val hotbar: HotbarConfig, + val rotation: RotationConfig, + private val prio: Int = 0 +) : Request(prio, interact) { + override val done: Boolean + get() = contexts.all { mc.world?.getBlockState(it.expectedPos)?.matches(it.expectedState) == true } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 66571b7db..d2b1a56d4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -19,11 +19,10 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig import com.lambda.event.Event -import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig abstract class PlaceConfig( - priority: Priority + priority: Int ) : RequestConfig(priority) { abstract val rotateForPlace: Boolean abstract val airPlace: AirPlaceMode diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 599eec354..c012e30d3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -29,6 +29,7 @@ import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager +import com.lambda.interaction.request.interacting.InteractionManager import com.lambda.interaction.request.placing.PlaceManager.activeRequest import com.lambda.interaction.request.placing.PlaceManager.processRequest import com.lambda.interaction.request.placing.PlacedBlockHandler.addPendingPlace @@ -92,6 +93,7 @@ object PlaceManager : RequestHandler( listen(priority = Int.MIN_VALUE) { activeRequest = null placementsThisTick = 0 + potentialPlacements.clear() } listen(priority = Int.MIN_VALUE) { @@ -111,7 +113,7 @@ object PlaceManager : RequestHandler( * @see processRequest */ override fun SafeContext.handleRequest(request: PlaceRequest) { - if (activeRequest != null || BreakManager.activeThisTick) return + if (activeRequest != null || BreakManager.activeThisTick || InteractionManager.activeThisTick) return activeRequest = request processRequest(request) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index 7a133d821..e8023d774 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -23,7 +23,7 @@ import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig -import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState @@ -33,7 +33,7 @@ data class PlaceRequest( val rotation: RotationConfig, val hotbar: HotbarConfig, val pendingInteractions: MutableCollection, - val prio: Priority = 0, + private val prio: Priority = 0, val onPlace: () -> Unit ) : Request(prio, build.placing) { override val done: Boolean diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt index 1111cd27f..0263624d6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -18,28 +18,28 @@ package com.lambda.interaction.request.placing import com.lambda.Lambda.mc -import com.lambda.context.SafeContext import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.placing.PlaceManager.placeSound +import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.item +import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue -import net.minecraft.block.BlockState import net.minecraft.item.BlockItem -import net.minecraft.util.math.BlockPos object PlacedBlockHandler { val pendingPlacements = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") - mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + if (it.placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) { + mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + } it.pendingInteractionsList.remove(it.context) } @@ -50,16 +50,18 @@ object PlacedBlockHandler { ?.let { info -> removePendingPlace(info) - // return if the block wasn't placed - if (!matchesTargetState(event.pos, info.context.targetState, event.newState)) - return@listen + // return if the block wasn't placed properly + if (!event.newState.matches(info.context.expectedState)) { + this@PlacedBlockHandler.warn( + "Place at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.expectedState}" + ) + } if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) with (info.context) { placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos) } info.onPlace() - return@listen } } @@ -91,16 +93,4 @@ object PlacedBlockHandler { pendingPlacements.setSizeLimit(request.build.placing.maxPendingPlacements) pendingPlacements.setDecayTime(request.build.interactionTimeout * 50L) } - - /** - * @return if the [targetState] matches the [newState] - * - * @see TargetState - */ - private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) = - if (targetState.matches(newState, pos, world)) true - else { - this@PlacedBlockHandler.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState") - false - } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt similarity index 99% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt index 9b6ba8753..f54c94224 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation +package com.lambda.interaction.request.rotating import com.lambda.Lambda.mc import com.lambda.threading.runSafe diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt similarity index 98% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt index a61110cf1..80550629c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation +package com.lambda.interaction.request.rotating import com.lambda.event.Event import com.lambda.event.events.TickEvent diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt similarity index 98% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt index 15e799cb4..3bdd734f4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation +package com.lambda.interaction.request.rotating import com.lambda.Lambda.mc import com.lambda.context.SafeContext @@ -30,8 +30,8 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler -import com.lambda.interaction.request.rotation.Rotation.Companion.slerp -import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotating.Rotation.Companion.slerp +import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.modules.client.Baritone import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt similarity index 95% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt index 2eafbd919..54b3136c2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation +package com.lambda.interaction.request.rotating /** * @property Silent Spoofing server-side rotation. diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt similarity index 93% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt index 50e96661d..e931dcd71 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation +package com.lambda.interaction.request.rotating import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request -import com.lambda.interaction.request.rotation.visibilty.RotationTarget +import com.lambda.interaction.request.rotating.visibilty.RotationTarget import com.lambda.threading.runSafe data class RotationRequest( diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PlaceDirection.kt similarity index 97% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PlaceDirection.kt index a6cfb2c2d..6b3e70210 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PlaceDirection.kt @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import net.minecraft.entity.Entity import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt similarity index 87% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt index 9a84ea68f..23f403103 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty -import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotating.Rotation.Companion.dist +import com.lambda.interaction.request.rotating.RotationManager import com.lambda.util.math.distSq import com.lambda.util.math.times diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RequestedHit.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RequestedHit.kt index 8cf300e29..0a8b5910e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RequestedHit.kt @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.ALL_SIDES +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.RotationManager +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.ALL_SIDES import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.RayCastUtils.blockResult diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RequestedHitDsl.kt similarity index 95% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RequestedHitDsl.kt index 2638b787b..0c6230b81 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RequestedHitDsl.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty import net.minecraft.entity.LivingEntity import net.minecraft.util.math.BlockPos diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt similarity index 82% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt index 5b68132fe..103f58e26 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt @@ -15,14 +15,14 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty import com.lambda.context.SafeContext -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.dist +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationManager +import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.collections.updatableLazy diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt similarity index 92% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt index 241010b3c..620c76972 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt @@ -15,16 +15,16 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.verify.SurfaceScan -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.ALL_SIDES -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.findRotation +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.dist +import com.lambda.interaction.request.rotating.RotationManager +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.ALL_SIDES +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.findRotation import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.extension.rotation import com.lambda.util.world.raycast.InteractionMask diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt similarity index 97% rename from common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt index 6c3ed439a..dd6eae3d1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt @@ -15,15 +15,15 @@ * along with this program. If not, see . */ -package com.lambda.interaction.request.rotation.visibilty +package com.lambda.interaction.request.rotating.visibilty import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.RotationManager import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.extension.component6 import com.lambda.util.math.distSq @@ -171,7 +171,6 @@ object VisibilityChecker { check: (Direction, Vec3d) -> Unit, ) { excludedSides.forEach { side -> - if (excludedSides.isNotEmpty() && side !in excludedSides) return@forEach val (minX, minY, minZ, maxX, maxY, maxZ) = box.contract(TaskFlowModule.shrinkFactor).bounds(side) val stepX = (maxX - minX) / resolution val stepY = (maxY - minY) / resolution diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt index d06862dc0..9af607c8d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt @@ -20,8 +20,8 @@ package com.lambda.module.modules.combat import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.extension.rotation diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index a39158be7..ffdab2763 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -30,10 +30,10 @@ import com.lambda.graphics.gl.Matrices.buildWorldProjection import com.lambda.graphics.gl.Matrices.withVertexTransform import com.lambda.graphics.renderer.gui.FontRenderer import com.lambda.graphics.renderer.gui.FontRenderer.drawString -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces -import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.RotationManager +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 636b6255f..cc9938f58 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -27,8 +27,8 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ContainerManager.transfer import com.lambda.interaction.material.container.containers.MainHandContainer -import com.lambda.interaction.request.rotation.RotationManager -import com.lambda.interaction.request.rotation.visibilty.lookAtEntity +import com.lambda.interaction.request.rotating.RotationManager +import com.lambda.interaction.request.rotating.visibilty.lookAtEntity import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt index f472a02b5..9f64699a8 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt @@ -40,7 +40,7 @@ object PropertyPrinter : Module( field.isAccessible = true val block = field.get(null) if (!Block::class.java.isAssignableFrom(block::class.java)) return@blocks - if ((block as Block).defaultState.properties.contains(property.key)) { + if (property.key in (block as Block).defaultState.properties) { file.appendText(" $block\n") } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 974876f2a..a8e7fd462 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -21,11 +21,11 @@ import com.lambda.context.SafeContext import com.lambda.event.events.ClientEvent import com.lambda.event.events.MovementEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationMode -import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationManager.onRotate +import com.lambda.interaction.request.rotating.RotationMode +import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.NamedEnum diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt index 4c118ce55..66a7c492d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt @@ -20,7 +20,7 @@ package com.lambda.module.modules.movement import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo import com.lambda.module.Module import com.lambda.module.modules.combat.KillAura import com.lambda.module.tag.ModuleTag diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 53bbd0c5b..720d75e13 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -18,13 +18,16 @@ package com.lambda.module.modules.player import com.lambda.Lambda.mc -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.event.events.* +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.MovementEvent +import com.lambda.event.events.PlayerEvent +import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationMode -import com.lambda.interaction.request.rotation.visibilty.lookAtHit +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationManager.onRotate +import com.lambda.interaction.request.rotating.RotationMode +import com.lambda.interaction.request.rotating.visibilty.lookAtHit import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.extension.partialTicks diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryMove.kt b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryMove.kt index ad4b2eb9c..be04ba4aa 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryMove.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryMove.kt @@ -20,11 +20,11 @@ package com.lambda.module.modules.player import com.lambda.event.events.MovementEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.gui.LambdaScreen -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.RotationConfig -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationMode -import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationManager.onRotate +import com.lambda.interaction.request.rotating.RotationMode +import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyboardUtils.isKeyPressed @@ -36,10 +36,22 @@ import net.minecraft.client.gui.screen.ChatScreen import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.ingame.AnvilScreen import net.minecraft.client.gui.screen.ingame.CommandBlockScreen -import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen import net.minecraft.client.gui.screen.ingame.SignEditScreen -import net.minecraft.client.network.ClientPlayerEntity -import org.lwjgl.glfw.GLFW.* +import org.lwjgl.glfw.GLFW.GLFW_KEY_A +import org.lwjgl.glfw.GLFW.GLFW_KEY_D +import org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN +import org.lwjgl.glfw.GLFW.GLFW_KEY_KP_2 +import org.lwjgl.glfw.GLFW.GLFW_KEY_KP_4 +import org.lwjgl.glfw.GLFW.GLFW_KEY_KP_6 +import org.lwjgl.glfw.GLFW.GLFW_KEY_KP_8 +import org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT +import org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL +import org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT +import org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT +import org.lwjgl.glfw.GLFW.GLFW_KEY_S +import org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE +import org.lwjgl.glfw.GLFW.GLFW_KEY_UP +import org.lwjgl.glfw.GLFW.GLFW_KEY_W object InventoryMove : Module( name = "InventoryMove", diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 7e743e4f9..006981ad0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -17,19 +17,26 @@ package com.lambda.module.modules.player -import com.google.gson.* +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.google.gson.JsonSerializationContext +import com.google.gson.JsonSerializer import com.lambda.brigadier.CommandResult -import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.core.TimerManager import com.lambda.event.EventFlow.lambdaScope import com.lambda.event.events.KeyboardEvent import com.lambda.event.events.MovementEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationMode -import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.RotationConfig +import com.lambda.interaction.request.rotating.RotationManager.onRotate +import com.lambda.interaction.request.rotating.RotationMode +import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.player.Replay.InputAction.Companion.toAction @@ -45,7 +52,14 @@ import com.lambda.util.Formatting.getTime import com.lambda.util.KeyCode import com.lambda.util.StringUtils.sanitizeForFilename import com.lambda.util.extension.rotation -import com.lambda.util.text.* +import com.lambda.util.text.ClickEvents +import com.lambda.util.text.HoverEvents +import com.lambda.util.text.TextBuilder +import com.lambda.util.text.buildText +import com.lambda.util.text.clickEvent +import com.lambda.util.text.color +import com.lambda.util.text.hoverEvent +import com.lambda.util.text.literal import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import net.minecraft.client.input.Input diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index 2d1a4d2b0..6cd4697ed 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -30,18 +30,18 @@ import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.interaction.blockplace.PlaceFinder.Companion.buildPlaceInfo import com.lambda.interaction.blockplace.PlaceInfo import com.lambda.interaction.blockplace.PlaceInteraction.placeBlock -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifference -import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.request.rotation.Rotation.Companion.wrap -import com.lambda.interaction.request.rotation.RotationManager.activeRotation -import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces -import com.lambda.interaction.request.rotation.visibilty.blockHit -import com.lambda.interaction.request.rotation.visibilty.lookAtHit +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.angleDifference +import com.lambda.interaction.request.rotating.Rotation.Companion.dist +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.Rotation.Companion.wrap +import com.lambda.interaction.request.rotating.RotationManager.activeRotation +import com.lambda.interaction.request.rotating.RotationManager.onRotate +import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotating.visibilty.VisibilityChecker.scanSurfaces +import com.lambda.interaction.request.rotating.visibilty.blockHit +import com.lambda.interaction.request.rotating.visibilty.lookAtHit import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.client.TaskFlowModule diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt index 6484cff55..982228422 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt @@ -24,7 +24,6 @@ import com.lambda.event.events.PlayerEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.gl.GlStateUtils.withBlendFunc @@ -33,20 +32,21 @@ import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.buildWorldProjection import com.lambda.graphics.gl.Matrices.withVertexTransform import com.lambda.graphics.pipeline.VertexBuilder +import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.shader.Shader.Companion.shader -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.client.GuiSettings.colorSpeed import com.lambda.module.tag.ModuleTag import com.lambda.util.extension.partialTicks +import com.lambda.util.math.DOWN import com.lambda.util.math.MathUtils.random import com.lambda.util.math.UP -import com.lambda.util.math.DOWN -import com.lambda.util.math.plus -import com.lambda.util.math.times import com.lambda.util.math.lerp import com.lambda.util.math.multAlpha +import com.lambda.util.math.plus +import com.lambda.util.math.times import com.lambda.util.math.transform import com.lambda.util.player.MovementUtils.moveDelta import com.lambda.util.world.raycast.InteractionMask diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 02bff4d08..42fb37aac 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -35,6 +35,7 @@ import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.Drawable +import com.lambda.interaction.construction.result.InteractResult import com.lambda.interaction.construction.result.Navigable import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.result.Resolvable @@ -45,8 +46,9 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer import com.lambda.interaction.request.breaking.BreakRequest.Companion.breakRequest import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.interaction.request.interacting.InteractionRequest import com.lambda.interaction.request.placing.PlaceRequest -import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -144,6 +146,7 @@ class BuildTask @Ta5kBuilder constructor( if (build.breaking.breaksPerTick > 1) { breakResults .filter { it.context.instantBreak } + .distinctBy { it.blockPos } .take(emptyPendingInteractionSlots) .let { instantBreakResults -> requestContexts.addAll(instantBreakResults.map { it.context }) @@ -175,6 +178,25 @@ class BuildTask @Ta5kBuilder constructor( ) { placements++ } ) } + is InteractResult.Interact -> { + val interactResults = resultsNotBlocked + .filterIsInstance() + .distinctBy { it.blockPos } + .take(emptyPendingInteractionSlots) + .map { it.context } + + interact.request( + InteractionRequest( + interactResults, + null, + pendingInteractions, + interact, + build, + hotbar, + rotation + ) + ) + } } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index ca363f441..808756401 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -18,11 +18,11 @@ package com.lambda.task.tasks import com.lambda.config.groups.InteractionConfig -import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.event.events.InventoryEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.rotation.visibilty.lookAtBlock +import com.lambda.interaction.request.rotating.visibilty.lookAtBlock import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.world.raycast.RayCastUtils.blockResult diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index a568a27ee..100e58e36 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -27,7 +27,7 @@ import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.build diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index d4e31e5e2..f3f15cc0c 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -94,6 +94,7 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.item.SwordItem import net.minecraft.registry.tag.FluidTags +import net.minecraft.state.property.Property import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.EightWayDirection @@ -240,9 +241,9 @@ object BlockUtils { fun SafeContext.fluidState(pos: BlockPos): FluidState = world.getFluidState(pos) fun SafeContext.blockEntity(pos: BlockPos) = world.getBlockEntity(pos) - fun BlockState.matches(state: BlockState) = + fun BlockState.matches(state: BlockState, ignoredProperties: Collection> = emptySet()) = this.block == state.block && this.properties.all { - /*it in TaskFlowModule.defaultIgnoreTags ||*/ this[it] == state[it] + this[it] == state[it] || it in ignoredProperties } fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos, breakThreshold: Float): Boolean { diff --git a/common/src/main/kotlin/com/lambda/util/extension/Entity.kt b/common/src/main/kotlin/com/lambda/util/extension/Entity.kt index 7523da8a9..b85804f1a 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/Entity.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/Entity.kt @@ -17,7 +17,7 @@ package com.lambda.util.extension -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.util.math.Vec3d diff --git a/common/src/main/kotlin/com/lambda/util/math/Linear.kt b/common/src/main/kotlin/com/lambda/util/math/Linear.kt index def44e228..efcc90e56 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Linear.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Linear.kt @@ -17,7 +17,7 @@ package com.lambda.util.math -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d import java.awt.Color diff --git a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt index d3eeb33d6..ddb53cc9f 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -18,7 +18,7 @@ package com.lambda.util.player import com.lambda.context.SafeContext -import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotating.RotationManager import com.lambda.util.math.MathUtils.toDegree import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.MathUtils.toRadian diff --git a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt index 13489d4b7..6235b670d 100644 --- a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt +++ b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt @@ -19,7 +19,7 @@ package com.lambda.util.player.prediction import com.lambda.Lambda.mc import com.lambda.context.SafeContext -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import com.lambda.module.modules.movement.SafeWalk.isNearLedge import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState diff --git a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt index 76950ff95..dbc97d8df 100644 --- a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt +++ b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt @@ -17,7 +17,7 @@ package com.lambda.util.player.prediction -import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotating.Rotation import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt index 0a241bacc..e007a7e60 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt @@ -19,8 +19,7 @@ package com.lambda.util.world.raycast import com.lambda.Lambda.mc import com.lambda.context.SafeContext -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.threading.runSafe +import com.lambda.interaction.request.rotating.Rotation import com.lambda.util.math.distSq import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.Entity diff --git a/common/src/test/kotlin/PlaceDirectionTest.kt b/common/src/test/kotlin/PlaceDirectionTest.kt index 51415defb..9f76d1b9a 100644 --- a/common/src/test/kotlin/PlaceDirectionTest.kt +++ b/common/src/test/kotlin/PlaceDirectionTest.kt @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.visibilty.PlaceDirection +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.visibilty.PlaceDirection import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -265,7 +265,7 @@ class PlaceDirectionTest { val secondSnapped = nextDirection.snapToArea(firstSnapped) // Verify that the yaw and pitch are snapped to the boundary - assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + assertEquals(nextDirection, PlaceDirection.fromRotation(secondSnapped)) } @Test @@ -278,7 +278,7 @@ class PlaceDirectionTest { val secondSnapped = nextDirection.snapToArea(firstSnapped) // Verify that the yaw and pitch are snapped to the boundary - assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + assertEquals(nextDirection, PlaceDirection.fromRotation(secondSnapped)) } @Test @@ -291,7 +291,7 @@ class PlaceDirectionTest { val secondSnapped = nextDirection.snapToArea(firstSnapped) // Verify that the yaw and pitch are snapped to the boundary - assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + assertEquals(nextDirection, PlaceDirection.fromRotation(secondSnapped)) } @Test @@ -304,7 +304,7 @@ class PlaceDirectionTest { val secondSnapped = nextDirection.snapToArea(firstSnapped) // Verify that the yaw and pitch are snapped to the boundary - assertEquals(direction, PlaceDirection.fromRotation(secondSnapped)) + assertEquals(nextDirection, PlaceDirection.fromRotation(secondSnapped)) } // Tests for when rotation is already in the area diff --git a/common/src/test/kotlin/RotationTest.kt b/common/src/test/kotlin/RotationTest.kt index 2ffdff1b7..a74d2dadf 100644 --- a/common/src/test/kotlin/RotationTest.kt +++ b/common/src/test/kotlin/RotationTest.kt @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifference -import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.Rotation.Companion.lerp -import com.lambda.interaction.request.rotation.Rotation.Companion.slerp -import com.lambda.interaction.request.rotation.Rotation.Companion.wrap +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.angleDifference +import com.lambda.interaction.request.rotating.Rotation.Companion.dist +import com.lambda.interaction.request.rotating.Rotation.Companion.lerp +import com.lambda.interaction.request.rotating.Rotation.Companion.slerp +import com.lambda.interaction.request.rotating.Rotation.Companion.wrap import kotlin.math.abs import kotlin.test.Test import kotlin.test.assertEquals From 6d1ef93e399b9010c5e1015104c1f1b0296a4ad3 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 3 Jul 2025 13:43:35 +0100 Subject: [PATCH 245/364] build sim function signature cleanup and proper interaction related naming --- ...teractContext.kt => InteractionContext.kt} | 4 +- .../construction/result/InteractResult.kt | 4 +- .../construction/simulation/BuildSimulator.kt | 66 ++++++++++--------- .../request/interacting/InteractionInfo.kt | 4 +- .../request/interacting/InteractionManager.kt | 4 +- .../request/interacting/InteractionRequest.kt | 4 +- 6 files changed, 45 insertions(+), 41 deletions(-) rename common/src/main/kotlin/com/lambda/interaction/construction/context/{InteractContext.kt => InteractionContext.kt} (97%) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt similarity index 97% rename from common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt index 9c5cd1165..2b071d9bf 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt @@ -34,7 +34,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import java.awt.Color -class InteractContext( +class InteractionContext( override val pov: Vec3d, override val result: BlockHitResult, override val rotation: RotationRequest, @@ -51,7 +51,7 @@ class InteractContext( override fun compareTo(other: BuildContext) = when { - other is InteractContext -> compareBy { + other is InteractionContext -> compareBy { BlockUtils.fluids.indexOf(it.checkedState.fluidState.fluid) }.thenByDescending { it.checkedState.fluidState.level diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt index 4847128b4..bbfcd5137 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/InteractResult.kt @@ -18,13 +18,13 @@ package com.lambda.interaction.construction.result import com.lambda.context.SafeContext -import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.construction.context.InteractionContext import net.minecraft.util.math.BlockPos sealed class InteractResult : BuildResult() { data class Interact( override val blockPos: BlockPos, - override val context: InteractContext + override val context: InteractionContext ) : Contextual, Drawable, InteractResult() { override val rank = Rank.INTERACT_SUCCESS diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 65adda4e7..c25ee4254 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -23,7 +23,7 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.construction.context.InteractionContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.processing.PreProcessingInfo import com.lambda.interaction.construction.processing.ProcessorRegistry.getProcessingInfo @@ -96,11 +96,11 @@ object BuildSimulator { ) = runSafe { structure.entries.flatMap { (pos, target) -> val preProcessing = target.getProcessingInfo() - checkRequirements(pos, target, eye, preProcessing, build, interact, rotation, inventory).let { + checkRequirements(pos, eye, preProcessing, target, build, interact, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } - checkPlaceResults(pos, target, eye, preProcessing, build.placing, interact, rotation, inventory).let { + checkPlaceResults(pos, eye, preProcessing, target, build.placing, interact, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } @@ -115,9 +115,9 @@ object BuildSimulator { private fun SafeContext.checkRequirements( pos: BlockPos, - target: TargetState, eye: Vec3d, preProcessing: PreProcessingInfo, + target: TargetState, build: BuildConfig, interact: InteractionConfig, rotation: RotationConfig, @@ -165,8 +165,12 @@ object BuildSimulator { /* the state requires post-processing */ if (target.matches(state, pos, world, ignoredProperties = preProcessing.ignore)) { - acc.addAll(checkPostProcessResults(target, state, pos, eye, preProcessing, interact, build.placing, rotation, inventory)) - if (acc.isNotEmpty()) return acc + checkPostProcessResults(pos, eye, preProcessing, state, target, interact, build.placing, rotation, inventory).let { postProcessResults -> + if (postProcessResults.isNotEmpty()) { + acc.addAll(postProcessResults) + return acc + } + } } /* block is unbreakable, so it cant be broken or replaced */ @@ -180,9 +184,9 @@ object BuildSimulator { private fun SafeContext.checkPlaceResults( pos: BlockPos, - target: TargetState, eye: Vec3d, preProcessing: PreProcessingInfo, + target: TargetState, place: PlaceConfig, interact: InteractionConfig, rotation: RotationConfig, @@ -265,7 +269,7 @@ object BuildSimulator { return@forEach } - checkPlaceOn(pos, validHits, target, eye, preProcessing, place, rotation, interact, inventory)?.let { placeResult -> + checkPlaceOn(pos, validHits, eye, preProcessing, target, place, rotation, interact, inventory)?.let { placeResult -> acc.add(placeResult) } } @@ -274,11 +278,11 @@ object BuildSimulator { } private fun SafeContext.checkPostProcessResults( - targetState: TargetState, - state: BlockState, pos: BlockPos, eye: Vec3d, preProcessing: PreProcessingInfo, + state: BlockState, + targetState: TargetState, interact: InteractionConfig, place: PlaceConfig, rotation: RotationConfig, @@ -293,12 +297,12 @@ object BuildSimulator { interactWithBlock( pos, state, - expectedState, - targetState, eye, side, - preProcessing, item ?: player.inventory.mainHandStack.item, + expectedState, + targetState, + preProcessing, placing, interact, place, @@ -334,12 +338,12 @@ object BuildSimulator { private fun SafeContext.interactWithBlock( pos: BlockPos, state: BlockState, - expectedState: BlockState, - targetState: TargetState, eye: Vec3d, sides: Set?, - preProcessing: PreProcessingInfo, item: Item, + expectedState: BlockState, + targetState: TargetState, + preProcessing: PreProcessingInfo, placing: Boolean, interact: InteractionConfig, place: PlaceConfig, @@ -407,18 +411,18 @@ object BuildSimulator { } } - return if (placing) checkPlaceOn(pos, validHits, targetState, eye, preProcessing, place, rotation, interact, inventory) - else checkInteractOn(pos, validHits, state, expectedState, targetState, item, eye, rotation, interact, inventory) + return if (placing) checkPlaceOn(pos, validHits, eye, preProcessing, targetState, place, rotation, interact, inventory) + else checkInteractOn(pos, eye, item, validHits, expectedState, targetState, state, rotation, interact, inventory) } private fun SafeContext.checkInteractOn( pos: BlockPos, + eye: Vec3d, + item: Item, validHits: MutableList, - currentState: BlockState, expectedState: BlockState, targetState: TargetState, - item: Item, - eye: Vec3d, + currentState: BlockState, rotation: RotationConfig, interact: InteractionConfig, inventory: InventoryConfig @@ -426,7 +430,7 @@ object BuildSimulator { interact.pointSelection.select(validHits)?.let { checkedHit -> val checkedResult = checkedHit.hit val rotationTarget = lookAt(checkedHit.targetRotation, 0.001) - val context = InteractContext( + val context = InteractionContext( eye, checkedResult.blockResult ?: return null, RotationRequest(rotationTarget, rotation), @@ -461,16 +465,16 @@ object BuildSimulator { private fun SafeContext.checkPlaceOn( pos: BlockPos, validHits: MutableList, - target: TargetState, eye: Vec3d, preProcessing: PreProcessingInfo, + targetState: TargetState, place: PlaceConfig, rotation: RotationConfig, interact: InteractionConfig, inventory: InventoryConfig ): BuildResult? { interact.pointSelection.select(validHits)?.let { checkedHit -> - val optimalStack = target.getStack(world, pos) + val optimalStack = targetState.getStack(world, pos) // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { @@ -528,9 +532,9 @@ object BuildSimulator { resultState = blockItem.getPlacementState(context) ?: return@placeState PlaceResult.BlockedByEntity(pos) - if (!target.matches(resultState, pos, world, preProcessing.ignore)) { + if (!targetState.matches(resultState, pos, world, preProcessing.ignore)) { return@placeState PlaceResult.NoIntegrity( - pos, resultState, context, (target as? TargetState.State)?.blockState + pos, resultState, context, (targetState as? TargetState.State)?.blockState ) } else { return@placeState null @@ -599,15 +603,15 @@ object BuildSimulator { blockState(blockHit.blockPos.offset(blockHit.side)), player.inventory.selectedSlot, context.blockPos, - target, + targetState, shouldSneak, false, currentDirIsInvalid, ) val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) - if (target is TargetState.Stack && !target.itemStack.equal(currentHandStack)) { - return BuildResult.WrongStack(pos, placeContext, target.itemStack, inventory) + if (targetState is TargetState.Stack && !targetState.itemStack.equal(currentHandStack)) { + return BuildResult.WrongStack(pos, placeContext, targetState.itemStack, inventory) } if (optimalStack.item != currentHandStack.item) { @@ -652,7 +656,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye, preProcessing, build.placing, interact, rotation, inventory) + val submerge = checkPlaceResults(pos, eye, preProcessing, TargetState.Solid, build.placing, interact, rotation, inventory) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -667,7 +671,7 @@ object BuildSimulator { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { liquidPos -> val submerge = if (blockState(liquidPos).isReplaceable) { - checkPlaceResults(liquidPos, TargetState.Solid, eye, preProcessing, build.placing, interact, rotation, inventory) + checkPlaceResults(liquidPos, eye, preProcessing, TargetState.Solid, build.placing, interact, rotation, inventory) } else { checkBreakResults(liquidPos, eye, preProcessing, breaking, interact, rotation, inventory, build) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt index 8d854478a..28a2e0e5c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt @@ -19,10 +19,10 @@ package com.lambda.interaction.request.interacting import com.lambda.config.groups.InteractionConfig import com.lambda.interaction.construction.context.BuildContext -import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.construction.context.InteractionContext data class InteractionInfo( - val context: InteractContext, + val context: InteractionContext, val pendingInteractionsList: MutableCollection, val interact: InteractionConfig ) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt index 47d4ab9e5..c0b34cf16 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -24,7 +24,7 @@ import com.lambda.event.events.MovementEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.construction.context.InteractionContext import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.interacting.InteractedBlockHandler.addPendingInteract @@ -46,7 +46,7 @@ object InteractionManager : RequestHandler( onOpen = { activeRequest?.let { processRequest(it) } } ) { private var activeRequest: InteractionRequest? = null - private var potentialInteractions = mutableListOf() + private var potentialInteractions = mutableListOf() private var interactionsThisTick = 0 private var maxInteractionsThisTick = 0 diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt index ccb991481..cbec8c8ad 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt @@ -21,7 +21,7 @@ import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.interaction.construction.context.BuildContext -import com.lambda.interaction.construction.context.InteractContext +import com.lambda.interaction.construction.context.InteractionContext import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotating.RotationConfig @@ -29,7 +29,7 @@ import com.lambda.util.BlockUtils.matches import net.minecraft.util.math.BlockPos data class InteractionRequest( - val contexts: Collection, + val contexts: Collection, val onInteract: ((BlockPos) -> Unit)?, val pendingInteractionsList: MutableCollection, val interact: InteractionConfig, From ffd345f3c5cf8dcc8ed95cbeff5576ee8b48cad4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 3 Jul 2025 15:02:53 +0100 Subject: [PATCH 246/364] use isEmpty and isNotEmpty over isAir --- .../construction/simulation/BuildSimulator.kt | 8 +++++--- .../interaction/construction/verify/StateMatcher.kt | 2 +- .../interaction/construction/verify/TargetState.kt | 13 +++++++------ .../interaction/request/breaking/BreakManager.kt | 13 +++++++------ .../request/breaking/BrokenBlockHandler.kt | 11 ++++++----- .../src/main/kotlin/com/lambda/util/BlockUtils.kt | 8 +++++--- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index c25ee4254..ffac5ea1e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -58,6 +58,8 @@ import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.BlockUtils.instantBreakable +import com.lambda.util.BlockUtils.isEmpty +import com.lambda.util.BlockUtils.isNotEmpty import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal @@ -195,10 +197,10 @@ object BuildSimulator { val acc = mutableSetOf() val targetPosState = blockState(pos) - if (target.isAir() || !targetPosState.isReplaceable) return acc + if (target.isEmpty() || !targetPosState.isReplaceable) return acc preProcessing.sides.forEach { neighbor -> - val hitPos = if (!place.airPlace.isEnabled() && (targetPosState.isAir || targetPosState.isLiquid)) + val hitPos = if (!place.airPlace.isEnabled() && targetPosState.isEmpty) pos.offset(neighbor) else pos val hitSide = neighbor.opposite @@ -638,7 +640,7 @@ object BuildSimulator { val state = blockState(pos) /* is a block that will be destroyed by breaking adjacent blocks */ - if (breaking.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (breaking.breakWeakBlocks && state.block.hardness == 0f && state.isNotEmpty) { acc.add(BuildResult.Ignored(pos)) return acc } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt index dd86ea93b..5b0105cf6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt @@ -26,5 +26,5 @@ import net.minecraft.util.math.BlockPos interface StateMatcher { fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection> = emptySet()): Boolean fun getStack(world: ClientWorld, pos: BlockPos): ItemStack - fun isAir(): Boolean + fun isEmpty(): Boolean } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index bb706373c..3eb54259a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.construction.verify import com.lambda.interaction.material.container.ContainerManager.findDisposable import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.BlockUtils.matches import com.lambda.util.StringUtils.capitalize import com.lambda.util.item.ItemUtils.block @@ -45,7 +46,7 @@ sealed class TargetState(val type: Type) : StateMatcher { override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = ItemStack.EMPTY - override fun isAir() = true + override fun isEmpty() = true } data object Solid : TargetState(Type.SOLID) { @@ -59,7 +60,7 @@ sealed class TargetState(val type: Type) : StateMatcher { it.item.block in TaskFlowModule.inventory.disposables } ?: ItemStack(Items.NETHERRACK) - override fun isAir() = false + override fun isEmpty() = false } data class Support(val direction: Direction) : TargetState(Type.SUPPORT) { @@ -74,7 +75,7 @@ sealed class TargetState(val type: Type) : StateMatcher { it.item.block in TaskFlowModule.inventory.disposables } ?: ItemStack(Items.NETHERRACK) - override fun isAir() = false + override fun isEmpty() = false } data class State(val blockState: BlockState) : TargetState(Type.STATE) { @@ -86,7 +87,7 @@ sealed class TargetState(val type: Type) : StateMatcher { override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = blockState.block.getPickStack(world, pos, blockState) - override fun isAir() = blockState.isAir + override fun isEmpty() = blockState.isEmpty } data class Block(val block: net.minecraft.block.Block) : TargetState(Type.BLOCK) { @@ -98,7 +99,7 @@ sealed class TargetState(val type: Type) : StateMatcher { override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = block.getPickStack(world, pos, block.defaultState) - override fun isAir() = block.defaultState.isAir + override fun isEmpty() = block.defaultState.isEmpty } data class Stack(val itemStack: ItemStack) : TargetState(Type.STACK) { @@ -113,6 +114,6 @@ sealed class TargetState(val type: Type) : StateMatcher { override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = itemStack - override fun isAir() = false + override fun isEmpty() = false } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index df527bda3..3df5ae0d2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -49,10 +49,11 @@ import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState -import com.lambda.util.BlockUtils.brokenState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta -import com.lambda.util.BlockUtils.isBroken +import com.lambda.util.BlockUtils.emptyState import com.lambda.util.BlockUtils.isEmpty +import com.lambda.util.BlockUtils.isNotBroken +import com.lambda.util.BlockUtils.isNotEmpty import com.lambda.util.Communication.warn import com.lambda.util.item.ItemUtils.block import com.lambda.util.math.lerp @@ -150,8 +151,8 @@ object BreakManager : RequestHandler( .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> // if not broken - if (!isBroken(info.context.checkedState, event.newState)) { - this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.checkedState.brokenState}") + if (isNotBroken(info.context.checkedState, event.newState)) { + this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.checkedState.emptyState}") // update the checked state info.context.checkedState = event.newState return@listen @@ -364,7 +365,7 @@ object BreakManager : RequestHandler( val blockState = blockState(ctx.expectedPos) val hardness = ctx.checkedState.getHardness(world, ctx.expectedPos) - return !blockState.isEmpty && hardness != 600f && hardness != -1f + return blockState.isNotEmpty && hardness != 600f && hardness != -1f } /** @@ -700,7 +701,7 @@ object BreakManager : RequestHandler( lastPosStarted = ctx.expectedPos val blockState = blockState(ctx.expectedPos) - val notEmpty = !blockState.isEmpty + val notEmpty = blockState.isNotEmpty if (notEmpty && info.breakingTicks == 0) { blockState.onBlockBreakStart(world, ctx.expectedPos, player) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 2324f1bb6..663f8ab8a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -29,9 +29,10 @@ import com.lambda.interaction.request.breaking.BreakManager.lastPosStarted import com.lambda.interaction.request.breaking.BreakManager.matchesBlockItem import com.lambda.interaction.request.breaking.ReBreakManager.reBreak import com.lambda.module.modules.client.TaskFlowModule -import com.lambda.util.BlockUtils.brokenState +import com.lambda.util.BlockUtils.emptyState import com.lambda.util.BlockUtils.fluidState -import com.lambda.util.BlockUtils.isBroken +import com.lambda.util.BlockUtils.isEmpty +import com.lambda.util.BlockUtils.isNotBroken import com.lambda.util.BlockUtils.matches import com.lambda.util.Communication.info import com.lambda.util.Communication.warn @@ -79,9 +80,9 @@ object BrokenBlockHandler { return@listen // return if the block's not broken - if (!isBroken(pending.context.checkedState, event.newState)) { + if (isNotBroken(pending.context.checkedState, event.newState)) { if (!pending.isReBreaking) { - this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.checkedState.brokenState}") + this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.checkedState.emptyState}") pending.stopPending() } else { pending.context.checkedState = event.newState @@ -174,7 +175,7 @@ object BrokenBlockHandler { return false val block = ctx.checkedState.block if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false - if (ctx.checkedState.isAir) return false + if (ctx.checkedState.isEmpty) return false block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) val fluidState = fluidState(ctx.expectedPos) diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index f3f15cc0c..1d2930eca 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -311,7 +311,9 @@ object BlockUtils { return speedMultiplier } - val BlockState.isEmpty get() = matches(fluidState.blockState) - val BlockState.brokenState: BlockState get() = fluidState.blockState - fun isBroken(oldState: BlockState, newState: BlockState) = !oldState.isEmpty && oldState.brokenState.matches(newState) + val BlockState.isEmpty get() = matches(emptyState) + val BlockState.isNotEmpty get() = !isEmpty + val BlockState.emptyState: BlockState get() = fluidState.blockState + fun isBroken(oldState: BlockState, newState: BlockState) = oldState.isNotEmpty && oldState.emptyState.matches(newState) + fun isNotBroken(oldState: BlockState, newState: BlockState) = !isBroken(oldState, newState) } From 57570c0e0d6a340ccad345d039fb1c53cf2d0c21 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 4 Jul 2025 12:48:57 +0100 Subject: [PATCH 247/364] use scan modes based on the block position rather than relative box dimensions --- .../preprocessors/BlockHalfPreProcessor.kt | 4 +- .../preprocessors/DoorHingePreProcessor.kt | 16 ++--- .../preprocessors/SlabPreProcessor.kt | 4 +- .../construction/verify/ScanMode.kt | 4 +- .../rotating/visibilty/PointSelection.kt | 2 +- .../rotating/visibilty/VisibilityChecker.kt | 64 +++++++++++-------- 6 files changed, 54 insertions(+), 40 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt index 6f32e424c..66dc94448 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt @@ -36,8 +36,8 @@ object BlockHalfPreProcessor : PlacementProcessor() { val slab = state.get(Properties.BLOCK_HALF) ?: return val surfaceScan = when (slab) { - BlockHalf.BOTTOM -> SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Y) - BlockHalf.TOP -> SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Y) + BlockHalf.BOTTOM -> SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.Y) + BlockHalf.TOP -> SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.Y) } accumulator.offerSurfaceScan(surfaceScan) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt index f08104490..37b11a238 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt @@ -38,19 +38,19 @@ object DoorHingePreProcessor : PlacementProcessor() { val side = state.get(Properties.DOOR_HINGE) ?: return@runSafe val scanner = when (state.get(Properties.HORIZONTAL_FACING) ?: return@runSafe) { Direction.NORTH -> - if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.X) - else SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.X) + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.X) + else SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.X) Direction.EAST -> - if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Z) - else SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Z) + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.Z) + else SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.Z) Direction.SOUTH -> - if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.X) - else SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.X) + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.X) + else SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.X) Direction.DOWN, Direction.UP, Direction.WEST -> - if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Z) - else SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Z) + if (side == DoorHinge.LEFT) SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.Z) + else SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.Z) } accumulator.offerSurfaceScan(scanner) } ?: Unit diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt index d110ef7d9..191926f3e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt @@ -36,8 +36,8 @@ object SlabPreProcessor : PlacementProcessor() { val slab = state.get(Properties.SLAB_TYPE) ?: return val surfaceScan = when (slab) { - SlabType.BOTTOM -> SurfaceScan(ScanMode.LESSER_HALF, Direction.Axis.Y) - SlabType.TOP -> SurfaceScan(ScanMode.GREATER_HALF, Direction.Axis.Y) + SlabType.BOTTOM -> SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.Y) + SlabType.TOP -> SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.Y) SlabType.DOUBLE -> { accumulator.addIgnores(Properties.SLAB_TYPE) SurfaceScan(ScanMode.FULL, Direction.Axis.Y) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt index 6bcaf31d4..2a5cd140c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/ScanMode.kt @@ -18,7 +18,7 @@ package com.lambda.interaction.construction.verify enum class ScanMode(val priority: Int) { - GREATER_HALF(1), - LESSER_HALF(1), + GREATER_BLOCK_HALF(1), + LESSER_BLOCK_HALF(1), FULL(0) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt index 23f403103..0aef9ac16 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt @@ -31,7 +31,7 @@ enum class PointSelection(val select: (MutableList Optimum({ hits -> val optimum = hits .map { it.hit.pos } - .reduceOrNull { acc, pos -> acc?.add(pos) } + .reduceOrNull { acc, pos -> pos?.let { acc?.add(it) } } ?.times(1 / hits.size) optimum?.let { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt index dd6eae3d1..b50b46e66 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt @@ -34,6 +34,7 @@ import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import java.util.* +import kotlin.math.floor import kotlin.math.pow /** @@ -165,53 +166,66 @@ object VisibilityChecker { */ fun scanSurfaces( box: Box, - excludedSides: Set = emptySet(), + sides: Set = emptySet(), resolution: Int = 5, scan: SurfaceScan = SurfaceScan.DEFAULT, check: (Direction, Vec3d) -> Unit, ) { - excludedSides.forEach { side -> + sides.forEach { side -> val (minX, minY, minZ, maxX, maxY, maxZ) = box.contract(TaskFlowModule.shrinkFactor).bounds(side) - val stepX = (maxX - minX) / resolution - val stepY = (maxY - minY) / resolution - val stepZ = (maxZ - minZ) / resolution - // Determine the bounds to scan based on the axis and mode - val (startX, endX) = if (scan.axis == Direction.Axis.X && stepX != 0.0) { - val centerX = (minX + maxX) / 2 + // Determine the bounds to scan based on the axis and mode. Skip if no part of the face is in the desired bounds + val (startX, endX) = if (scan.axis == Direction.Axis.X && maxX != minX) { when (scan.mode) { - ScanMode.GREATER_HALF -> centerX + 0.01 to maxX - ScanMode.LESSER_HALF -> minX to centerX - 0.01 + ScanMode.GREATER_BLOCK_HALF -> (floor(minX) + 0.501).let { center -> + if (maxX < center) return@forEach + minX.coerceAtLeast(center) to maxX + } + ScanMode.LESSER_BLOCK_HALF -> (floor(maxX) + 0.499).let { center -> + if (minX > center) return@forEach + minX to maxX.coerceAtMost(center) + } ScanMode.FULL -> minX to maxX } } else minX to maxX - val (startY, endY) = if (scan.axis == Direction.Axis.Y && stepY != 0.0) { - val centerY = (minY + maxY) / 2 + val (startY, endY) = if (scan.axis == Direction.Axis.Y && maxY != minY) { when (scan.mode) { - ScanMode.GREATER_HALF -> centerY + 0.01 to maxY - ScanMode.LESSER_HALF -> minY to centerY - 0.01 + ScanMode.GREATER_BLOCK_HALF -> (floor(minY) + 0.501).let { center -> + if (maxY < center) return@forEach + minY.coerceAtLeast(center) to maxY + } + ScanMode.LESSER_BLOCK_HALF -> (floor(maxY) + 0.499).let { center -> + if (minY > center) return@forEach + minY to maxY.coerceAtMost(center) + } ScanMode.FULL -> minY to maxY } } else minY to maxY - val (startZ, endZ) = if (scan.axis == Direction.Axis.Z && stepZ != 0.0) { - val centerZ = (minZ + maxZ) / 2 + val (startZ, endZ) = if (scan.axis == Direction.Axis.Z && maxZ != minZ) { when (scan.mode) { - ScanMode.GREATER_HALF -> centerZ + 0.01 to maxZ - ScanMode.LESSER_HALF -> minZ to centerZ - 0.01 + ScanMode.GREATER_BLOCK_HALF -> (floor(minZ) + 0.501).let { center -> + if (maxZ < center) return@forEach + minZ.coerceAtLeast(center) to maxZ + } + ScanMode.LESSER_BLOCK_HALF -> (floor(maxZ) + 0.499).let { center -> + if (minZ > center) return@forEach + minZ to maxZ.coerceAtMost(center) + } ScanMode.FULL -> minZ to maxZ } } else minZ to maxZ - (0..resolution).forEach outer@{ i -> - val x = if (stepX != 0.0) startX + stepX * i else startX - if (x > endX) return@outer - (0..resolution).forEach inner@{ j -> - val y = if (stepY != 0.0) startY + stepY * j else startY - if (y > endY) return@inner + val stepX = (endX - startX) / resolution + val stepY = (endY - startY) / resolution + val stepZ = (endZ - startZ) / resolution + + (0..resolution).forEach outer@ { i -> + val x = if (stepX != 0.0) startX + (stepX * i) else startX + (0..resolution).forEach inner@ { j -> + val y = if (stepY != 0.0) startY + (stepY * j) else startY val z = if (stepZ != 0.0) startZ + stepZ * ((if (stepX != 0.0) j else i)) else startZ - if (z > endZ) return@inner check(side, Vec3d(x, y, z)) } } From e3258698ca627ebd60bb95d2c5773933691d49c7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 4 Jul 2025 15:34:43 +0100 Subject: [PATCH 248/364] use the players regular position when simulating placements --- .../lambda/interaction/construction/simulation/BuildSimulator.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index ffac5ea1e..15ab0147b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -480,7 +480,6 @@ object BuildSimulator { // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { - setPos(eye.x, eye.y - standingEyeHeight, eye.z) this.rotation = RotationManager.serverRotation } From 2585445103a32a2f871a1b72e874b81bef800f5a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 4 Jul 2025 21:55:11 +0100 Subject: [PATCH 249/364] cleaned up and removed essentially redundant context values --- .../construction/context/BreakContext.kt | 29 +++++----------- .../construction/context/BuildContext.kt | 24 +++++++------ .../context/InteractionContext.kt | 17 ++++------ .../construction/context/PlaceContext.kt | 15 ++++---- .../construction/simulation/BuildSimulator.kt | 34 ++++++------------- .../construction/verify/TargetState.kt | 14 +++++++- .../interaction/request/breaking/BreakInfo.kt | 2 +- .../request/breaking/BreakManager.kt | 16 ++++----- .../request/breaking/BrokenBlockHandler.kt | 20 +++++------ .../request/breaking/ReBreakManager.kt | 4 +-- .../interacting/InteractedBlockHandler.kt | 4 +-- .../request/placing/PlacedBlockHandler.kt | 2 +- 12 files changed, 80 insertions(+), 101 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index abb62ca84..3e9745d3b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -25,45 +25,32 @@ import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotating.RotationRequest -import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.block.FallingBlock import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction -import net.minecraft.util.math.Vec3d import java.awt.Color data class BreakContext( - override val pov: Vec3d, override val result: BlockHitResult, override val rotation: RotationRequest, - override var checkedState: BlockState, - override val targetState: TargetState, override var hotbarIndex: Int, - var instantBreak: Boolean, -) : BuildContext { + override var cachedState: BlockState, + override val targetState: TargetState, + var instantBreak: Boolean +) : BuildContext() { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) override val expectedPos: BlockPos get() = result.blockPos - override val distance: Double by lazy { - result.distanceTo(pov) - } - - fun exposedSides(ctx: SafeContext) = - Direction.entries.filter { - ctx.world.isAir(expectedPos.offset(it)) - } - - override val expectedState: BlockState = checkedState.fluidState.blockState + override val expectedState: BlockState = cachedState.fluidState.blockState override fun compareTo(other: BuildContext): Int { return when (other) { is BreakContext -> compareByDescending { - if (it.checkedState.block is FallingBlock) it.expectedPos.y else 0 + if (it.cachedState.block is FallingBlock) it.expectedPos.y else 0 }.thenBy { it.instantBreak }.thenBy { @@ -77,8 +64,8 @@ data class BreakContext( } override fun SafeContext.buildRenderer() { - withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) - withState(checkedState, expectedPos, sideColor, result.side) + withState(cachedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) + withState(cachedState, expectedPos, sideColor, result.side) } fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 9f85c357a..8116937c1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -17,22 +17,24 @@ package com.lambda.interaction.construction.context +import com.lambda.Lambda.mc import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotating.RotationRequest import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d -interface BuildContext : Comparable, Drawable { - val pov: Vec3d - val result: BlockHitResult - val rotation: RotationRequest - val distance: Double - val expectedState: BlockState - val targetState: TargetState - val expectedPos: BlockPos - val checkedState: BlockState - val hotbarIndex: Int +abstract class BuildContext : Comparable, Drawable { + abstract val result: BlockHitResult + abstract val rotation: RotationRequest + abstract val hotbarIndex: Int + abstract val expectedPos: BlockPos + abstract val cachedState: BlockState + abstract val expectedState: BlockState + abstract val targetState: TargetState + + val distance by lazy { + mc.player?.eyePos?.distanceTo(result.pos) ?: Double.MAX_VALUE + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt index 2b071d9bf..f16381352 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.construction.context -import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude @@ -31,30 +30,26 @@ import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d import java.awt.Color class InteractionContext( - override val pov: Vec3d, override val result: BlockHitResult, override val rotation: RotationRequest, - override val distance: Double, + override var hotbarIndex: Int, + override val expectedPos: BlockPos, + override val cachedState: BlockState, override val expectedState: BlockState, override val targetState: TargetState, - override val expectedPos: BlockPos, - override val checkedState: BlockState, - override var hotbarIndex: Int, - val interaction: InteractionConfig, -) : BuildContext { +) : BuildContext() { private val baseColor = Color(35, 254, 79, 25) private val sideColor = Color(35, 254, 79, 100) override fun compareTo(other: BuildContext) = when { other is InteractionContext -> compareBy { - BlockUtils.fluids.indexOf(it.checkedState.fluidState.fluid) + BlockUtils.fluids.indexOf(it.cachedState.fluidState.fluid) }.thenByDescending { - it.checkedState.fluidState.level + it.cachedState.fluidState.level }.thenBy { it.rotation.target.angleDistance }.thenBy { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 2d11be61d..9d3c67a91 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -31,32 +31,29 @@ import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d import java.awt.Color data class PlaceContext( - override val pov: Vec3d, override val result: BlockHitResult, override val rotation: RotationRequest, - override val distance: Double, - override val expectedState: BlockState, - override val checkedState: BlockState, override val hotbarIndex: Int, override val expectedPos: BlockPos, + override val cachedState: BlockState, + override val expectedState: BlockState, override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, - val currentDirIsInvalid: Boolean = false, -) : BuildContext { + val currentDirIsInvalid: Boolean = false +) : BuildContext() { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) override fun compareTo(other: BuildContext) = when (other) { is PlaceContext -> compareBy { - BlockUtils.fluids.indexOf(it.checkedState.fluidState.fluid) + BlockUtils.fluids.indexOf(it.cachedState.fluidState.fluid) }.thenByDescending { - it.checkedState.fluidState.level + it.cachedState.fluidState.level }.thenBy { it.sneak == mc.player?.isSneaking }.thenBy { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 15ab0147b..f97fbe4b5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -414,12 +414,11 @@ object BuildSimulator { } return if (placing) checkPlaceOn(pos, validHits, eye, preProcessing, targetState, place, rotation, interact, inventory) - else checkInteractOn(pos, eye, item, validHits, expectedState, targetState, state, rotation, interact, inventory) + else checkInteractOn(pos, item, validHits, expectedState, targetState, state, rotation, interact, inventory) } private fun SafeContext.checkInteractOn( pos: BlockPos, - eye: Vec3d, item: Item, validHits: MutableList, expectedState: BlockState, @@ -433,16 +432,13 @@ object BuildSimulator { val checkedResult = checkedHit.hit val rotationTarget = lookAt(checkedHit.targetRotation, 0.001) val context = InteractionContext( - eye, checkedResult.blockResult ?: return null, RotationRequest(rotationTarget, rotation), - eye.distanceTo(checkedResult.pos), - expectedState, - targetState, + player.inventory.selectedSlot, pos, currentState, - player.inventory.selectedSlot, - interact + expectedState, + targetState ) val stackSelection = item.select() @@ -596,18 +592,16 @@ object BuildSimulator { } else lookAt(rot, 0.001) val placeContext = PlaceContext( - eye, blockHit, RotationRequest(rotationRequest, rotation), - eye.distanceTo(blockHit.pos), - resultState, - blockState(blockHit.blockPos.offset(blockHit.side)), player.inventory.selectedSlot, context.blockPos, + resultState, + blockState(blockHit.blockPos.offset(blockHit.side)), targetState, shouldSneak, false, - currentDirIsInvalid, + currentDirIsInvalid ) val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) @@ -693,11 +687,6 @@ object BuildSimulator { val verify: CheckedHit.() -> Boolean = { hit.blockResult?.blockPos == pos } - val targetState = if (!state.fluidState.isEmpty) { - TargetState.State(state.fluidState.blockState) - } else { - TargetState.Air - } /* the player is buried inside the block */ if (boxes.any { it.contains(eye) }) { @@ -706,12 +695,11 @@ object BuildSimulator { lookAtBlock(pos, config = interact), rotation ) val breakContext = BreakContext( - eye, blockHit, rotationRequest, - state, - targetState, player.inventory.selectedSlot, + state, + TargetState.Empty, instantBreakable(state, pos, breaking.breakThreshold) ) acc.add(BreakResult.Break(pos, breakContext)) @@ -759,9 +747,7 @@ object BuildSimulator { val request = RotationRequest(target, rotation) val instant = instantBreakable(state, pos, breaking.breakThreshold) - val breakContext = BreakContext( - eye, blockHit, request, state, targetState, player.inventory.selectedSlot, instant - ) + val breakContext = BreakContext(blockHit, request, player.inventory.selectedSlot, state, TargetState.Empty, instant) if (gamemode.isCreative) { acc.add(BreakResult.Break(pos, breakContext)) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index 3eb54259a..d3b1570eb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -34,7 +34,19 @@ import net.minecraft.util.math.Direction sealed class TargetState(val type: Type) : StateMatcher { enum class Type { - AIR, SOLID, SUPPORT, STATE, BLOCK, STACK + EMPTY, AIR, SOLID, SUPPORT, STATE, BLOCK, STACK + } + + data object Empty : TargetState(Type.EMPTY) { + override fun toString() = "Empty" + + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) = + state.isEmpty + + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = + ItemStack.EMPTY + + override fun isEmpty() = true } data object Air : TargetState(Type.AIR) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 8a755b060..435a94c49 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -107,7 +107,7 @@ data class BreakInfo( } private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { - val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) + val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) val progress = (breakDelta * breakingTicks) / getBreakThreshold() return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 3df5ae0d2..a7b7feb60 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -151,10 +151,10 @@ object BreakManager : RequestHandler( .firstOrNull { it.context.expectedPos == event.pos } ?.let { info -> // if not broken - if (isNotBroken(info.context.checkedState, event.newState)) { - this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.checkedState.emptyState}") + if (isNotBroken(info.context.cachedState, event.newState)) { + this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.cachedState.emptyState}") // update the checked state - info.context.checkedState = event.newState + info.context.cachedState = event.newState return@listen } destroyBlock(info) @@ -189,7 +189,7 @@ object BreakManager : RequestHandler( .forEach { info -> val config = info.breakConfig if (!config.renders) return@listen - val breakDelta = info.context.checkedState.calcBreakDelta( + val breakDelta = info.context.cachedState.calcBreakDelta( player, world, info.context.expectedPos, @@ -200,7 +200,7 @@ object BreakManager : RequestHandler( if (info.isPrimary) it * (2 - info.breakConfig.breakThreshold) else it }.toDouble() - val state = info.context.checkedState + val state = info.context.cachedState val boxes = state.getOutlineShape(world, info.context.expectedPos).boundingBoxes.map { it.offset(info.context.expectedPos) } @@ -278,7 +278,7 @@ object BreakManager : RequestHandler( .forEach { info -> if (info.updatedProgressThisTick) return@forEach val minKeepTicks = if (info.isSecondary) { - val breakDelta = info.context.checkedState.calcBreakDelta( + val breakDelta = info.context.cachedState.calcBreakDelta( player, world, info.context.expectedPos, @@ -363,7 +363,7 @@ object BreakManager : RequestHandler( } val blockState = blockState(ctx.expectedPos) - val hardness = ctx.checkedState.getHardness(world, ctx.expectedPos) + val hardness = ctx.cachedState.getHardness(world, ctx.expectedPos) return blockState.isNotEmpty && hardness != 600f && hardness != -1f } @@ -764,7 +764,7 @@ object BreakManager : RequestHandler( */ fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) - val correctMaterial = info.context.checkedState.block == entity.stack.item.block + val correctMaterial = info.context.cachedState.block == entity.stack.item.block return inRange && correctMaterial } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 663f8ab8a..b8b01bc61 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -61,7 +61,7 @@ object BrokenBlockHandler { else warn("${info::class.simpleName}'s item drop at ${info.context.expectedPos.toShortString()} timed out") if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) { - world.setBlockState(info.context.expectedPos, info.context.checkedState) + world.setBlockState(info.context.expectedPos, info.context.cachedState) } } info.internalOnCancel() @@ -76,16 +76,16 @@ object BrokenBlockHandler { else null }?.let { pending -> // return if the state hasn't changed - if (event.newState.matches(pending.context.checkedState)) + if (event.newState.matches(pending.context.cachedState)) return@listen // return if the block's not broken - if (isNotBroken(pending.context.checkedState, event.newState)) { + if (isNotBroken(pending.context.cachedState, event.newState)) { if (!pending.isReBreaking) { - this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.checkedState.emptyState}") + this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}") pending.stopPending() } else { - pending.context.checkedState = event.newState + pending.context.cachedState = event.newState } return@listen } @@ -171,16 +171,16 @@ object BrokenBlockHandler { if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false - if (!player.mainHandStack.item.canMine(ctx.checkedState, world, ctx.expectedPos, player)) + if (!player.mainHandStack.item.canMine(ctx.cachedState, world, ctx.expectedPos, player)) return false - val block = ctx.checkedState.block + val block = ctx.cachedState.block if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false - if (ctx.checkedState.isEmpty) return false + if (ctx.cachedState.isEmpty) return false - block.onBreak(world, ctx.expectedPos, ctx.checkedState, player) + block.onBreak(world, ctx.expectedPos, ctx.cachedState, player) val fluidState = fluidState(ctx.expectedPos) val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) - if (setState) block.onBroken(world, ctx.expectedPos, ctx.checkedState) + if (setState) block.onBroken(world, ctx.expectedPos, ctx.cachedState) if (info.breakConfig.breakingTexture) info.setBreakingTextureStage(player, world, -1) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index c43a708ce..4f0d0f845 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -72,9 +72,9 @@ object ReBreakManager { val context = info.context val awaitThenBreak = info.breakConfig.breakConfirmation == BreakConfig.BreakConfirmationMode.AwaitThenBreak - val breakProgress = context.checkedState.calcBlockBreakingDelta(player, world, context.expectedPos) + val breakProgress = context.cachedState.calcBlockBreakingDelta(player, world, context.expectedPos) return@runSafe if (info.breakingTicks * breakProgress >= info.breakConfig.breakThreshold) { - if (context.checkedState.isEmpty) { + if (context.cachedState.isEmpty) { return@runSafe ReBreakResult.Ignored } if (!awaitThenBreak) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt index 2a42873a2..6f1f805e1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt @@ -38,7 +38,7 @@ object InteractedBlockHandler { ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") if (it.interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) { - mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + mc.world?.setBlockState(it.context.expectedPos, it.context.cachedState) } it.pendingInteractionsList.remove(it.context) } @@ -55,7 +55,7 @@ object InteractedBlockHandler { if (info.interact.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract) with (info.context) { - checkedState.onUse(world, player, Hand.MAIN_HAND, result) + cachedState.onUse(world, player, Hand.MAIN_HAND, result) } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt index 0263624d6..e2b667e05 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -38,7 +38,7 @@ object PlacedBlockHandler { ) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") if (it.placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) { - mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState) + mc.world?.setBlockState(it.context.expectedPos, it.context.cachedState) } it.pendingInteractionsList.remove(it.context) } From 7dd5283693e007277b569ec777cd4ab1a5275f10 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 4 Jul 2025 23:33:04 +0100 Subject: [PATCH 250/364] more cleanup --- .../construction/context/BreakContext.kt | 15 ++--- .../construction/context/BuildContext.kt | 4 +- .../context/InteractionContext.kt | 7 +- .../construction/context/PlaceContext.kt | 6 +- .../construction/simulation/BuildSimulator.kt | 20 ++---- .../interaction/request/breaking/BreakInfo.kt | 8 +-- .../request/breaking/BreakManager.kt | 66 +++++++++---------- .../request/breaking/BreakRequest.kt | 3 +- .../request/breaking/BrokenBlockHandler.kt | 28 ++++---- .../request/breaking/ReBreakManager.kt | 9 ++- .../interacting/InteractedBlockHandler.kt | 6 +- .../request/interacting/InteractionManager.kt | 2 +- .../request/interacting/InteractionRequest.kt | 2 +- .../request/placing/PlaceManager.kt | 4 +- .../request/placing/PlaceRequest.kt | 3 +- .../request/placing/PlacedBlockHandler.kt | 8 +-- .../module/modules/player/PacketMine.kt | 3 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 2 +- 18 files changed, 91 insertions(+), 105 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 3e9745d3b..4eb228a45 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -20,11 +20,11 @@ package com.lambda.interaction.construction.context import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.util.BlockUtils.emptyState import net.minecraft.block.BlockState import net.minecraft.block.FallingBlock import net.minecraft.util.hit.BlockHitResult @@ -36,21 +36,18 @@ data class BreakContext( override val rotation: RotationRequest, override var hotbarIndex: Int, override var cachedState: BlockState, - override val targetState: TargetState, var instantBreak: Boolean ) : BuildContext() { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) - override val expectedPos: BlockPos - get() = result.blockPos - - override val expectedState: BlockState = cachedState.fluidState.blockState + override val blockPos: BlockPos = result.blockPos + override val expectedState: BlockState = cachedState.emptyState override fun compareTo(other: BuildContext): Int { return when (other) { is BreakContext -> compareByDescending { - if (it.cachedState.block is FallingBlock) it.expectedPos.y else 0 + if (it.cachedState.block is FallingBlock) it.blockPos.y else 0 }.thenBy { it.instantBreak }.thenBy { @@ -64,8 +61,8 @@ data class BreakContext( } override fun SafeContext.buildRenderer() { - withState(cachedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) - withState(cachedState, expectedPos, sideColor, result.side) + withState(cachedState, blockPos, baseColor, DirectionMask.ALL.exclude(result.side)) + withState(cachedState, blockPos, sideColor, result.side) } fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 8116937c1..433b4b4c0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -19,7 +19,6 @@ package com.lambda.interaction.construction.context import com.lambda.Lambda.mc import com.lambda.interaction.construction.result.Drawable -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotating.RotationRequest import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult @@ -29,10 +28,9 @@ abstract class BuildContext : Comparable, Drawable { abstract val result: BlockHitResult abstract val rotation: RotationRequest abstract val hotbarIndex: Int - abstract val expectedPos: BlockPos abstract val cachedState: BlockState abstract val expectedState: BlockState - abstract val targetState: TargetState + abstract val blockPos: BlockPos val distance by lazy { mc.player?.eyePos?.distanceTo(result.pos) ?: Double.MAX_VALUE diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt index f16381352..4bf0762a1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt @@ -20,7 +20,6 @@ package com.lambda.interaction.construction.context import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.interacting.InteractionRequest @@ -36,14 +35,14 @@ class InteractionContext( override val result: BlockHitResult, override val rotation: RotationRequest, override var hotbarIndex: Int, - override val expectedPos: BlockPos, override val cachedState: BlockState, override val expectedState: BlockState, - override val targetState: TargetState, ) : BuildContext() { private val baseColor = Color(35, 254, 79, 25) private val sideColor = Color(35, 254, 79, 100) + override val blockPos: BlockPos = result.blockPos + override fun compareTo(other: BuildContext) = when { other is InteractionContext -> compareBy { @@ -62,7 +61,7 @@ class InteractionContext( } override fun SafeContext.buildRenderer() { - withState(expectedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side.opposite)) + withState(expectedState, blockPos, baseColor, DirectionMask.ALL.exclude(result.side.opposite)) withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 9d3c67a91..f82106e35 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -21,7 +21,6 @@ import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude -import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceRequest @@ -37,10 +36,9 @@ data class PlaceContext( override val result: BlockHitResult, override val rotation: RotationRequest, override val hotbarIndex: Int, - override val expectedPos: BlockPos, + override val blockPos: BlockPos, override val cachedState: BlockState, override val expectedState: BlockState, - override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, val currentDirIsInvalid: Boolean = false @@ -70,7 +68,7 @@ data class PlaceContext( } override fun SafeContext.buildRenderer() { - withState(expectedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side.opposite)) + withState(expectedState, blockPos, baseColor, DirectionMask.ALL.exclude(result.side.opposite)) withState(blockState(result.blockPos), result.blockPos, sideColor, result.side) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index f97fbe4b5..05593f8c2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -271,7 +271,7 @@ object BuildSimulator { return@forEach } - checkPlaceOn(pos, validHits, eye, preProcessing, target, place, rotation, interact, inventory)?.let { placeResult -> + checkPlaceOn(pos, validHits, preProcessing, target, place, rotation, interact, inventory)?.let { placeResult -> acc.add(placeResult) } } @@ -413,8 +413,8 @@ object BuildSimulator { } } - return if (placing) checkPlaceOn(pos, validHits, eye, preProcessing, targetState, place, rotation, interact, inventory) - else checkInteractOn(pos, item, validHits, expectedState, targetState, state, rotation, interact, inventory) + return if (placing) checkPlaceOn(pos, validHits, preProcessing, targetState, place, rotation, interact, inventory) + else checkInteractOn(pos, item, validHits, expectedState, state, rotation, interact, inventory) } private fun SafeContext.checkInteractOn( @@ -422,7 +422,6 @@ object BuildSimulator { item: Item, validHits: MutableList, expectedState: BlockState, - targetState: TargetState, currentState: BlockState, rotation: RotationConfig, interact: InteractionConfig, @@ -435,10 +434,8 @@ object BuildSimulator { checkedResult.blockResult ?: return null, RotationRequest(rotationTarget, rotation), player.inventory.selectedSlot, - pos, currentState, - expectedState, - targetState + expectedState ) val stackSelection = item.select() @@ -463,7 +460,6 @@ object BuildSimulator { private fun SafeContext.checkPlaceOn( pos: BlockPos, validHits: MutableList, - eye: Vec3d, preProcessing: PreProcessingInfo, targetState: TargetState, place: PlaceConfig, @@ -596,9 +592,8 @@ object BuildSimulator { RotationRequest(rotationRequest, rotation), player.inventory.selectedSlot, context.blockPos, + blockState(context.blockPos), resultState, - blockState(blockHit.blockPos.offset(blockHit.side)), - targetState, shouldSneak, false, currentDirIsInvalid @@ -699,7 +694,6 @@ object BuildSimulator { rotationRequest, player.inventory.selectedSlot, state, - TargetState.Empty, instantBreakable(state, pos, breaking.breakThreshold) ) acc.add(BreakResult.Break(pos, breakContext)) @@ -744,10 +738,10 @@ object BuildSimulator { val bestHit = interact.pointSelection.select(validHits) ?: return acc val blockHit = bestHit.hit.blockResult ?: return acc val target = lookAt(bestHit.targetRotation, 0.001) - val request = RotationRequest(target, rotation) + val rotationRequest = RotationRequest(target, rotation) val instant = instantBreakable(state, pos, breaking.breakThreshold) - val breakContext = BreakContext(blockHit, request, player.inventory.selectedSlot, state, TargetState.Empty, instant) + val breakContext = BreakContext(blockHit, rotationRequest, player.inventory.selectedSlot, state, instant) if (gamemode.isCreative) { acc.add(BreakResult.Break(pos, breakContext)) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 435a94c49..5d077d836 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -75,7 +75,7 @@ data class BreakInfo( } fun internalOnCancel() { - request.onCancel?.invoke(context.expectedPos) + request.onCancel?.invoke(context.blockPos) } fun updateInfo(context: BreakContext, request: BreakRequest) { @@ -103,11 +103,11 @@ data class BreakInfo( world: ClientWorld, stage: Int = getBreakTextureProgress(player, world) ) { - world.setBlockBreakingInfo(player.id, context.expectedPos, stage) + world.setBlockBreakingInfo(player.id, context.blockPos, stage) } private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int { - val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack) + val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, player.mainHandStack) val progress = (breakDelta * breakingTicks) / getBreakThreshold() return if (progress > 0.0f) (progress * 10.0f).toInt() else -1 } @@ -127,7 +127,7 @@ data class BreakInfo( interaction.sendSequencedPacket(world) { sequence: Int -> PlayerActionC2SPacket( action, - context.expectedPos, + context.blockPos, context.result.side, sequence ) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index a7b7feb60..58037de98 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -92,7 +92,7 @@ object BreakManager : RequestHandler( private val pendingBreakCount get() = breakInfos.count { it != null } + pendingBreaks.size override val blockedPositions - get() = breakInfos.mapNotNull { it?.context?.expectedPos } + pendingBreaks.map { it.context.expectedPos } + get() = breakInfos.mapNotNull { it?.context?.blockPos } + pendingBreaks.map { it.context.blockPos } private var activeRequest: BreakRequest? = null @@ -144,11 +144,11 @@ object BreakManager : RequestHandler( } listen(priority = Int.MIN_VALUE) { event -> - if (event.pos == ReBreakManager.reBreak?.context?.expectedPos) return@listen + if (event.pos == ReBreakManager.reBreak?.context?.blockPos) return@listen breakInfos .filterNotNull() - .firstOrNull { it.context.expectedPos == event.pos } + .firstOrNull { it.context.blockPos == event.pos } ?.let { info -> // if not broken if (isNotBroken(info.context.cachedState, event.newState)) { @@ -158,7 +158,7 @@ object BreakManager : RequestHandler( return@listen } destroyBlock(info) - info.request.onStop?.invoke(info.context.expectedPos) + info.request.onStop?.invoke(info.context.blockPos) info.internalOnBreak() if (!info.callbacksCompleted) { info.startPending() @@ -192,7 +192,7 @@ object BreakManager : RequestHandler( val breakDelta = info.context.cachedState.calcBreakDelta( player, world, - info.context.expectedPos, + info.context.blockPos, info.breakConfig, player.inventory.getStack(info.context.hotbarIndex) ) @@ -201,8 +201,8 @@ object BreakManager : RequestHandler( else it }.toDouble() val state = info.context.cachedState - val boxes = state.getOutlineShape(world, info.context.expectedPos).boundingBoxes.map { - it.offset(info.context.expectedPos) + val boxes = state.getOutlineShape(world, info.context.blockPos).boundingBoxes.map { + it.offset(info.context.blockPos) } val fillColor = if (config.dynamicFillColor) lerp(progress, config.startFillColor, config.endFillColor) @@ -281,7 +281,7 @@ object BreakManager : RequestHandler( val breakDelta = info.context.cachedState.calcBreakDelta( player, world, - info.context.expectedPos, + info.context.blockPos, info.breakConfig, player.inventory.getStack(info.context.hotbarIndex) ) @@ -317,7 +317,7 @@ object BreakManager : RequestHandler( private fun SafeContext.populateFrom(request: BreakRequest) { // Sanitize the new breaks val newBreaks = request.contexts - .distinctBy { it.expectedPos } + .distinctBy { it.blockPos } .filter { ctx -> canAccept(ctx, request.build.breaking) } .toMutableList() @@ -325,10 +325,10 @@ object BreakManager : RequestHandler( breakInfos .filterNotNull() .forEach { info -> - newBreaks.find { ctx -> ctx.expectedPos == info.context.expectedPos }?.let { ctx -> + newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos }?.let { ctx -> if (!info.updatedThisTick) { info.updateInfo(ctx, request) - info.request.onUpdate?.invoke(info.context.expectedPos) + info.request.onUpdate?.invoke(info.context.blockPos) } newBreaks.remove(ctx) return@forEach @@ -352,7 +352,7 @@ object BreakManager : RequestHandler( * @return if the break context can be accepted. */ private fun SafeContext.canAccept(ctx: BreakContext, breakConfig: BreakConfig): Boolean { - if (pendingBreaks.any { it.context.expectedPos == ctx.expectedPos }) return false + if (pendingBreaks.any { it.context.blockPos == ctx.blockPos }) return false if (breakConfig.doubleBreak) { breakInfos @@ -362,8 +362,8 @@ object BreakManager : RequestHandler( } } - val blockState = blockState(ctx.expectedPos) - val hardness = ctx.cachedState.getHardness(world, ctx.expectedPos) + val blockState = blockState(ctx.blockPos) + val hardness = ctx.cachedState.getHardness(world, ctx.blockPos) return blockState.isNotEmpty && hardness != 600f && hardness != -1f } @@ -461,7 +461,7 @@ object BreakManager : RequestHandler( * @see startPending */ private fun SafeContext.onBlockBreak(info: BreakInfo) { - info.request.onStop?.invoke(info.context.expectedPos) + info.request.onStop?.invoke(info.context.blockPos) if (info.isRedundant) { info.startPending() } else { @@ -559,13 +559,13 @@ object BreakManager : RequestHandler( val ctx = info.context val hitResult = ctx.result - if (gamemode.isCreative && world.worldBorder.contains(ctx.expectedPos) && info.breaking) { + if (gamemode.isCreative && world.worldBorder.contains(ctx.blockPos) && info.breaking) { if (info.isRedundant) { onBlockBreak(info) return true } breakCooldown = config.breakDelay - lastPosStarted = ctx.expectedPos + lastPosStarted = ctx.blockPos onBlockBreak(info) info.startBreakPacket(world, interaction) if (config.swing.isEnabled()) { @@ -580,7 +580,7 @@ object BreakManager : RequestHandler( primaryBreak = reBreakResult.breakInfo.apply { type = Primary ReBreakManager.clearReBreak() - request.onStart?.invoke(ctx.expectedPos) + request.onStart?.invoke(ctx.blockPos) } return primaryBreak?.let { primary -> @@ -590,7 +590,7 @@ object BreakManager : RequestHandler( is ReBreakResult.ReBroke -> { info.type = ReBreak info.nullify() - info.request.onReBreak?.invoke(info.context.expectedPos) + info.request.onReBreak?.invoke(info.context.blockPos) return true } else -> {} @@ -608,7 +608,7 @@ object BreakManager : RequestHandler( return true } - val blockState = blockState(ctx.expectedPos) + val blockState = blockState(ctx.blockPos) if (blockState.isEmpty) { info.nullify() info.internalOnCancel() @@ -619,7 +619,7 @@ object BreakManager : RequestHandler( val progress = blockState.calcBreakDelta( player, world, - ctx.expectedPos, + ctx.blockPos, config ) * (info.breakingTicks - config.fudgeFactor) @@ -642,7 +642,7 @@ object BreakManager : RequestHandler( (blockSoundGroup.getVolume() + 1.0f) / 8.0f, blockSoundGroup.getPitch() * 0.5f, SoundInstance.createRandom(), - ctx.expectedPos + ctx.blockPos ) ) } @@ -650,7 +650,7 @@ object BreakManager : RequestHandler( } if (config.particles) { - mc.particleManager.addBlockBreakingParticles(ctx.expectedPos, hitResult.side) + mc.particleManager.addBlockBreakingParticles(ctx.blockPos, hitResult.side) } if (config.breakingTexture) { @@ -684,29 +684,29 @@ object BreakManager : RequestHandler( private fun SafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false - if (!world.worldBorder.contains(ctx.expectedPos)) return false + if (player.isBlockBreakingRestricted(world, ctx.blockPos, gamemode)) return false + if (!world.worldBorder.contains(ctx.blockPos)) return false if (gamemode.isCreative) { - lastPosStarted = ctx.expectedPos - info.request.onStart?.invoke(ctx.expectedPos) + lastPosStarted = ctx.blockPos + info.request.onStart?.invoke(ctx.blockPos) onBlockBreak(info) info.startBreakPacket(world, interaction) breakCooldown = info.breakConfig.breakDelay return true } if (info.breaking) return false - info.request.onStart?.invoke(ctx.expectedPos) + info.request.onStart?.invoke(ctx.blockPos) - lastPosStarted = ctx.expectedPos + lastPosStarted = ctx.blockPos - val blockState = blockState(ctx.expectedPos) + val blockState = blockState(ctx.blockPos) val notEmpty = blockState.isNotEmpty if (notEmpty && info.breakingTicks == 0) { - blockState.onBlockBreakStart(world, ctx.expectedPos, player) + blockState.onBlockBreakStart(world, ctx.blockPos, player) } - val breakDelta = blockState.calcBreakDelta(player, world, ctx.expectedPos, info.breakConfig) + val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig) if (notEmpty && breakDelta >= info.getBreakThreshold()) { onBlockBreak(info) } else { @@ -763,7 +763,7 @@ object BreakManager : RequestHandler( * @return if the [ItemEntity] matches the [BreakInfo]'s expected item drop. */ fun matchesBlockItem(info: BreakInfo, entity: ItemEntity): Boolean { - val inRange = info.context.expectedPos.toCenterPos().isInRange(entity.pos, 0.5) + val inRange = info.context.blockPos.toCenterPos().isInRange(entity.pos, 0.5) val correctMaterial = info.context.cachedState.block == entity.stack.item.block return inRange && correctMaterial } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 320517672..388272176 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -26,6 +26,7 @@ import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.isEmpty import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos @@ -49,7 +50,7 @@ data class BreakRequest( var onReBreak: ((BlockPos) -> Unit)? = null override val done: Boolean - get() = runSafe { contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } } == true + get() = runSafe { contexts.all { blockState(it.blockPos).isEmpty } } == true @BreakRequestBuilder class RequestBuilder( diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index b8b01bc61..f47a69439 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -53,15 +53,15 @@ object BrokenBlockHandler { TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { info -> mc.world?.let { world -> - val pos = info.context.expectedPos + val pos = info.context.blockPos val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)) if (!loaded) return@let - if (!info.broken) warn("${info::class.simpleName} at ${info.context.expectedPos.toShortString()} timed out") - else warn("${info::class.simpleName}'s item drop at ${info.context.expectedPos.toShortString()} timed out") + if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out") + else warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out") if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) { - world.setBlockState(info.context.expectedPos, info.context.cachedState) + world.setBlockState(info.context.blockPos, info.context.cachedState) } } info.internalOnCancel() @@ -71,8 +71,8 @@ object BrokenBlockHandler { init { listen(priority = Int.MIN_VALUE + 1) { event -> run { - pendingBreaks.firstOrNull { it.context.expectedPos == event.pos } - ?: if (reBreak?.context?.expectedPos == event.pos) reBreak + pendingBreaks.firstOrNull { it.context.blockPos == event.pos } + ?: if (reBreak?.context?.blockPos == event.pos) reBreak else null }?.let { pending -> // return if the state hasn't changed @@ -96,7 +96,7 @@ object BrokenBlockHandler { pending.internalOnBreak() if (pending.callbacksCompleted) { pending.stopPending() - if (lastPosStarted == pending.context.expectedPos) { + if (lastPosStarted == pending.context.blockPos) { ReBreakManager.offerReBreak(pending) } } @@ -116,7 +116,7 @@ object BrokenBlockHandler { pending.internalOnItemDrop(it.entity) if (pending.callbacksCompleted) { pending.stopPending() - if (lastPosStarted == pending.context.expectedPos) { + if (lastPosStarted == pending.context.blockPos) { ReBreakManager.offerReBreak(pending) } } @@ -169,18 +169,18 @@ object BrokenBlockHandler { fun SafeContext.destroyBlock(info: BreakInfo): Boolean { val ctx = info.context - if (player.isBlockBreakingRestricted(world, ctx.expectedPos, gamemode)) return false + if (player.isBlockBreakingRestricted(world, ctx.blockPos, gamemode)) return false - if (!player.mainHandStack.item.canMine(ctx.cachedState, world, ctx.expectedPos, player)) + if (!player.mainHandStack.item.canMine(ctx.cachedState, world, ctx.blockPos, player)) return false val block = ctx.cachedState.block if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false if (ctx.cachedState.isEmpty) return false - block.onBreak(world, ctx.expectedPos, ctx.cachedState, player) - val fluidState = fluidState(ctx.expectedPos) - val setState = world.setBlockState(ctx.expectedPos, fluidState.blockState, 11) - if (setState) block.onBroken(world, ctx.expectedPos, ctx.cachedState) + block.onBreak(world, ctx.blockPos, ctx.cachedState, player) + val fluidState = fluidState(ctx.blockPos) + val setState = world.setBlockState(ctx.blockPos, fluidState.blockState, 11) + if (setState) block.onBroken(world, ctx.blockPos, ctx.cachedState) if (info.breakConfig.breakingTexture) info.setBreakingTextureStage(player, world, -1) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt index 4f0d0f845..7d5e4ea55 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt @@ -52,7 +52,7 @@ object ReBreakManager { breaking = true resetCallbacks() } - info.request.onReBreakStart?.invoke(info.context.expectedPos) + info.request.onReBreakStart?.invoke(info.context.blockPos) } fun clearReBreak() { @@ -63,21 +63,20 @@ object ReBreakManager { runSafe { val info = reBreak ?: return@runSafe ReBreakResult.Ignored - if (info.context.expectedPos != ctx.expectedPos || !info.breakConfig.reBreak) { + if (info.context.blockPos != ctx.blockPos || !info.breakConfig.reBreak) { return@runSafe ReBreakResult.Ignored } if (info.updatedThisTick) return@runSafe ReBreakResult.ReBroke info.updateInfo(ctx, breakRequest) val context = info.context - val awaitThenBreak = info.breakConfig.breakConfirmation == BreakConfig.BreakConfirmationMode.AwaitThenBreak - val breakProgress = context.cachedState.calcBlockBreakingDelta(player, world, context.expectedPos) + val breakProgress = context.cachedState.calcBlockBreakingDelta(player, world, context.blockPos) return@runSafe if (info.breakingTicks * breakProgress >= info.breakConfig.breakThreshold) { if (context.cachedState.isEmpty) { return@runSafe ReBreakResult.Ignored } - if (!awaitThenBreak) { + if (info.breakConfig.breakConfirmation != BreakConfig.BreakConfirmationMode.AwaitThenBreak) { destroyBlock(info) } info.stopBreakPacket(world, interaction) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt index 6f1f805e1..046bcf99d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt @@ -36,9 +36,9 @@ object InteractedBlockHandler { val pendingInteractions = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { - info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") + info("${it::class.simpleName} at ${it.context.blockPos.toShortString()} timed out") if (it.interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) { - mc.world?.setBlockState(it.context.expectedPos, it.context.cachedState) + mc.world?.setBlockState(it.context.blockPos, it.context.cachedState) } it.pendingInteractionsList.remove(it.context) } @@ -46,7 +46,7 @@ object InteractedBlockHandler { init { listen(priority = Int.MIN_VALUE) { event -> pendingInteractions - .firstOrNull { it.context.expectedPos == event.pos } + .firstOrNull { it.context.blockPos == event.pos } ?.let { info -> removePendingInteract(info) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt index c0b34cf16..43d454aa7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -98,7 +98,7 @@ object InteractionManager : RequestHandler( PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, ctx.result, sequence) } } - request.onInteract?.invoke(ctx.expectedPos) + request.onInteract?.invoke(ctx.blockPos) interactionsThisTick++ iterator.remove() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt index cbec8c8ad..b01799e21 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt @@ -39,5 +39,5 @@ data class InteractionRequest( private val prio: Int = 0 ) : Request(prio, interact) { override val done: Boolean - get() = contexts.all { mc.world?.getBlockState(it.expectedPos)?.matches(it.expectedState) == true } + get() = contexts.all { mc.world?.getBlockState(it.blockPos)?.matches(it.expectedState) == true } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index c012e30d3..fdce00661 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -77,7 +77,7 @@ object PlaceManager : RequestHandler( { player -> shouldSneak == player.isSneaking } override val blockedPositions - get() = pendingPlacements.map { it.context.expectedPos } + get() = pendingPlacements.map { it.context.blockPos } fun Any.onPlace( alwaysListen: Boolean = false, @@ -175,7 +175,7 @@ object PlaceManager : RequestHandler( */ private fun canPlace(placeContext: PlaceContext) = pendingPlacements.none { pending -> - pending.context.expectedPos == placeContext.expectedPos + pending.context.blockPos == placeContext.blockPos } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index e8023d774..2bbc48c00 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -26,6 +26,7 @@ import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.matches data class PlaceRequest( val contexts: Collection, @@ -38,6 +39,6 @@ data class PlaceRequest( ) : Request(prio, build.placing) { override val done: Boolean get() = runSafe { - contexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) } + contexts.all { it.expectedState.matches(blockState(it.blockPos)) } } == true } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt index e2b667e05..a5c775e98 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -36,9 +36,9 @@ object PlacedBlockHandler { val pendingPlacements = LimitedDecayQueue( TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L ) { - info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") + info("${it::class.simpleName} at ${it.context.blockPos.toShortString()} timed out") if (it.placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) { - mc.world?.setBlockState(it.context.expectedPos, it.context.cachedState) + mc.world?.setBlockState(it.context.blockPos, it.context.cachedState) } it.pendingInteractionsList.remove(it.context) } @@ -46,7 +46,7 @@ object PlacedBlockHandler { init { listen(priority = Int.MIN_VALUE) { event -> pendingPlacements - .firstOrNull { it.context.expectedPos == event.pos } + .firstOrNull { it.context.blockPos == event.pos } ?.let { info -> removePendingPlace(info) @@ -59,7 +59,7 @@ object PlacedBlockHandler { if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace) with (info.context) { - placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos) + placeSound(expectedState.block.item as BlockItem, expectedState, blockPos) } info.onPlace() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 617f5925e..40e74b258 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -47,7 +47,6 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import java.awt.Color import java.util.concurrent.ConcurrentLinkedQueue -import kotlin.collections.ArrayList object PacketMine : Module( "PacketMine", @@ -232,7 +231,7 @@ object PacketMine : Module( forEach { modified = modified or it.retainAll { pos -> positions.any { retain -> - retain.expectedPos == pos + retain.blockPos == pos } } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 42fb37aac..7356cdcc2 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -104,7 +104,7 @@ class BuildTask @Ta5kBuilder constructor( .plus(pendingInteractions.toList()) val resultsNotBlocked = results - .filter { result -> pendingInteractions.none { it.expectedPos == result.blockPos } } + .filter { result -> pendingInteractions.none { it.blockPos == result.blockPos } } .sorted() val bestResult = resultsNotBlocked.firstOrNull() ?: return@listen From 79799aafc9a48aea9a70a95e51a6d76552af2128 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 01:34:14 +0100 Subject: [PATCH 251/364] remove problematic return if current dir was valid --- .../interaction/construction/context/PlaceContext.kt | 4 ++-- .../construction/simulation/BuildSimulator.kt | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index f82106e35..e131487d6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -41,7 +41,7 @@ data class PlaceContext( override val expectedState: BlockState, val sneak: Boolean, val insideBlock: Boolean, - val currentDirIsInvalid: Boolean = false + val currentDirIsValid: Boolean = false ) : BuildContext() { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) @@ -75,7 +75,7 @@ data class PlaceContext( fun requestDependencies(request: PlaceRequest): Boolean { val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false) val validRotation = if (request.build.placing.rotateForPlace) { - request.rotation.request(rotation, false).done && !currentDirIsInvalid + request.rotation.request(rotation, false).done && currentDirIsValid } else true return hotbarRequest.done && validRotation } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 05593f8c2..3eb80901a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -534,12 +534,12 @@ object BuildSimulator { } } - val currentDirIsInvalid = simulatePlaceState()?.let { basePlaceResult -> + val currentDirIsValid = simulatePlaceState()?.let { basePlaceResult -> if (!place.rotateForPlace) { return basePlaceResult } - true - } ?: false + false + } ?: true run rotate@ { if (!place.axisRotate) { @@ -557,8 +557,6 @@ object BuildSimulator { return@rotate } - if (!currentDirIsInvalid) return@rotate - PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> fakePlayer.rotation = direction.rotation when (val placeResult = simulatePlaceState()) { @@ -596,7 +594,7 @@ object BuildSimulator { resultState, shouldSneak, false, - currentDirIsInvalid + currentDirIsValid ) val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) From 14c8cd18cf64217aa4130ec221d165e7a3e53708 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 01:34:53 +0100 Subject: [PATCH 252/364] more cleanup --- .../construction/simulation/BuildSimulator.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 3eb80901a..c6b04c950 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -525,13 +525,9 @@ object BuildSimulator { resultState = blockItem.getPlacementState(context) ?: return@placeState PlaceResult.BlockedByEntity(pos) - if (!targetState.matches(resultState, pos, world, preProcessing.ignore)) { - return@placeState PlaceResult.NoIntegrity( - pos, resultState, context, (targetState as? TargetState.State)?.blockState - ) - } else { - return@placeState null - } + return@placeState if (!targetState.matches(resultState, pos, world, preProcessing.ignore)) + PlaceResult.NoIntegrity(pos, resultState, context, (targetState as? TargetState.State)?.blockState) + else null } val currentDirIsValid = simulatePlaceState()?.let { basePlaceResult -> @@ -552,7 +548,7 @@ object BuildSimulator { } fakePlayer.rotation = player.rotation - simulatePlaceState() ?: run { + if (simulatePlaceState() == null) { rot = fakePlayer.rotation return@rotate } From 25c3b8dd6b6b3c1629fb2235a0e45590a6b5ae8a Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 01:48:32 +0100 Subject: [PATCH 253/364] move checkPostProcessResults invoke to the simulate function --- .../construction/simulation/BuildSimulator.kt | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index c6b04c950..078741b85 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -98,7 +98,11 @@ object BuildSimulator { ) = runSafe { structure.entries.flatMap { (pos, target) -> val preProcessing = target.getProcessingInfo() - checkRequirements(pos, eye, preProcessing, target, build, interact, rotation, inventory).let { + checkRequirements(pos, preProcessing, target, build).let { + if (it.isEmpty()) return@let + return@flatMap it + } + checkPostProcessResults(pos, eye, preProcessing, target, interact, build.placing, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } @@ -117,13 +121,9 @@ object BuildSimulator { private fun SafeContext.checkRequirements( pos: BlockPos, - eye: Vec3d, preProcessing: PreProcessingInfo, target: TargetState, - build: BuildConfig, - interact: InteractionConfig, - rotation: RotationConfig, - inventory: InventoryConfig + build: BuildConfig ): Set { val acc = mutableSetOf() @@ -165,16 +165,6 @@ object BuildSimulator { return acc } - /* the state requires post-processing */ - if (target.matches(state, pos, world, ignoredProperties = preProcessing.ignore)) { - checkPostProcessResults(pos, eye, preProcessing, state, target, interact, build.placing, rotation, inventory).let { postProcessResults -> - if (postProcessResults.isNotEmpty()) { - acc.addAll(postProcessResults) - return acc - } - } - } - /* block is unbreakable, so it cant be broken or replaced */ if (state.getHardness(world, pos) < 0 && !gamemode.isCreative) { acc.add(BuildResult.Unbreakable(pos, state)) @@ -283,7 +273,6 @@ object BuildSimulator { pos: BlockPos, eye: Vec3d, preProcessing: PreProcessingInfo, - state: BlockState, targetState: TargetState, interact: InteractionConfig, place: PlaceConfig, @@ -294,6 +283,10 @@ object BuildSimulator { val acc = mutableSetOf() + val state = blockState(pos) + if (!targetState.matches(state, pos, world, preProcessing.ignore)) + return acc + val interactBlock: (BlockState, Set?, Item?, Boolean) -> Unit = { expectedState, side, item, placing -> interactWithBlock( From 3c6694f59d72bc5dd30abdb91b50aac824dab8e9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 04:20:34 +0100 Subject: [PATCH 254/364] inline checkPlaceOn as it's not suitable for slabs and potentially other place-style post-processing --- .../processing/ProcessorRegistry.kt | 2 + .../construction/simulation/BuildSimulator.kt | 58 ++++++++++--------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt index 7de710d7f..01d0ee2b1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt @@ -98,6 +98,8 @@ object ProcessorRegistry : Loadable { Properties.EAST, Properties.SOUTH, Properties.WEST, + Properties.PERSISTENT, + Properties.DISTANCE_1_7 ) override fun load() = "Loaded ${processors.size} pre processors" diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 078741b85..a69e35702 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -60,6 +60,7 @@ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.BlockUtils.isNotEmpty +import com.lambda.util.BlockUtils.item import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal @@ -70,6 +71,7 @@ import com.lambda.util.player.gamemode import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock +import net.minecraft.block.enums.SlabType import net.minecraft.block.pattern.CachedBlockPosition import net.minecraft.enchantment.Enchantments import net.minecraft.item.BlockItem @@ -98,7 +100,7 @@ object BuildSimulator { ) = runSafe { structure.entries.flatMap { (pos, target) -> val preProcessing = target.getProcessingInfo() - checkRequirements(pos, preProcessing, target, build).let { + checkRequirements(pos, target, build).let { if (it.isEmpty()) return@let return@flatMap it } @@ -121,7 +123,6 @@ object BuildSimulator { private fun SafeContext.checkRequirements( pos: BlockPos, - preProcessing: PreProcessingInfo, target: TargetState, build: BuildConfig ): Set { @@ -289,6 +290,7 @@ object BuildSimulator { val interactBlock: (BlockState, Set?, Item?, Boolean) -> Unit = { expectedState, side, item, placing -> + //ToDo: Refactor this function chain into a better solution interactWithBlock( pos, state, @@ -311,15 +313,37 @@ object BuildSimulator { val mismatchedProperties = state.properties.filter { state.get(it) != targetState.blockState.get(it) } mismatchedProperties.forEach { property -> when (property) { - Properties.EYE -> run eye@ { - if (state.get(Properties.EYE)) return@eye + Properties.EYE -> { + if (state.get(Properties.EYE)) return@forEach val expectedState = state.with(Properties.EYE, true) interactBlock(expectedState, null, null, false) } - Properties.OPEN -> run open@ { + Properties.INVERTED -> { + val expectedState = state.with(Properties.INVERTED, !state.get(Properties.INVERTED)) + interactBlock(expectedState, null, null, false) + } + Properties.DELAY -> { + val expectedState = state.with(Properties.DELAY, state.cycle(Properties.DELAY).get(Properties.DELAY)) + interactBlock(expectedState, null, null, false) + } + Properties.COMPARATOR_MODE -> { + val expectedState = state.with(Properties.COMPARATOR_MODE, state.cycle(Properties.COMPARATOR_MODE).get(Properties.COMPARATOR_MODE)) + interactBlock(expectedState, null, null, false) + } + Properties.OPEN -> { val expectedState = state.with(Properties.OPEN, !state.get(Properties.OPEN)) interactBlock(expectedState, null, null, false) } + Properties.SLAB_TYPE -> { + if (targetState.blockState.get(Properties.SLAB_TYPE) != SlabType.DOUBLE) return@forEach + val slabType = state.get(Properties.SLAB_TYPE) + val side = when (slabType) { + SlabType.TOP -> Direction.DOWN + else -> Direction.UP + } + val expectedState = state.with(Properties.SLAB_TYPE, SlabType.DOUBLE) + interactBlock(expectedState, setOf(side), state.block.item, true) + } } } @@ -406,20 +430,6 @@ object BuildSimulator { } } - return if (placing) checkPlaceOn(pos, validHits, preProcessing, targetState, place, rotation, interact, inventory) - else checkInteractOn(pos, item, validHits, expectedState, state, rotation, interact, inventory) - } - - private fun SafeContext.checkInteractOn( - pos: BlockPos, - item: Item, - validHits: MutableList, - expectedState: BlockState, - currentState: BlockState, - rotation: RotationConfig, - interact: InteractionConfig, - inventory: InventoryConfig - ): BuildResult? { interact.pointSelection.select(validHits)?.let { checkedHit -> val checkedResult = checkedHit.hit val rotationTarget = lookAt(checkedHit.targetRotation, 0.001) @@ -427,7 +437,7 @@ object BuildSimulator { checkedResult.blockResult ?: return null, RotationRequest(rotationTarget, rotation), player.inventory.selectedSlot, - currentState, + state, expectedState ) @@ -504,12 +514,8 @@ object BuildSimulator { return PlaceResult.NotItemBlock(pos, optimalStack) } - val checked = blockItem.getPlacementContext(context) - if (checked == null) { - return PlaceResult.ScaffoldExceeded(pos, context) - } else { - context = checked - } + context = blockItem.getPlacementContext(context) + ?: return PlaceResult.ScaffoldExceeded(pos, context) lateinit var resultState: BlockState var rot = fakePlayer.rotation From 624e9ca8b1136c56acdc4a32f0bf0d5dff11e2cc Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 04:21:38 +0100 Subject: [PATCH 255/364] unused param --- .../interaction/construction/simulation/BuildSimulator.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index a69e35702..e40f9157b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -298,7 +298,6 @@ object BuildSimulator { side, item ?: player.inventory.mainHandStack.item, expectedState, - targetState, preProcessing, placing, interact, @@ -361,7 +360,6 @@ object BuildSimulator { sides: Set?, item: Item, expectedState: BlockState, - targetState: TargetState, preProcessing: PreProcessingInfo, placing: Boolean, interact: InteractionConfig, From f1776a5c15043c6eb59fe18c7c9a79666d728b4b Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 13:13:17 +0100 Subject: [PATCH 256/364] use direction entries rather than empty set for default PreProcessingInfo. Missed that when converting PreProcessingInfo to an interface --- .../interaction/construction/processing/PreProcessingInfo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt index f9cb95030..054e879f9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PreProcessingInfo.kt @@ -73,7 +73,7 @@ interface PreProcessingInfo { val DEFAULT = object : PreProcessingInfo { override val surfaceScan = SurfaceScan.DEFAULT override val ignore = setOf>() - override val sides = setOf() + override val sides = Direction.entries.toSet() override val shouldBeOmitted = false } } From dc7a2bff85e8505522f514dca4e45c206ffbc475 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 21:36:45 +0100 Subject: [PATCH 257/364] filter interactions that collide with current pending interactions --- .../interaction/request/interacting/InteractionManager.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt index 43d454aa7..b3ed9a0c5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -106,7 +106,9 @@ object InteractionManager : RequestHandler( private fun populateFrom(request: InteractionRequest) { setPendingConfigs(request) - potentialInteractions = request.contexts.toMutableList() + potentialInteractions = request.contexts + .filter { pendingInteractions.none { pending -> pending.context.blockPos == it.blockPos } } + .toMutableList() val pendingLimit = (request.build.maxPendingInteractions - pendingPlacements.size).coerceAtLeast(0) maxInteractionsThisTick = (request.build.interactionsPerTick.coerceAtMost(pendingLimit)) From d0d6452bc62bd68eab19ecd1ec3a30c99124b9a2 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 5 Jul 2025 21:47:35 +0100 Subject: [PATCH 258/364] update breaking on tick event input pre by default to please grim and match the hotbar swap timing --- .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 5013da63d..2e88c875e 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -37,7 +37,7 @@ class BreakSettings( override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis) override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position", visibility = vis) override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis) - override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", visibility = vis) + override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", visibility = vis) override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis) override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None } override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds", visibility = vis) From bc54d80e33207623d0082bb0eb35b2e50cc03a66 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 6 Jul 2025 14:20:59 +0100 Subject: [PATCH 259/364] await item instead of failing --- .../construction/result/BreakResult.kt | 10 +++++-- .../construction/result/BuildResult.kt | 21 ++++++++++++--- .../material/container/MaterialContainer.kt | 26 ++++++++++++++++++- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 1548a8408..b44735a84 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -99,8 +99,14 @@ sealed class BreakResult : BuildResult() { override fun resolve() = selectStack { isItem(badItem).not() - }.transfer(MainHandContainer, inventory) - ?: MaterialContainer.FailureTask("Couldn't find a tool for ${blockState.block.name.string} with $badItem in main hand.") + }.let { selection -> + selection.transfer(MainHandContainer, inventory) + ?: MaterialContainer.AwaitItemTask( + "Couldn't find a tool for ${blockState.block.name.string} with $badItem in main hand.", + selection, + inventory + ) + } override fun SafeContext.buildRenderer() { withPos(blockPos, color) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 1477b9c33..2b37e5a34 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -205,8 +205,15 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true - override fun resolve() = neededSelection - .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find $neededSelection anywhere.") + override fun resolve() = + neededSelection.let { selection -> + selection.transfer(MainHandContainer, inventory) + ?: MaterialContainer.AwaitItemTask( + "Couldn't find $neededSelection anywhere.", + selection, + inventory + ) + } override fun SafeContext.buildRenderer() { if (blockState(blockPos).isAir) { @@ -242,8 +249,14 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = - neededStack.select() - .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find ${neededStack.item.name.string} anywhere.") + neededStack.select().let { selection -> + selection.transfer(MainHandContainer, inventory) + ?: MaterialContainer.AwaitItemTask( + "Couldn't find ${neededStack.item.name.string} anywhere.", + selection, + inventory + ) + } override fun SafeContext.buildRenderer() { if (blockState(blockPos).isAir) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 925743a70..1cd96bf33 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -17,11 +17,16 @@ package com.lambda.interaction.material.container +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial import com.lambda.interaction.material.container.containers.ShulkerBoxContainer import com.lambda.interaction.material.transfer.TransferResult import com.lambda.task.Task +import com.lambda.util.Communication.logError import com.lambda.util.Nameable import com.lambda.util.item.ItemStackUtils.count import com.lambda.util.item.ItemStackUtils.empty @@ -29,7 +34,12 @@ import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import com.lambda.util.item.ItemStackUtils.spaceLeft import com.lambda.util.item.ItemUtils import com.lambda.util.item.ItemUtils.toItemCount -import com.lambda.util.text.* +import com.lambda.util.text.TextBuilder +import com.lambda.util.text.TextDsl +import com.lambda.util.text.buildText +import com.lambda.util.text.highlighted +import com.lambda.util.text.literal +import com.lambda.util.text.text import net.minecraft.item.ItemStack import net.minecraft.text.Text @@ -87,6 +97,20 @@ abstract class MaterialContainer( } } + class AwaitItemTask(override val name: String, val selection: StackSelection, inventory: InventoryConfig) : Task() { + init { + listen { + if (selection.findContainerWithMaterial(inventory) != null) { + success() + } + } + } + + override fun SafeContext.onStart() { + logError(name) + } + } + /** * Withdraws items from the container to the player's inventory. */ From 0ecb9491ddcfd3d42f5325bb8ac42320006f3819 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 7 Jul 2025 16:57:15 +0100 Subject: [PATCH 260/364] check all other position blocking managers when accepting action requests --- .../com/lambda/interaction/request/ManagerUtils.kt | 5 +++++ .../interaction/request/breaking/BreakManager.kt | 3 ++- .../request/interacting/InteractionManager.kt | 9 +++++++-- .../interaction/request/placing/PlaceManager.kt | 13 +++---------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt index 0d9a860cf..805f18e91 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt @@ -18,8 +18,13 @@ package com.lambda.interaction.request import com.lambda.util.reflections.getInstances +import net.minecraft.util.math.BlockPos object ManagerUtils { val managers = getInstances>() val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } + val positionBlockingManagers = getInstances() + + fun isPosBlocked(pos: BlockPos) = + positionBlockingManagers.any { manager -> manager.blockedPositions.any { blocked -> blocked == pos } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 58037de98..1f742425a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -31,6 +31,7 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.graphics.renderer.esp.builders.buildFilled import com.lambda.graphics.renderer.esp.builders.buildOutline import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.request.ManagerUtils.isPosBlocked import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler @@ -352,7 +353,7 @@ object BreakManager : RequestHandler( * @return if the break context can be accepted. */ private fun SafeContext.canAccept(ctx: BreakContext, breakConfig: BreakConfig): Boolean { - if (pendingBreaks.any { it.context.blockPos == ctx.blockPos }) return false + if (isPosBlocked(ctx.blockPos)) return false if (breakConfig.doubleBreak) { breakInfos diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt index b3ed9a0c5..ac6f6e077 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -25,6 +25,8 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.InteractionContext +import com.lambda.interaction.request.ManagerUtils.isPosBlocked +import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.interacting.InteractedBlockHandler.addPendingInteract @@ -44,13 +46,16 @@ object InteractionManager : RequestHandler( TickEvent.Input.Post, TickEvent.Player.Post, onOpen = { activeRequest?.let { processRequest(it) } } -) { +), PositionBlocking { private var activeRequest: InteractionRequest? = null private var potentialInteractions = mutableListOf() private var interactionsThisTick = 0 private var maxInteractionsThisTick = 0 + override val blockedPositions + get() = pendingInteractions.map { it.context.blockPos } + init { listen(priority = Int.MIN_VALUE) { activeRequest = null @@ -107,7 +112,7 @@ object InteractionManager : RequestHandler( private fun populateFrom(request: InteractionRequest) { setPendingConfigs(request) potentialInteractions = request.contexts - .filter { pendingInteractions.none { pending -> pending.context.blockPos == it.blockPos } } + .filter { !isPosBlocked(it.blockPos) } .toMutableList() val pendingLimit = (request.build.maxPendingInteractions - pendingPlacements.size).coerceAtLeast(0) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index fdce00661..faf0cd6f5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -25,6 +25,7 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.request.ManagerUtils.isPosBlocked import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler @@ -156,28 +157,20 @@ object PlaceManager : RequestHandler( * Filters the [request]'s [PlaceContext]s, placing them into the [potentialPlacements] collection, and * setting the maxPlacementsThisTick value. * - * @see canPlace + * @see isPosBlocked */ private fun populateFrom(request: PlaceRequest) { val place = request.build.placing setPendingConfigs(request) potentialPlacements = request.contexts - .filter { canPlace(it) } + .filter { !isPosBlocked(it.blockPos) } .toMutableList() val pendingLimit = (place.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0) maxPlacementsThisTick = (place.placementsPerTick.coerceAtMost(pendingLimit)) } - /** - * @return if none of the [pendingPlacements] match positions with the [placeContext] - */ - private fun canPlace(placeContext: PlaceContext) = - pendingPlacements.none { pending -> - pending.context.blockPos == placeContext.blockPos - } - /** * A modified version of the minecraft interactBlock method, renamed to better suit its usage. * From 2918f969918ecad6df48b82d38b74e3929c253fc Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 8 Jul 2025 03:43:21 +0100 Subject: [PATCH 261/364] fix point selection optimum mode. Was calculating average vector hit pos with integers :skull: --- .../request/rotating/visibilty/PointSelection.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt index 0aef9ac16..bf3f4f66b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt @@ -22,7 +22,7 @@ import com.lambda.interaction.request.rotating.RotationManager import com.lambda.util.math.distSq import com.lambda.util.math.times -enum class PointSelection(val select: (MutableList) -> VisibilityChecker.CheckedHit?) { +enum class PointSelection(val select: (Collection) -> VisibilityChecker.CheckedHit?) { ByRotation({ hits -> hits.minByOrNull { RotationManager.activeRotation dist it.targetRotation @@ -30,9 +30,9 @@ enum class PointSelection(val select: (MutableList }), Optimum({ hits -> val optimum = hits - .map { it.hit.pos } - .reduceOrNull { acc, pos -> pos?.let { acc?.add(it) } } - ?.times(1 / hits.size) + .mapNotNull { it.hit.pos } + .reduceOrNull { acc, pos -> acc.add(pos) } + ?.times(1 / hits.size.toDouble()) optimum?.let { hits.minByOrNull { it.hit.pos distSq optimum } From c269194bbc7399214f57e25469d4cfebc6cb9e6d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 8 Jul 2025 03:52:42 +0100 Subject: [PATCH 262/364] null check on hit positions --- .../interaction/request/rotating/visibilty/PointSelection.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt index bf3f4f66b..66c7056b1 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt @@ -35,7 +35,7 @@ enum class PointSelection(val select: (Collection) ?.times(1 / hits.size.toDouble()) optimum?.let { - hits.minByOrNull { it.hit.pos distSq optimum } + hits.minByOrNull { it.hit.pos?.distSq(optimum) ?: 0.0 } } }) } From 140a17fbfe1bef0e2f106b24bfa2725471cfead4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 9 Jul 2025 00:31:08 +0100 Subject: [PATCH 263/364] treat double slab placements like actual placements rather than interactions, and more build sim cleanup --- .../processing/PlacementProcessor.kt | 3 +- .../processing/ProcessorRegistry.kt | 15 +- .../preprocessors/AttachmentPreProcessor.kt | 3 +- .../preprocessors/AxisPreProcessor.kt | 3 +- .../preprocessors/BlockFacePreProcessor.kt | 3 +- .../preprocessors/BlockHalfPreProcessor.kt | 3 +- .../preprocessors/DoorHingePreProcessor.kt | 3 +- .../preprocessors/HopperFacingPreProcessor.kt | 3 +- .../preprocessors/SlabPreProcessor.kt | 12 +- .../construction/simulation/BuildSimulator.kt | 542 ++++++++---------- 10 files changed, 286 insertions(+), 304 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt index 40152ea08..230f98f5d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt @@ -18,8 +18,9 @@ package com.lambda.interaction.construction.processing import net.minecraft.block.BlockState +import net.minecraft.util.math.BlockPos abstract class PlacementProcessor { abstract fun acceptsState(state: BlockState): Boolean - abstract fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) + abstract fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt index 01d0ee2b1..d8ae95056 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt @@ -21,7 +21,10 @@ import com.lambda.core.Loadable import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.reflections.getInstances import net.minecraft.block.BlockState +import net.minecraft.block.SlabBlock +import net.minecraft.block.enums.SlabType import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos object ProcessorRegistry : Loadable { private val processors = getInstances() @@ -104,17 +107,23 @@ object ProcessorRegistry : Loadable { override fun load() = "Loaded ${processors.size} pre processors" - fun TargetState.getProcessingInfo(): PreProcessingInfo = + fun TargetState.getProcessingInfo(pos: BlockPos): PreProcessingInfo = (this as? TargetState.State)?.let { state -> - processorCache.getOrPut(state.blockState) { + val get: () -> PreProcessingInfo = { + val infoAccumulator = PreProcessingInfoAccumulator() processors.forEach { if (!it.acceptsState(state.blockState)) return@forEach - it.preProcess(state.blockState, infoAccumulator) + it.preProcess(state.blockState, pos, infoAccumulator) } infoAccumulator.complete() } + if (isExemptFromCache(state)) get() + else processorCache.getOrPut(state.blockState, get) } ?: PreProcessingInfo.DEFAULT + + private fun isExemptFromCache(state: TargetState.State) = + state.blockState.block is SlabBlock && state.blockState.get(Properties.SLAB_TYPE) == SlabType.DOUBLE } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt index e64e64390..823f4b76a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AttachmentPreProcessor.kt @@ -22,6 +22,7 @@ import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulat import net.minecraft.block.BlockState import net.minecraft.block.enums.Attachment import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @@ -30,7 +31,7 @@ object AttachmentPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.properties.contains(Properties.ATTACHMENT) - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) { val attachment = state.get(Properties.ATTACHMENT) ?: return with (accumulator) { when (attachment) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt index 4c917b6c7..2497a82e5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/AxisPreProcessor.kt @@ -21,6 +21,7 @@ import com.lambda.interaction.construction.processing.PlacementProcessor import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos // Collected using reflections and then accessed from a collection in ProcessorRegistry @Suppress("unused") @@ -28,7 +29,7 @@ object AxisPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.AXIS).isPresent - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) { val axis = state.get(Properties.AXIS) accumulator.retainSides { side -> side.axis == axis diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt index 38cabc348..8000cd0fc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockFacePreProcessor.kt @@ -22,6 +22,7 @@ import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulat import net.minecraft.block.BlockState import net.minecraft.block.enums.BlockFace import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @@ -30,7 +31,7 @@ object BlockFacePreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.BLOCK_FACE).isPresent - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) { val property = state.get(Properties.BLOCK_FACE) ?: return with (accumulator) { when (property) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt index 66dc94448..82e9d616a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/BlockHalfPreProcessor.kt @@ -24,6 +24,7 @@ import com.lambda.interaction.construction.verify.SurfaceScan import net.minecraft.block.BlockState import net.minecraft.block.enums.BlockHalf import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @@ -32,7 +33,7 @@ object BlockHalfPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.getOrEmpty(Properties.BLOCK_HALF).isPresent - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) { val slab = state.get(Properties.BLOCK_HALF) ?: return val surfaceScan = when (slab) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt index 37b11a238..07abad61d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/DoorHingePreProcessor.kt @@ -25,6 +25,7 @@ import com.lambda.threading.runSafe import net.minecraft.block.BlockState import net.minecraft.block.enums.DoorHinge import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @@ -33,7 +34,7 @@ object DoorHingePreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.properties.contains(Properties.DOOR_HINGE) - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) = + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) = runSafe { val side = state.get(Properties.DOOR_HINGE) ?: return@runSafe val scanner = when (state.get(Properties.HORIZONTAL_FACING) ?: return@runSafe) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt index 447d50586..f37848f58 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/HopperFacingPreProcessor.kt @@ -21,6 +21,7 @@ import com.lambda.interaction.construction.processing.PlacementProcessor import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import net.minecraft.block.BlockState import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @@ -29,7 +30,7 @@ object HopperFacingPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.properties.contains(Properties.HOPPER_FACING) - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) { val facing = state.get(Properties.HOPPER_FACING) ?: return when { facing.axis == Direction.Axis.Y -> accumulator.retainSides { it.axis == Direction.Axis.Y } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt index 191926f3e..f46b986bc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/SlabPreProcessor.kt @@ -21,10 +21,13 @@ import com.lambda.interaction.construction.processing.PlacementProcessor import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState import net.minecraft.block.SlabBlock import net.minecraft.block.enums.SlabType import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction // Collected using reflections and then accessed from a collection in ProcessorRegistry @@ -32,15 +35,20 @@ import net.minecraft.util.math.Direction object SlabPreProcessor : PlacementProcessor() { override fun acceptsState(state: BlockState) = state.block is SlabBlock - override fun preProcess(state: BlockState, accumulator: PreProcessingInfoAccumulator) { + override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) { val slab = state.get(Properties.SLAB_TYPE) ?: return + val currentState = runSafe { blockState(pos) } ?: return val surfaceScan = when (slab) { SlabType.BOTTOM -> SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.Y) SlabType.TOP -> SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.Y) SlabType.DOUBLE -> { accumulator.addIgnores(Properties.SLAB_TYPE) - SurfaceScan(ScanMode.FULL, Direction.Axis.Y) + if (currentState.block !is SlabBlock) SurfaceScan.DEFAULT + else when (currentState.get(Properties.SLAB_TYPE)) { + SlabType.BOTTOM -> SurfaceScan(ScanMode.GREATER_BLOCK_HALF, Direction.Axis.Y) + else -> SurfaceScan(ScanMode.LESSER_BLOCK_HALF, Direction.Axis.Y) + } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index e40f9157b..84c08d61f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -60,7 +60,6 @@ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.BlockUtils.isNotEmpty -import com.lambda.util.BlockUtils.item import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn import com.lambda.util.item.ItemStackUtils.equal @@ -71,6 +70,7 @@ import com.lambda.util.player.gamemode import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState import net.minecraft.block.OperatorBlock +import net.minecraft.block.SlabBlock import net.minecraft.block.enums.SlabType import net.minecraft.block.pattern.CachedBlockPosition import net.minecraft.enchantment.Enchantments @@ -99,7 +99,7 @@ object BuildSimulator { build: BuildConfig = TaskFlowModule.build, ) = runSafe { structure.entries.flatMap { (pos, target) -> - val preProcessing = target.getProcessingInfo() + val preProcessing = target.getProcessingInfo(pos) checkRequirements(pos, target, build).let { if (it.isEmpty()) return@let return@flatMap it @@ -175,52 +175,43 @@ object BuildSimulator { return acc } - private fun SafeContext.checkPlaceResults( + private fun SafeContext.checkPostProcessResults( pos: BlockPos, eye: Vec3d, preProcessing: PreProcessingInfo, - target: TargetState, - place: PlaceConfig, + targetState: TargetState, interact: InteractionConfig, + place: PlaceConfig, rotation: RotationConfig, inventory: InventoryConfig ): Set { - val acc = mutableSetOf() - val targetPosState = blockState(pos) - - if (target.isEmpty() || !targetPosState.isReplaceable) return acc - - preProcessing.sides.forEach { neighbor -> - val hitPos = if (!place.airPlace.isEnabled() && targetPosState.isEmpty) - pos.offset(neighbor) - else pos - val hitSide = neighbor.opposite + if (targetState !is TargetState.State) return emptySet() - val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> - if (!outlineShape.isEmpty || !place.airPlace.isEnabled()) outlineShape - else VoxelShapes.fullCube() - } - if (voxelShape.isEmpty) return@forEach + val acc = mutableSetOf() - val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } - val verify: CheckedHit.() -> Boolean = { - hit.blockResult?.blockPos == hitPos && hit.blockResult?.side == hitSide - } + val state = blockState(pos) + if (!targetState.matches(state, pos, world, preProcessing.ignore)) + return acc + val interactBlock: (BlockState, Set?, Item?, Boolean) -> Unit = interactBlock@ { expectedState, sides, item, placing -> + val boxes = state.getOutlineShape(world, pos).boundingBoxes.map { it.offset(pos) } val validHits = mutableListOf() + val blockedHits = mutableSetOf() val misses = mutableSetOf() - val reachSq = interact.interactReach.pow(2) + val airPlace = placing && place.airPlace.isEnabled() boxes.forEach { box -> - val sides = if (interact.checkSideVisibility) { - box.getVisibleSurfaces(eye).intersect(setOf(hitSide)) - } else { - setOf(hitSide) - } + val refinedSides = if (interact.checkSideVisibility) { + box.getVisibleSurfaces(eye).let { visibleSides -> + sides?.let { specific -> + visibleSides.intersect(specific) + } ?: visibleSides.toSet() + } + } else sides ?: Direction.entries.toSet() - scanSurfaces(box, sides, interact.resolution, preProcessing.surfaceScan) { _, vec -> + scanSurfaces(box, refinedSides, interact.resolution, preProcessing.surfaceScan) { hitSide, vec -> val distSquared = eye distSq vec - if (distSquared > reachSq) { + if (distSquared > interact.interactReach.pow(2)) { misses.add(vec) return@scanSurfaces } @@ -230,23 +221,26 @@ object BuildSimulator { val hit = if (interact.strictRayCast) { val rayCast = newRotation.rayCast(interact.interactReach, eye) when { - rayCast != null && (!place.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> + rayCast != null && (!airPlace || eye distSq rayCast.pos <= distSquared) -> rayCast.blockResult - place.airPlace.isEnabled() -> { + airPlace -> { val hitVec = newRotation.castBox(box, interact.interactReach, eye) - BlockHitResult(hitVec, hitSide, hitPos, false) + BlockHitResult(hitVec, hitSide, pos, false) } else -> null } } else { val hitVec = newRotation.castBox(box, interact.interactReach, eye) - BlockHitResult(hitVec, hitSide, hitPos, false) + BlockHitResult(hitVec, hitSide, pos, false) } ?: return@scanSurfaces val checked = CheckedHit(hit, newRotation, interact.interactReach) - if (!checked.verify()) return@scanSurfaces + if (hit.blockResult?.blockPos != pos) { + blockedHits.add(vec) + return@scanSurfaces + } validHits.add(checked) } @@ -255,59 +249,41 @@ object BuildSimulator { if (validHits.isEmpty()) { if (misses.isNotEmpty()) { acc.add(BuildResult.OutOfReach(pos, eye, misses)) - return@forEach + } else { + //ToDo: Must clean up surface scan usage / renders. Added temporary direction until changes are made + acc.add(BuildResult.NotVisible(pos, pos, Direction.UP, eye.distanceTo(pos.vecOf(Direction.UP)))) } - - acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.vecOf(hitSide)))) - return@forEach + return@interactBlock } - checkPlaceOn(pos, validHits, preProcessing, target, place, rotation, interact, inventory)?.let { placeResult -> - acc.add(placeResult) - } - } - - return acc - } - - private fun SafeContext.checkPostProcessResults( - pos: BlockPos, - eye: Vec3d, - preProcessing: PreProcessingInfo, - targetState: TargetState, - interact: InteractionConfig, - place: PlaceConfig, - rotation: RotationConfig, - inventory: InventoryConfig - ): Set { - if (targetState !is TargetState.State) return emptySet() - - val acc = mutableSetOf() + interact.pointSelection.select(validHits)?.let { checkedHit -> + val checkedResult = checkedHit.hit + val rotationTarget = lookAt(checkedHit.targetRotation, 0.001) + val context = InteractionContext( + checkedResult.blockResult ?: return@interactBlock, + RotationRequest(rotationTarget, rotation), + player.inventory.selectedSlot, + state, + expectedState + ) - val state = blockState(pos) - if (!targetState.matches(state, pos, world, preProcessing.ignore)) - return acc + val stackSelection = (item ?: player.mainHandStack.item).select() + val hotbarCandidates = selectContainer { + matches(stackSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) + }.let { predicate -> + stackSelection.containerWithMaterial(inventory, predicate) + } - val interactBlock: (BlockState, Set?, Item?, Boolean) -> Unit = - { expectedState, side, item, placing -> - //ToDo: Refactor this function chain into a better solution - interactWithBlock( - pos, - state, - eye, - side, - item ?: player.inventory.mainHandStack.item, - expectedState, - preProcessing, - placing, - interact, - place, - rotation, - inventory - )?.let { result -> - acc.add(result) + if (hotbarCandidates.isEmpty()) { + acc.add(BuildResult.WrongItemSelection(pos, context, stackSelection, player.mainHandStack, inventory)) + return@interactBlock + } else { + context.hotbarIndex = player.hotbar.indexOf(hotbarCandidates.first().matchingStacks(stackSelection).first()) } + + acc.add(InteractResult.Interact(pos, context)) } + } val mismatchedProperties = state.properties.filter { state.get(it) != targetState.blockState.get(it) } mismatchedProperties.forEach { property -> @@ -335,13 +311,9 @@ object BuildSimulator { } Properties.SLAB_TYPE -> { if (targetState.blockState.get(Properties.SLAB_TYPE) != SlabType.DOUBLE) return@forEach - val slabType = state.get(Properties.SLAB_TYPE) - val side = when (slabType) { - SlabType.TOP -> Direction.DOWN - else -> Direction.UP + checkPlaceResults(pos, eye, preProcessing, targetState, place, interact, rotation, inventory).let { placeResults -> + acc.addAll(placeResults) } - val expectedState = state.with(Properties.SLAB_TYPE, SlabType.DOUBLE) - interactBlock(expectedState, setOf(side), state.block.item, true) } } } @@ -353,256 +325,242 @@ object BuildSimulator { return acc } - private fun SafeContext.interactWithBlock( + private fun SafeContext.checkPlaceResults( pos: BlockPos, - state: BlockState, eye: Vec3d, - sides: Set?, - item: Item, - expectedState: BlockState, preProcessing: PreProcessingInfo, - placing: Boolean, - interact: InteractionConfig, + targetState: TargetState, place: PlaceConfig, + interact: InteractionConfig, rotation: RotationConfig, inventory: InventoryConfig - ): BuildResult? { - val boxes = state.getOutlineShape(world, pos).boundingBoxes.map { it.offset(pos) } - val validHits = mutableListOf() - val blockedHits = mutableSetOf() - val misses = mutableSetOf() - val airPlace = placing && place.airPlace.isEnabled() - - boxes.forEach { box -> - val refinedSides = if (interact.checkSideVisibility) { - box.getVisibleSurfaces(eye).let { visibleSides -> - sides?.let { specific -> - visibleSides.intersect(specific) - } ?: visibleSides.toSet() - } - } else sides ?: Direction.entries.toSet() - - scanSurfaces(box, refinedSides, interact.resolution, preProcessing.surfaceScan) { hitSide, vec -> - val distSquared = eye distSq vec - if (distSquared > interact.interactReach.pow(2)) { - misses.add(vec) - return@scanSurfaces - } - - val newRotation = eye.rotationTo(vec) - - val hit = if (interact.strictRayCast) { - val rayCast = newRotation.rayCast(interact.interactReach, eye) - when { - rayCast != null && (!airPlace || eye distSq rayCast.pos <= distSquared) -> - rayCast.blockResult - - airPlace -> { - val hitVec = newRotation.castBox(box, interact.interactReach, eye) - BlockHitResult(hitVec, hitSide, pos, false) - } + ): Set { + val acc = mutableSetOf() + val currentState = blockState(pos) - else -> null - } - } else { - val hitVec = newRotation.castBox(box, interact.interactReach, eye) - BlockHitResult(hitVec, hitSide, pos, false) - } ?: return@scanSurfaces + val statePromoting = currentState.block is SlabBlock && targetState.matches(currentState, pos, world, preProcessing.ignore) + if (targetState.isEmpty() || (!currentState.isReplaceable && !statePromoting)) return acc - val checked = CheckedHit(hit, newRotation, interact.interactReach) - if (hit.blockResult?.blockPos != pos) { - blockedHits.add(vec) - return@scanSurfaces - } + preProcessing.sides.forEach { neighbor -> + val hitPos = if (!place.airPlace.isEnabled() && (currentState.isEmpty || statePromoting)) + pos.offset(neighbor) + else pos + val hitSide = neighbor.opposite - validHits.add(checked) + val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> + if (!outlineShape.isEmpty || !place.airPlace.isEnabled()) outlineShape + else VoxelShapes.fullCube() } - } + if (voxelShape.isEmpty) return@forEach - if (validHits.isEmpty()) { - return if (misses.isNotEmpty()) { - BuildResult.OutOfReach(pos, eye, misses) - } else { - //ToDo: Must clean up surface scan usage / renders. Added temporary direction until changes are made - BuildResult.NotVisible(pos, pos, Direction.UP, eye.distanceTo(pos.vecOf(Direction.UP))) + val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } + val verify: CheckedHit.() -> Boolean = { + hit.blockResult?.blockPos == hitPos && hit.blockResult?.side == hitSide } - } - interact.pointSelection.select(validHits)?.let { checkedHit -> - val checkedResult = checkedHit.hit - val rotationTarget = lookAt(checkedHit.targetRotation, 0.001) - val context = InteractionContext( - checkedResult.blockResult ?: return null, - RotationRequest(rotationTarget, rotation), - player.inventory.selectedSlot, - state, - expectedState - ) - - val stackSelection = item.select() - val hotbarCandidates = selectContainer { - matches(stackSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) - }.let { predicate -> - stackSelection.containerWithMaterial(inventory, predicate) - } + val validHits = mutableListOf() + val misses = mutableSetOf() + val reachSq = interact.interactReach.pow(2) - if (hotbarCandidates.isEmpty()) { - return BuildResult.WrongItemSelection(pos, context, stackSelection, player.mainHandStack, inventory) - } else { - context.hotbarIndex = player.hotbar.indexOf(hotbarCandidates.first().matchingStacks(stackSelection).first()) - } + boxes.forEach { box -> + val sides = if (interact.checkSideVisibility) { + box.getVisibleSurfaces(eye).intersect(setOf(hitSide)) + } else setOf(hitSide) - return InteractResult.Interact(pos, context) - } + scanSurfaces(box, sides, interact.resolution, preProcessing.surfaceScan) { _, vec -> + val distSquared = eye distSq vec + if (distSquared > reachSq) { + misses.add(vec) + return@scanSurfaces + } - return null - } + val newRotation = eye.rotationTo(vec) - private fun SafeContext.checkPlaceOn( - pos: BlockPos, - validHits: MutableList, - preProcessing: PreProcessingInfo, - targetState: TargetState, - place: PlaceConfig, - rotation: RotationConfig, - interact: InteractionConfig, - inventory: InventoryConfig - ): BuildResult? { - interact.pointSelection.select(validHits)?.let { checkedHit -> - val optimalStack = targetState.getStack(world, pos) + val hit = if (interact.strictRayCast) { + val rayCast = newRotation.rayCast(interact.interactReach, eye) + when { + rayCast != null && (!place.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) -> + rayCast.blockResult - // ToDo: For each hand and sneak or not? - val fakePlayer = copyPlayer(player).apply { - this.rotation = RotationManager.serverRotation - } + place.airPlace.isEnabled() -> { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, hitSide, hitPos, false) + } - val checkedResult = checkedHit.hit - - val usageContext = ItemUsageContext( - fakePlayer, - Hand.MAIN_HAND, - checkedResult.blockResult, - ) - val cachePos = CachedBlockPosition( - usageContext.world, usageContext.blockPos, false - ) - val canBePlacedOn = optimalStack.canPlaceOn( - usageContext.world.registryManager.get(RegistryKeys.BLOCK), - cachePos, - ) - if (!player.abilities.allowModifyWorld && !canBePlacedOn) { - return PlaceResult.IllegalUsage(pos) - } + else -> null + } + } else { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, hitSide, hitPos, false) + } ?: return@scanSurfaces - var context = ItemPlacementContext(usageContext) + val checked = CheckedHit(hit, newRotation, interact.interactReach) + if (!checked.verify()) return@scanSurfaces - if (context.blockPos != pos) { - return PlaceResult.UnexpectedPosition(pos, context.blockPos) + validHits.add(checked) + } } - if (!optimalStack.item.isEnabled(world.enabledFeatures)) { - return PlaceResult.BlockFeatureDisabled(pos, optimalStack) - } + if (validHits.isEmpty()) { + if (misses.isNotEmpty()) { + acc.add(BuildResult.OutOfReach(pos, eye, misses)) + return@forEach + } - if (!context.canPlace()) { - return PlaceResult.CantReplace(pos, context) + acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.vecOf(hitSide)))) + return@forEach } - val blockItem = optimalStack.item as? BlockItem ?: run { - return PlaceResult.NotItemBlock(pos, optimalStack) - } + interact.pointSelection.select(validHits)?.let { checkedHit -> + val optimalStack = targetState.getStack(world, pos) - context = blockItem.getPlacementContext(context) - ?: return PlaceResult.ScaffoldExceeded(pos, context) + // ToDo: For each hand and sneak or not? + val fakePlayer = copyPlayer(player).apply { + this.rotation = RotationManager.serverRotation + } - lateinit var resultState: BlockState - var rot = fakePlayer.rotation + val checkedResult = checkedHit.hit - val simulatePlaceState = placeState@ { - resultState = blockItem.getPlacementState(context) - ?: return@placeState PlaceResult.BlockedByEntity(pos) + val usageContext = ItemUsageContext( + fakePlayer, + Hand.MAIN_HAND, + checkedResult.blockResult, + ) + val cachePos = CachedBlockPosition( + usageContext.world, usageContext.blockPos, false + ) + val canBePlacedOn = optimalStack.canPlaceOn( + usageContext.world.registryManager.get(RegistryKeys.BLOCK), + cachePos, + ) + if (!player.abilities.allowModifyWorld && !canBePlacedOn) { + acc.add(PlaceResult.IllegalUsage(pos)) + return@forEach + } - return@placeState if (!targetState.matches(resultState, pos, world, preProcessing.ignore)) - PlaceResult.NoIntegrity(pos, resultState, context, (targetState as? TargetState.State)?.blockState) - else null - } + var context = ItemPlacementContext(usageContext) - val currentDirIsValid = simulatePlaceState()?.let { basePlaceResult -> - if (!place.rotateForPlace) { - return basePlaceResult + if (context.blockPos != pos) { + acc.add(PlaceResult.UnexpectedPosition(pos, context.blockPos)) + return@forEach } - false - } ?: true - - run rotate@ { - if (!place.axisRotate) { - fakePlayer.rotation = checkedHit.targetRotation - simulatePlaceState()?.let { rotatedPlaceResult -> - return rotatedPlaceResult - } - rot = fakePlayer.rotation - return@rotate + + if (!optimalStack.item.isEnabled(world.enabledFeatures)) { + acc.add(PlaceResult.BlockFeatureDisabled(pos, optimalStack)) + return@forEach } - fakePlayer.rotation = player.rotation - if (simulatePlaceState() == null) { - rot = fakePlayer.rotation - return@rotate + if (!context.canPlace() && !statePromoting) { + acc.add(PlaceResult.CantReplace(pos, context)) + return@forEach } - PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> - fakePlayer.rotation = direction.rotation - when (val placeResult = simulatePlaceState()) { - is PlaceResult.BlockedByEntity -> { - return placeResult - } + val blockItem = optimalStack.item as? BlockItem ?: run { + acc.add(PlaceResult.NotItemBlock(pos, optimalStack)) + return@forEach + } + + context = blockItem.getPlacementContext(context) + ?: run { + acc.add(PlaceResult.ScaffoldExceeded(pos, context)) + return@forEach + } + + lateinit var resultState: BlockState + var rot = fakePlayer.rotation - is PlaceResult.NoIntegrity -> { - if (index != PlaceDirection.entries.lastIndex) return@direction - return placeResult + val simulatePlaceState = placeState@ { + resultState = blockItem.getPlacementState(context) + ?: return@placeState PlaceResult.BlockedByEntity(pos) + + return@placeState if (!targetState.matches(resultState, pos, world, preProcessing.ignore)) + PlaceResult.NoIntegrity(pos, resultState, context, (targetState as? TargetState.State)?.blockState) + else null + } + + val currentDirIsValid = simulatePlaceState()?.let { basePlaceResult -> + if (!place.rotateForPlace) { + acc.add(basePlaceResult) + return@forEach + } + false + } ?: true + + run rotate@ { + if (!place.axisRotate) { + fakePlayer.rotation = checkedHit.targetRotation + simulatePlaceState()?.let { rotatedPlaceResult -> + acc.add(rotatedPlaceResult) + return@forEach } + rot = fakePlayer.rotation + return@rotate + } + + fakePlayer.rotation = player.rotation + if (simulatePlaceState() == null) { + rot = fakePlayer.rotation + return@rotate + } - else -> { - rot = fakePlayer.rotation - return@rotate + PlaceDirection.entries.asReversed().forEachIndexed direction@ { index, direction -> + fakePlayer.rotation = direction.rotation + when (val placeResult = simulatePlaceState()) { + is PlaceResult.BlockedByEntity -> { + acc.add(placeResult) + return@forEach + } + + is PlaceResult.NoIntegrity -> { + if (index != PlaceDirection.entries.lastIndex) return@direction + acc.add(placeResult) + return@forEach + } + + else -> { + rot = fakePlayer.rotation + return@rotate + } } } } - } - val blockHit = checkedResult.blockResult ?: return null - val hitBlock = blockState(blockHit.blockPos).block - val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks - - val rotationRequest = if (place.axisRotate) { - lookInDirection(PlaceDirection.fromRotation(rot)) - } else lookAt(rot, 0.001) - - val placeContext = PlaceContext( - blockHit, - RotationRequest(rotationRequest, rotation), - player.inventory.selectedSlot, - context.blockPos, - blockState(context.blockPos), - resultState, - shouldSneak, - false, - currentDirIsValid - ) - - val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) - if (targetState is TargetState.Stack && !targetState.itemStack.equal(currentHandStack)) { - return BuildResult.WrongStack(pos, placeContext, targetState.itemStack, inventory) - } + val blockHit = checkedResult.blockResult ?: return@forEach + val hitBlock = blockState(blockHit.blockPos).block + val shouldSneak = hitBlock::class in BlockUtils.interactionBlocks - if (optimalStack.item != currentHandStack.item) { - return BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), currentHandStack, inventory) - } + val rotationRequest = if (place.axisRotate) { + lookInDirection(PlaceDirection.fromRotation(rot)) + } else lookAt(rot, 0.001) - return PlaceResult.Place(pos, placeContext) + val placeContext = PlaceContext( + blockHit, + RotationRequest(rotationRequest, rotation), + player.inventory.selectedSlot, + context.blockPos, + blockState(context.blockPos), + resultState, + shouldSneak, + false, + currentDirIsValid + ) + + val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) + if (targetState is TargetState.Stack && !targetState.itemStack.equal(currentHandStack)) { + acc.add(BuildResult.WrongStack(pos, placeContext, targetState.itemStack, inventory)) + return@forEach + } + + if (optimalStack.item != currentHandStack.item) { + acc.add(BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), currentHandStack, inventory)) + return@forEach + } + + acc.add(PlaceResult.Place(pos, placeContext)) + } } - return null + return acc } private fun SafeContext.checkBreakResults( From fcc88608d9ea29560d89d735f8058e78eef49afb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 11 Jul 2025 17:20:23 +0100 Subject: [PATCH 264/364] allow use of the rest of the hotbar for placing --- .../construction/context/PlaceContext.kt | 2 +- .../construction/simulation/BuildSimulator.kt | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index e131487d6..afa32d260 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -35,7 +35,7 @@ import java.awt.Color data class PlaceContext( override val result: BlockHitResult, override val rotation: RotationRequest, - override val hotbarIndex: Int, + override var hotbarIndex: Int, override val blockPos: BlockPos, override val cachedState: BlockState, override val expectedState: BlockState, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 84c08d61f..56c53fe82 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -62,7 +62,6 @@ import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.BlockUtils.isNotEmpty import com.lambda.util.BlockUtils.vecOf import com.lambda.util.Communication.warn -import com.lambda.util.item.ItemStackUtils.equal import com.lambda.util.math.distSq import com.lambda.util.player.SlotUtils.hotbar import com.lambda.util.player.copyPlayer @@ -545,17 +544,19 @@ object BuildSimulator { currentDirIsValid ) - val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) - if (targetState is TargetState.Stack && !targetState.itemStack.equal(currentHandStack)) { - acc.add(BuildResult.WrongStack(pos, placeContext, targetState.itemStack, inventory)) - return@forEach + val selection = optimalStack.item.select() + val hotbarSelection = selectContainer { ofAnyType(MaterialContainer.Rank.HOTBAR) } + val containerStacks = selection.containerWithMaterial(inventory, hotbarSelection).firstOrNull()?.stacks ?: run { + acc.add(BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), player.mainHandStack, inventory)) + return acc } - - if (optimalStack.item != currentHandStack.item) { - acc.add(BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), currentHandStack, inventory)) - return@forEach + val stack = selection.filterStacks(containerStacks).run { + firstOrNull { player.inventory.getSlotWithStack(it) == player.inventory.selectedSlot } + ?: first() } + placeContext.hotbarIndex = player.inventory.getSlotWithStack(stack) + acc.add(PlaceResult.Place(pos, placeContext)) } } @@ -716,7 +717,7 @@ object BuildSimulator { ) val silentSwapSelection = selectContainer { - matches(stackSelection) and ofAnyType(MaterialContainer.Rank.HOTBAR) + ofAnyType(MaterialContainer.Rank.HOTBAR) } val swapCandidates = stackSelection.containerWithMaterial(inventory, silentSwapSelection) From 7e804e30271f321fc78add42ccaa050d99109f67 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Fri, 11 Jul 2025 17:22:40 +0100 Subject: [PATCH 265/364] small cleanup --- .../interaction/construction/simulation/BuildSimulator.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 56c53fe82..8ef89d76d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -545,12 +545,12 @@ object BuildSimulator { ) val selection = optimalStack.item.select() - val hotbarSelection = selectContainer { ofAnyType(MaterialContainer.Rank.HOTBAR) } - val containerStacks = selection.containerWithMaterial(inventory, hotbarSelection).firstOrNull()?.stacks ?: run { + val containerSelection = selectContainer { ofAnyType(MaterialContainer.Rank.HOTBAR) } + val container = selection.containerWithMaterial(inventory, containerSelection).firstOrNull() ?: run { acc.add(BuildResult.WrongItemSelection(pos, placeContext, optimalStack.item.select(), player.mainHandStack, inventory)) return acc } - val stack = selection.filterStacks(containerStacks).run { + val stack = selection.filterStacks(container.stacks).run { firstOrNull { player.inventory.getSlotWithStack(it) == player.inventory.selectedSlot } ?: first() } From 48cb6bff992caab4f19cb31c09ef3d02f371639b Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 12 Jul 2025 00:31:13 +0200 Subject: [PATCH 266/364] OCD --- .../interaction/construction/simulation/BuildSimulator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 8ef89d76d..7d45d1ab6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -585,7 +585,7 @@ object BuildSimulator { /* player is standing on top of the block */ val pBox = player.boundingBox - val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) + val aabb = pBox.withMinY(pBox.minY - 1.0E-6) world.findSupportingBlockPos(player, aabb).orElse(null)?.let { support -> if (support != pos) return@let val belowSupport = blockState(support.down()) From 46405bc0201f75facf0220d9bd4b493d646c7c43 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 12 Jul 2025 14:03:49 +0100 Subject: [PATCH 267/364] naming convention --- .../main/kotlin/com/lambda/module/modules/debug/StateInfo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt index adcc1be0a..46ea910ca 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt @@ -30,7 +30,7 @@ import net.minecraft.state.property.Property import net.minecraft.util.hit.BlockHitResult object StateInfo : Module( - "State Info", + "StateInfo", "Prints the target block's state into chat", setOf(ModuleTag.DEBUG) ) { From df00f719f228f2ef377db53e63bc5ff2f431c3b7 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 12 Jul 2025 14:07:43 +0100 Subject: [PATCH 268/364] "WorldColors" as opposed to "World Colors" --- .../main/kotlin/com/lambda/module/modules/render/WorldColors.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/WorldColors.kt b/common/src/main/kotlin/com/lambda/module/modules/render/WorldColors.kt index 89f208207..ec5b0216e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/WorldColors.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/WorldColors.kt @@ -24,7 +24,7 @@ import net.minecraft.util.math.Vec3d import java.awt.Color object WorldColors : Module( - name = "World Colors", + name = "WorldColors", description = "Changes the color of the sky", defaultTags = setOf(ModuleTag.RENDER) ) { From 1b253dd472ee46f849363753aa365af88c643b72 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 12 Jul 2025 18:04:34 +0100 Subject: [PATCH 269/364] AntiAim module and wrapping rotations --- .../interaction/request/rotating/Rotation.kt | 2 + .../request/rotating/RotationManager.kt | 3 +- .../lambda/module/modules/player/AntiAim.kt | 224 ++++++++++++++++++ 3 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt index f54c94224..bd141cccd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt @@ -102,6 +102,8 @@ data class Rotation(val yaw: Double, val pitch: Double) { } fun wrap(deg: Double) = wrapDegrees(deg) + fun wrap(deg: Float) = wrapDegrees(deg) + fun Rotation.wrap() = Rotation(wrap(yaw), pitch) fun Rotation.lerp(other: Rotation, delta: Double): Rotation { // Calculate the wrapped difference to ensure we take the shortest path diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt index 3bdd734f4..7cc966ee4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt @@ -31,6 +31,7 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotating.Rotation.Companion.slerp +import com.lambda.interaction.request.rotating.Rotation.Companion.wrap import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.modules.client.Baritone import com.lambda.threading.runGameScheduled @@ -149,7 +150,7 @@ object RotationManager : RequestHandler( val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier val turnSpeed = request.turnSpeed() * speedMultiplier - serverRotation.slerp(rotationTo, turnSpeed) + serverRotation.slerp(rotationTo, turnSpeed).wrap() } ?: player.rotation } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt new file mode 100644 index 000000000..64d4a72bd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt @@ -0,0 +1,224 @@ +/* + * Copyright 2025 Lambda + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package com.lambda.module.modules.player + +import com.lambda.config.groups.RotationSettings +import com.lambda.context.SafeContext +import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.Rotation.Companion.wrap +import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.interaction.request.rotating.visibilty.lookAt +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.distSq +import net.minecraft.entity.Entity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.math.MathHelper.wrapDegrees +import java.util.* +import java.util.random.RandomGenerator + +object AntiAim : Module( + "AntiAim", + "Rotates the player using the given configs", + setOf(ModuleTag.PLAYER, ModuleTag.MOVEMENT) +) { + private val page by setting("Page", Page.General) + private val yaw by setting("Yaw Mode", YawMode.Spin, "The mode used when setting the players yaw") { page == Page.General } + .onValueChange { _, to -> + if (to == YawMode.Custom) { + // To bypass recursion issue + setConfigCustomYaw(player.yaw) + } + } + private val spinMode by setting("Spin Mode", LeftRight.Right) { yaw == YawMode.Spin && page == Page.General } + private val sideMode by setting("Side Mode", LeftRight.Left) { yaw == YawMode.Sideways && page == Page.General } + private var customYaw by setting("Custom Yaw", 0f, -179f..180f, 1f) { yaw == YawMode.Custom && page == Page.General } + private val yawPlayerMode by setting("Yaw Player mode", PlayerMode.Closest) { yaw == YawMode.Player && page == Page.General } + + private val pitch by setting("Pitch Mode", PitchMode.UpAndDown, "The mode used when setting the players pitch") { page == Page.General } + .onValueChange { _, to -> + if (to == PitchMode.Custom) { + // To bypass recursion issue + setConfigCustomPitch(player.pitch) + } + } + private val verticalMode by setting("Vertical Mode", VerticalMode.Up) { pitch == PitchMode.Vertical && page == Page.General } + private var customPitch by setting("Custom Pitch", 0f, -90f..90f, 1f) { pitch == PitchMode.Custom && page == Page.General } + private val pitchPlayerMode by setting("Pitch Player Mode", PlayerMode.Closest) { pitch == PitchMode.Player && page == Page.General } + + private val yawSpeed by setting("Yaw Speed", 30, 1..90, 1, "Yaw rotation degrees per tick", "°") { page == Page.General && yaw != YawMode.None } + private val pitchSpeed by setting("Pitch Speed", 30, 1..90, 1, "Pitch rotation degrees per tick", "°") { page == Page.General && pitch != PitchMode.None } + + private val rotation = RotationSettings(this, -1) { page == Page.Rotation } + + private var currentYaw = 0.0f + private var currentPitch = 0.0f + + private val random = Random.from(RandomGenerator.getDefault()) + private var jitterRight = true + private var jitterUp = true + private var pitchingUp = true + + init { + onEnable { + currentYaw = player.yaw + currentPitch = player.pitch + } + + listen(priority = Int.MAX_VALUE) { + currentYaw = wrapDegrees(when (yaw) { + YawMode.Spin -> when (spinMode) { + LeftRight.Left -> currentYaw - yawSpeed + LeftRight.Right -> currentYaw + yawSpeed + } + YawMode.Jitter -> { + val delta = random.nextFloat() * (yawSpeed) + if (jitterRight) { + jitterRight = false + currentYaw + delta + } else { + jitterRight = true + currentYaw - delta + } + } + YawMode.Sideways -> when (sideMode) { + LeftRight.Left -> player.yaw - 90 + LeftRight.Right -> player.yaw + 90 + } + YawMode.Backwards -> player.yaw - 180 + YawMode.Custom -> customYaw + YawMode.Player -> { + val target = getLookAtPlayer(yawPlayerMode) + target?.let { + player.eyePos.rotationTo(target.eyePos).yawF + } ?: player.yaw + } + YawMode.None -> player.yaw + }) + + currentPitch = when (pitch) { + PitchMode.UpAndDown -> { + if (pitchingUp) { + (currentPitch - pitchSpeed).also { + if (currentPitch <= -90) { + pitchingUp = false + } + } + } else { + (currentPitch + pitchSpeed).also { + if (currentPitch >= 90) { + pitchingUp = true + } + } + } + } + PitchMode.Jitter -> { + val delta = random.nextFloat() * (pitchSpeed) + if (jitterUp) { + jitterUp = false + currentPitch - delta + } else { + jitterUp = true + currentPitch + delta + } + } + PitchMode.Vertical -> { + when (verticalMode) { + VerticalMode.Up -> -90f + VerticalMode.Down -> 90f + } + } + PitchMode.Custom -> customPitch + PitchMode.Player -> { + val target = getLookAtPlayer(pitchPlayerMode) + target?.let { + player.eyePos.rotationTo(target.eyePos).pitchF + } ?: player.pitch + } + PitchMode.None -> player.pitch + }.coerceIn(-90f..90f) + } + + listen(priority = Int.MIN_VALUE) { + if (currentYaw == wrap(player.yaw) && currentPitch == player.pitch) return@listen + val rotationRequest = RotationRequest(lookAt(Rotation(currentYaw, currentPitch)), rotation) + rotation.request(rotationRequest, false) + } + } + + private fun SafeContext.getLookAtPlayer(playerMode: PlayerMode): Entity? { + val players = world.entities.filter { it is PlayerEntity && it != player } + return when (playerMode) { + PlayerMode.Closest -> players.minByOrNull { it.eyePos distSq player.eyePos } + PlayerMode.Farthest -> players.maxByOrNull { it.eyePos distSq player.eyePos } + PlayerMode.Random -> players.randomOrNull() + } + } + + private fun setConfigCustomYaw(newYaw: Float) { + customYaw = newYaw + } + + private fun setConfigCustomPitch(newPitch: Float) { + customPitch = newPitch + } + + private enum class YawMode { + None, + Spin, + Jitter, + Sideways, + Backwards, + Custom, + Player + } + + private enum class LeftRight { + Left, + Right + } + + private enum class PlayerMode { + Closest, + Farthest, + Random + } + + private enum class PitchMode { + None, + UpAndDown, + Jitter, + Vertical, + Custom, + Player + } + + private enum class VerticalMode { + Up, + Down + } + + private enum class Page { + General, + Rotation + } +} \ No newline at end of file From af1b01f21e46afde2cb3c41e49fa51bfe485a1cb Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 12 Jul 2025 21:32:03 +0100 Subject: [PATCH 270/364] activeRotation over serverRotation for renders, and removed excess interpolation causing it to feel slower than vanilla --- .../lambda/config/groups/RotationSettings.kt | 6 ------ .../request/rotating/RotationConfig.kt | 8 -------- .../request/rotating/RotationManager.kt | 19 +++++++------------ 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 29c9138a9..9cd78b7bd 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -18,7 +18,6 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.interaction.request.rotating.RotationMode @@ -42,11 +41,6 @@ class RotationSettings( /** How many ticks to wait before resetting the rotation */ override val decayTicks by c.setting("Reset Rotation", 1, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } - /** - * At what sub-tick stages rotations can be performed - */ - override val rotationStageMask by c.setting("Rotation Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick stages at which rotations can be performed", visibility = vis) - /** Whether the rotation is instant */ var instant by c.setting("Instant Rotation", true, "Instantly rotate") { rotate && vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt index 80550629c..7891d416b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt @@ -17,8 +17,6 @@ package com.lambda.interaction.request.rotating -import com.lambda.event.Event -import com.lambda.event.events.TickEvent import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig @@ -51,11 +49,6 @@ abstract class RotationConfig(priority: Priority) : RequestConfig - val rotate: Boolean get() = rotationMode != RotationMode.None override fun requestInternal(request: RotationRequest, queueIfClosed: Boolean) { @@ -66,7 +59,6 @@ abstract class RotationConfig(priority: Priority) : RequestConfig( // Handle LOCK mode if (activeRequest?.mode == RotationMode.Lock) { - mc.player?.yaw = serverRotation.yawF - mc.player?.pitch = serverRotation.pitchF + mc.player?.yaw = activeRotation.yawF + mc.player?.pitch = activeRotation.pitchF } } @@ -161,34 +160,30 @@ object RotationManager : RequestHandler( activeRequest = null } - private val smoothRotation - get() = - lerp(mc.partialTicks, prevServerRotation, serverRotation) - @JvmStatic val lockRotation get() = - if (activeRequest?.mode == RotationMode.Lock) smoothRotation else null + if (activeRequest?.mode == RotationMode.Lock) activeRotation else null @JvmStatic val renderYaw get() = - if (activeRequest == null) null else smoothRotation.yaw.toFloat() + if (activeRequest == null) null else activeRotation.yaw.toFloat() @JvmStatic val renderPitch get() = - if (activeRequest == null) null else smoothRotation.pitch.toFloat() + if (activeRequest == null) null else activeRotation.pitch.toFloat() @JvmStatic val handYaw get() = - if (activeRequest?.mode == RotationMode.Lock) serverRotation.yaw.toFloat() else null + if (activeRequest?.mode == RotationMode.Lock) activeRotation.yaw.toFloat() else null @JvmStatic val handPitch get() = - if (activeRequest?.mode == RotationMode.Lock) serverRotation.pitch.toFloat() else null + if (activeRequest?.mode == RotationMode.Lock) activeRotation.pitch.toFloat() else null @JvmStatic val movementYaw: Float? From 1679832bb75482dd16cf1abb9c00902451b2be9d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 12 Jul 2025 21:37:50 +0100 Subject: [PATCH 271/364] interpolate between serverRotation and activeRotation, rather than prevServerRotation and serverRotation --- .../com/lambda/interaction/request/rotating/RotationManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt index 7a7f0aaa5..44172254e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt @@ -203,7 +203,7 @@ object RotationManager : RequestHandler( fun getRotationForVector(deltaTime: Double): Vec2d? { if (activeRequest?.mode == RotationMode.Silent) return null - val rot = lerp(deltaTime, prevServerRotation, serverRotation) + val rot = lerp(deltaTime, serverRotation, activeRotation) return Vec2d(rot.yaw, rot.pitch) } From e1ab355aa92f7bd4aa35909228eec06f319be858 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sat, 12 Jul 2025 22:17:51 +0100 Subject: [PATCH 272/364] use kotlin random --- .../main/kotlin/com/lambda/module/modules/player/AntiAim.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt index 64d4a72bd..11ad66c84 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt @@ -33,8 +33,7 @@ import com.lambda.util.math.distSq import net.minecraft.entity.Entity import net.minecraft.entity.player.PlayerEntity import net.minecraft.util.math.MathHelper.wrapDegrees -import java.util.* -import java.util.random.RandomGenerator +import kotlin.random.Random object AntiAim : Module( "AntiAim", @@ -73,7 +72,7 @@ object AntiAim : Module( private var currentYaw = 0.0f private var currentPitch = 0.0f - private val random = Random.from(RandomGenerator.getDefault()) + private val random = Random(0) private var jitterRight = true private var jitterUp = true private var pitchingUp = true From a5fd6f31106764250b667d23e9b06e8b68f46222 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Sun, 13 Jul 2025 14:26:57 +0100 Subject: [PATCH 273/364] fix rotation render accuracy and potentially other issues --- .../mixin/entity/LivingEntityMixin.java | 4 +-- .../mixin/entity/PlayerEntityMixin.java | 2 +- .../render/LivingEntityRendererMixin.java | 29 +++---------------- .../request/rotating/RotationManager.kt | 29 +++++++++---------- 4 files changed, 21 insertions(+), 43 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java index 9343953fb..b7e15cc0f 100644 --- a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java @@ -127,7 +127,7 @@ private float rotBody(LivingEntity entity) { return entity.getYaw(); } - Float yaw = RotationManager.getRenderYaw(); + Float yaw = RotationManager.getHeadYaw(); return (yaw == null) ? entity.getYaw() : yaw; } @@ -158,7 +158,7 @@ private float rotHead(LivingEntity entity) { return entity.getYaw(); } - Float yaw = RotationManager.getRenderYaw(); + Float yaw = RotationManager.getHeadYaw(); return (yaw == null) ? entity.getYaw() : yaw; } } diff --git a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java index 97560a19b..8624fc277 100644 --- a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java @@ -42,7 +42,7 @@ private float injectHeadYaw(PlayerEntity instance) { return instance.getYaw(); } - Float yaw = RotationManager.getRenderYaw(); + Float yaw = RotationManager.getHeadYaw(); return (yaw != null) ? yaw : instance.getYaw(); } diff --git a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java index 0d9b533ee..5bbf06391 100644 --- a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java @@ -17,40 +17,16 @@ package com.lambda.mixin.render; -import com.lambda.Lambda; import com.lambda.interaction.request.rotating.RotationManager; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.LivingEntityRenderer; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.LivingEntity; import net.minecraft.util.math.MathHelper; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Objects; @Mixin(LivingEntityRenderer.class) public class LivingEntityRendererMixin { - @Unique - private Float lambda$pitch = null; - - @Inject(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At("HEAD")) - private void injectRender(T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) { - Float rotationPitch = RotationManager.getRenderPitch(); - - this.lambda$pitch = null; - - if (livingEntity != Lambda.getMc().player || rotationPitch == null) { - return; - } - - this.lambda$pitch = rotationPitch; - } - /** * Uses the current rotation render pitch *
{@code
@@ -63,6 +39,9 @@ private void injectRender(T livingEntity, float f, float g, MatrixStack matrixSt
      */
     @Redirect(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerp(FFF)F", ordinal = 0), require = 0)
     private float injectRotationPitch(float g, float f, float s) {
-        return Objects.requireNonNullElseGet(lambda$pitch, () -> MathHelper.lerp(g, f, s));
+        Float headPitch = RotationManager.getHeadPitch();
+        if (headPitch != null) {
+            return MathHelper.lerp(g, RotationManager.INSTANCE.getPrevServerRotation().getPitchF(), headPitch);
+        } else return MathHelper.lerp(g, f, s);
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
index 44172254e..7074ca608 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
@@ -36,6 +36,7 @@ import com.lambda.interaction.request.rotating.visibilty.lookAt
 import com.lambda.module.modules.client.Baritone
 import com.lambda.threading.runGameScheduled
 import com.lambda.threading.runSafe
+import com.lambda.util.extension.partialTicks
 import com.lambda.util.extension.rotation
 import com.lambda.util.math.MathUtils.toRadian
 import com.lambda.util.math.Vec2d
@@ -160,48 +161,46 @@ object RotationManager : RequestHandler(
         activeRequest = null
     }
 
+    private val smoothRotation
+        get() = lerp(mc.partialTicks, serverRotation, activeRotation)
+
     @JvmStatic
     val lockRotation
-        get() =
-            if (activeRequest?.mode == RotationMode.Lock) activeRotation else null
+        get() = if (activeRequest?.mode == RotationMode.Lock) smoothRotation else null
 
     @JvmStatic
-    val renderYaw
-        get() =
-            if (activeRequest == null) null else activeRotation.yaw.toFloat()
+    val headYaw
+        get() = if (activeRequest == null) null else activeRotation.yawF
 
     @JvmStatic
-    val renderPitch
-        get() =
-            if (activeRequest == null) null else activeRotation.pitch.toFloat()
+    val headPitch
+        get() = if (activeRequest == null) null else activeRotation.pitchF
 
     @JvmStatic
     val handYaw
-        get() =
-            if (activeRequest?.mode == RotationMode.Lock) activeRotation.yaw.toFloat() else null
+        get() = if (activeRequest?.mode == RotationMode.Lock) activeRotation.yawF else null
 
     @JvmStatic
     val handPitch
-        get() =
-            if (activeRequest?.mode == RotationMode.Lock) activeRotation.pitch.toFloat() else null
+        get() = if (activeRequest?.mode == RotationMode.Lock) activeRotation.pitchF else null
 
     @JvmStatic
     val movementYaw: Float?
         get() {
-            if (activeRequest?.mode == RotationMode.Silent) return null
+            if (activeRequest == null || activeRequest?.mode == RotationMode.Silent) return null
             return activeRotation.yaw.toFloat()
         }
 
     @JvmStatic
     val movementPitch: Float?
         get() {
-            if (activeRequest?.mode == RotationMode.Silent) return null
+            if (activeRequest == null || activeRequest?.mode == RotationMode.Silent) return null
             return activeRotation.pitch.toFloat()
         }
 
     @JvmStatic
     fun getRotationForVector(deltaTime: Double): Vec2d? {
-        if (activeRequest?.mode == RotationMode.Silent) return null
+        if (activeRequest == null || activeRequest?.mode == RotationMode.Silent) return null
 
         val rot = lerp(deltaTime, serverRotation, activeRotation)
         return Vec2d(rot.yaw, rot.pitch)

From 248c69055adc48ca02d1ddeef7d747296ebd07a9 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 13 Jul 2025 16:17:40 +0100
Subject: [PATCH 274/364] removed unused priority

---
 .../com/lambda/interaction/request/Request.kt      |  3 +--
 .../interaction/request/breaking/BreakRequest.kt   | 14 +++++---------
 .../interaction/request/hotbar/HotbarRequest.kt    |  6 ++----
 .../request/interacting/InteractionRequest.kt      |  5 ++---
 .../interaction/request/placing/PlaceRequest.kt    |  4 +---
 .../request/rotating/RotationRequest.kt            |  6 ++----
 .../com/lambda/module/modules/player/Nuker.kt      |  2 +-
 7 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
index b7115cd77..97c8db0a3 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
@@ -17,8 +17,7 @@
 
 package com.lambda.interaction.request
 
-abstract class Request (
-    val priority: Priority,
+abstract class Request(
     val config: RequestConfig<*>
 ) {
     var fresh = true
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index 388272176..1f0f3d721 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -20,7 +20,6 @@ package com.lambda.interaction.request.breaking
 import com.lambda.config.groups.BuildConfig
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.context.BuildContext
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.Request
 import com.lambda.interaction.request.hotbar.HotbarConfig
 import com.lambda.interaction.request.rotating.RotationConfig
@@ -38,9 +37,8 @@ data class BreakRequest(
     val build: BuildConfig,
     val rotation: RotationConfig,
     val hotbar: HotbarConfig,
-    val pendingInteractions: MutableCollection,
-    private val prio: Priority = 0
-) : Request(prio, build.breaking) {
+    val pendingInteractions: MutableCollection
+) : Request(build.breaking) {
     var onStart: ((BlockPos) -> Unit)? = null
     var onUpdate: ((BlockPos) -> Unit)? = null
     var onStop: ((BlockPos) -> Unit)? = null
@@ -58,10 +56,9 @@ data class BreakRequest(
         build: BuildConfig,
         rotation: RotationConfig,
         hotbar: HotbarConfig,
-        pendingInteractions: MutableCollection,
-        prio: Priority = 0
+        pendingInteractions: MutableCollection
     ) {
-        val request = BreakRequest(contexts, build, rotation, hotbar, pendingInteractions, prio)
+        val request = BreakRequest(contexts, build, rotation, hotbar, pendingInteractions)
 
         @BreakRequestBuilder
         fun onStart(callback: (BlockPos) -> Unit) {
@@ -110,8 +107,7 @@ data class BreakRequest(
             rotation: RotationConfig,
             hotbar: HotbarConfig,
             pendingInteractions: MutableCollection,
-            prio: Priority = 0,
             builder: RequestBuilder.() -> Unit
-        ) = RequestBuilder(contexts, build, rotation, hotbar, pendingInteractions, prio).apply(builder).build()
+        ) = RequestBuilder(contexts, build, rotation, hotbar, pendingInteractions).apply(builder).build()
     }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
index 3ef363521..c0eb9e272 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
@@ -17,16 +17,14 @@
 
 package com.lambda.interaction.request.hotbar
 
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.Request
 
 class HotbarRequest(
     val slot: Int,
     val hotbar: HotbarConfig,
     var keepTicks: Int = hotbar.keepTicks,
-    var swapPause: Int = hotbar.swapPause,
-    priority: Priority = 0,
-) : Request(priority, hotbar) {
+    var swapPause: Int = hotbar.swapPause
+) : Request(hotbar) {
     var activeRequestAge = 0
     var swapPauseAge = 0
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
index b01799e21..c0c0be422 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
@@ -35,9 +35,8 @@ data class InteractionRequest(
     val interact: InteractionConfig,
     val build: BuildConfig,
     val hotbar: HotbarConfig,
-    val rotation: RotationConfig,
-    private val prio: Int = 0
-) : Request(prio, interact) {
+    val rotation: RotationConfig
+) : Request(interact) {
     override val done: Boolean
         get() = contexts.all { mc.world?.getBlockState(it.blockPos)?.matches(it.expectedState) == true }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
index 2bbc48c00..c598dcc9c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
@@ -20,7 +20,6 @@ package com.lambda.interaction.request.placing
 import com.lambda.config.groups.BuildConfig
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.PlaceContext
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.Request
 import com.lambda.interaction.request.hotbar.HotbarConfig
 import com.lambda.interaction.request.rotating.RotationConfig
@@ -34,9 +33,8 @@ data class PlaceRequest(
     val rotation: RotationConfig,
     val hotbar: HotbarConfig,
     val pendingInteractions: MutableCollection,
-    private val prio: Priority = 0,
     val onPlace: () -> Unit
-) : Request(prio, build.placing) {
+) : Request(build.placing) {
     override val done: Boolean
         get() = runSafe {
             contexts.all { it.expectedState.matches(blockState(it.blockPos)) }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
index e931dcd71..ea800c1c4 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
@@ -17,28 +17,26 @@
 
 package com.lambda.interaction.request.rotating
 
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.Request
 import com.lambda.interaction.request.rotating.visibilty.RotationTarget
 import com.lambda.threading.runSafe
 
 data class RotationRequest(
     val target: RotationTarget,
-    val prio: Priority,
     val mode: RotationMode,
     val rot: RotationConfig,
     var keepTicks: Int = 3,
     var decayTicks: Int = 0,
     val turnSpeed: () -> Double = { 180.0 },
     val speedMultiplier: Double = 1.0
-) : Request(prio, rot) {
+) : Request(rot) {
     var age = 0
 
     constructor(
         target: RotationTarget,
         config: RotationConfig,
         speedMultiplier: Double = 1.0
-    ) : this(target, config.priority, config.rotationMode, config, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier)
+    ) : this(target, config.rotationMode, config, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier)
 
     override val done: Boolean get() =
         mode == RotationMode.None || runSafe { target.verify() } == true
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index a8cc5765f..157ff8b42 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -37,7 +37,7 @@ object Nuker : Module(
     private val height by setting("Height", 4, 1..8, 1)
     private val width by setting("Width", 4, 1..8, 1)
     private val flatten by setting("Flatten", true)
-    private val onlyBreakInstant by setting("Only Break Instant", true)
+    private val onlyBreakInstant by setting("Only Break Instant", false)
     private val fillFloor by setting("Fill Floor", false)
     private val baritoneSelection by setting("Baritone Selection", false, "Restricts nuker to your baritone selection")
 

From 69d3616a13e68a983d9d1d9b69f42ef1a7679f58 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 13 Jul 2025 19:18:26 +0100
Subject: [PATCH 275/364] small nuker changes

---
 .../kotlin/com/lambda/module/modules/player/Nuker.kt     | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index 157ff8b42..499dc7710 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -20,6 +20,7 @@ package com.lambda.module.modules.player
 import com.lambda.interaction.construction.blueprint.TickingBlueprint.Companion.tickingBlueprint
 import com.lambda.interaction.construction.verify.TargetState
 import com.lambda.module.Module
+import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.module.tag.ModuleTag
 import com.lambda.task.RootTask.run
 import com.lambda.task.Task
@@ -27,6 +28,7 @@ import com.lambda.task.tasks.BuildTask.Companion.build
 import com.lambda.util.BaritoneUtils
 import com.lambda.util.BlockUtils.blockPos
 import com.lambda.util.BlockUtils.blockState
+import com.lambda.util.BlockUtils.isNotEmpty
 import net.minecraft.util.math.BlockPos
 
 object Nuker : Module(
@@ -37,7 +39,7 @@ object Nuker : Module(
     private val height by setting("Height", 4, 1..8, 1)
     private val width by setting("Width", 4, 1..8, 1)
     private val flatten by setting("Flatten", true)
-    private val onlyBreakInstant by setting("Only Break Instant", false)
+    private val instantOnly by setting("Instant Only", false)
     private val fillFloor by setting("Fill Floor", false)
     private val baritoneSelection by setting("Baritone Selection", false, "Restricts nuker to your baritone selection")
 
@@ -49,10 +51,9 @@ object Nuker : Module(
                 val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width)
                     .asSequence()
                     .map { it.blockPos }
-                    .filter { !world.isAir(it) }
+                    .filter { blockState(it).isNotEmpty }
                     .filter { !flatten || it.y >= player.blockPos.y }
-                    .filter { !onlyBreakInstant || blockState(it).getHardness(world, it) <= 1 }
-                    .filter { blockState(it).getHardness(world, it) >= 0 }
+                    .filter { !instantOnly || blockState(it).getHardness(world, it) <= TaskFlowModule.build.breaking.breakThreshold }
                     .filter { pos ->
                         if (!baritoneSelection) true
                         else BaritoneUtils.primary.selectionManager.selections.any {

From fa9a42c2cdaf14e4623923f0d97d4504a63fed63 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 13 Jul 2025 20:20:02 +0100
Subject: [PATCH 276/364] dont ignore break contexts that could potentially
 update current breaking infos

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 1f742425a..4e9251b06 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -353,7 +353,7 @@ object BreakManager : RequestHandler(
      * @return if the break context can be accepted.
      */
     private fun SafeContext.canAccept(ctx: BreakContext, breakConfig: BreakConfig): Boolean {
-        if (isPosBlocked(ctx.blockPos)) return false
+        if (breakInfos.none { it?.context?.blockPos == ctx.blockPos } && isPosBlocked(ctx.blockPos)) return false
 
         if (breakConfig.doubleBreak) {
             breakInfos

From 441a5812133414ab8c85c3a79ce38a14aaa34960 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 13 Jul 2025 22:48:50 +0100
Subject: [PATCH 277/364] pages for break settings

---
 .../com/lambda/config/groups/BreakSettings.kt | 83 ++++++++++---------
 .../com/lambda/config/groups/BuildSettings.kt |  2 +-
 .../construction/simulation/BuildSimulator.kt |  3 +-
 3 files changed, 47 insertions(+), 41 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 2e88c875e..c60b9c1a9 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -29,45 +29,52 @@ class BreakSettings(
     priority: Priority = 0,
     vis: () -> Boolean = { true }
 ) : BreakConfig(priority) {
-    override val breakMode by c.setting("Break Mode", BreakMode.Packet, visibility = vis)
-    override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", visibility = vis)
-    override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes", visibility = vis)
-    override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis)
-    override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis)
-    override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis)
-    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position", visibility = vis)
-    override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis)
-    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed", visibility = vis)
-    override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis)
-    override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None }
-    override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds", visibility = vis)
-    override val particles by c.setting("Particles", true, "Renders the breaking particles", visibility = vis)
-    override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages", visibility = vis)
-    override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", visibility = vis)
-    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken", visibility = vis)
-    override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking", visibility = vis)
-    override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks", visibility = vis)
-    override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick", visibility = vis)
-    override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis)
-    override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill", visibility = vis)
-    override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", visibility = vis)
-    override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis)
-    override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis)
-    override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe }
+    val page by c.setting("Break Page", Page.General, visibility = vis)
+    override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
+    override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
+    override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
+    override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
+    override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() && page == Page.General }
+    override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
+    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
+    override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
+    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
+    override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() && page == Page.General }
+    override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && page == Page.General && swing != SwingMode.None }
+    override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { vis() && page == Page.General }
+    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.General }
+    override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() && page == Page.General }
+    override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() && page == Page.General }
+    override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.General }
+    override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block") { vis() && page == Page.General }
+    override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill") { vis() && page == Page.General }
+    override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.General }
+    override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.General }
+    override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() && page == Page.General }
+    override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && page == Page.General && forceFortunePickaxe }
 
-    override val renders by c.setting("Renders", true, "Enables the render settings for breaking progress", visibility = vis)
-    override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && renders }
-    override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && renders }
-    override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && renders && outline }
-    override val animation by c.setting("Animation", AnimationMode.Out, "The style of animation used for the box") { vis() && renders }
+    override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() && page == Page.Cosmetic }
+    override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() && page == Page.Cosmetic }
+    override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() && page == Page.Cosmetic }
 
-    override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && renders && fill }
-    override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60), "The color of the fill") { vis() && renders && !dynamicFillColor && fill }
-    override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60), "The color of the fill at the start of breaking") { vis() && renders && dynamicFillColor && fill }
-    override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60), "The color of the fill at the end of breaking") { vis() && renders && dynamicFillColor && fill }
+    override val renders by c.setting("Renders", true, "Enables the render settings for breaking progress") { vis() && page == Page.Cosmetic }
+    override val animation by c.setting("Animation", AnimationMode.Out, "The style of animation used for the box") { vis() && page == Page.Cosmetic && renders }
 
-    override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && renders && outline }
-    override val staticOutlineColor by c.setting("Outline Color", Color(255, 0, 0, 255), "The Color of the outline at the start of breaking") { vis() && renders && !dynamicOutlineColor && outline }
-    override val startOutlineColor by c.setting("Start Outline Color", Color(255, 0, 0, 255), "The color of the outline at the start of breaking") { vis() && renders && dynamicOutlineColor && outline }
-    override val endOutlineColor by c.setting("End Outline Color", Color(0, 255, 0, 255), "The color of the outline at the end of breaking") { vis() && renders && dynamicOutlineColor && outline }
+    override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && page == Page.Cosmetic && renders }
+    override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && page == Page.Cosmetic && renders && fill }
+    override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60), "The color of the fill") { vis() && page == Page.Cosmetic && renders && !dynamicFillColor && fill }
+    override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60), "The color of the fill at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
+    override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60), "The color of the fill at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
+
+    override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && page == Page.Cosmetic && renders }
+    override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && page == Page.Cosmetic && renders && outline }
+    override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && page == Page.Cosmetic && renders && outline }
+    override val staticOutlineColor by c.setting("Outline Color", Color(255, 0, 0, 255), "The Color of the outline at the start of breaking") { vis() && page == Page.Cosmetic && renders && !dynamicOutlineColor && outline }
+    override val startOutlineColor by c.setting("Start Outline Color", Color(255, 0, 0, 255), "The color of the outline at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicOutlineColor && outline }
+    override val endOutlineColor by c.setting("End Outline Color", Color(0, 255, 0, 255), "The color of the outline at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicOutlineColor && outline }
+
+    enum class Page {
+        General,
+        Cosmetic
+    }
 }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
index df918e81d..12374504f 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
@@ -35,7 +35,7 @@ class BuildSettings(
     override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.General }
     override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.General && pathing }
     override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.General }
-    override val interactionsPerTick by c.setting("Interactions Per Tick", 5, 1..30, 1, "The amount of interactions that can happen per tick", visibility = vis)
+    override val interactionsPerTick by c.setting("Interactions Per Tick", 5, 1..30, 1, "The amount of interactions that can happen per tick") { vis() && page == Page.General }
     override val maxPendingInteractions by c.setting("Max Pending Interactions", 20, 1..30, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General }
 
     // Breaking
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 7d45d1ab6..f765feb2a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -83,7 +83,6 @@ import net.minecraft.state.property.Properties
 import net.minecraft.util.Hand
 import net.minecraft.util.hit.BlockHitResult
 import net.minecraft.util.math.BlockPos
-import net.minecraft.util.math.Box
 import net.minecraft.util.math.Direction
 import net.minecraft.util.math.Vec3d
 import net.minecraft.util.shape.VoxelShapes
@@ -578,7 +577,7 @@ object BuildSimulator {
         val state = blockState(pos)
 
         /* is a block that will be destroyed by breaking adjacent blocks */
-        if (breaking.breakWeakBlocks && state.block.hardness == 0f && state.isNotEmpty) {
+        if (!breaking.breakWeakBlocks && state.block.hardness == 0f && state.isNotEmpty) {
             acc.add(BuildResult.Ignored(pos))
             return acc
         }

From 196c9790d32797711695fa7b865b51599d9a1554 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 13 Jul 2025 22:55:31 +0100
Subject: [PATCH 278/364] brighter default render colours

---
 .../kotlin/com/lambda/config/groups/BreakSettings.kt | 12 ++++++------
 .../com/lambda/module/modules/player/PacketMine.kt   |  6 +++---
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index c60b9c1a9..b52bb472b 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -62,16 +62,16 @@ class BreakSettings(
 
     override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && page == Page.Cosmetic && renders }
     override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && page == Page.Cosmetic && renders && fill }
-    override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60), "The color of the fill") { vis() && page == Page.Cosmetic && renders && !dynamicFillColor && fill }
-    override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60), "The color of the fill at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
-    override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60), "The color of the fill at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
+    override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill") { vis() && page == Page.Cosmetic && renders && !dynamicFillColor && fill }
+    override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
+    override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60).brighter(), "The color of the fill at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
 
     override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && page == Page.Cosmetic && renders }
     override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && page == Page.Cosmetic && renders && outline }
     override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && page == Page.Cosmetic && renders && outline }
-    override val staticOutlineColor by c.setting("Outline Color", Color(255, 0, 0, 255), "The Color of the outline at the start of breaking") { vis() && page == Page.Cosmetic && renders && !dynamicOutlineColor && outline }
-    override val startOutlineColor by c.setting("Start Outline Color", Color(255, 0, 0, 255), "The color of the outline at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicOutlineColor && outline }
-    override val endOutlineColor by c.setting("End Outline Color", Color(0, 255, 0, 255), "The color of the outline at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicOutlineColor && outline }
+    override val staticOutlineColor by c.setting("Outline Color", Color.RED.brighter(), "The Color of the outline at the start of breaking") { vis() && page == Page.Cosmetic && renders && !dynamicOutlineColor && outline }
+    override val startOutlineColor by c.setting("Start Outline Color", Color.RED.brighter(), "The color of the outline at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicOutlineColor && outline }
+    override val endOutlineColor by c.setting("End Outline Color", Color.GREEN.brighter(), "The color of the outline at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicOutlineColor && outline }
 
     enum class Page {
         General,
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 40e74b258..6dbc25d35 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -72,9 +72,9 @@ object PacketMine : Module(
     private val renderSize by setting("Render Size", 0.3f, 0.01f..1f, 0.01f, "The scale of the queue renders") { queue && renderQueue }
     private val renderMode by setting("Render Mode", RenderMode.State, "The style of the queue renders") { queue && renderQueue }
     private val dynamicColor by setting("Dynamic Color", true, "Interpolates the color between start and end") { queue && renderQueue }
-    private val staticColor by setting("Color", Color(255, 0, 0, 60)) { queue && renderQueue && !dynamicColor }
-    private val startColor by setting("Start Color", Color(255, 255, 0, 60), "The color of the start (closest to breaking) of the queue") { queue && renderQueue && dynamicColor }
-    private val endColor by setting("End Color", Color(255, 0, 0, 60), "The color of the end (farthest from breaking) of the queue") { queue && renderQueue && dynamicColor }
+    private val staticColor by setting("Color", Color(255, 0, 0, 60).brighter()) { queue && renderQueue && !dynamicColor }
+    private val startColor by setting("Start Color", Color(255, 255, 0, 60).brighter(), "The color of the start (closest to breaking) of the queue") { queue && renderQueue && dynamicColor }
+    private val endColor by setting("End Color", Color(255, 0, 0, 60).brighter(), "The color of the end (farthest from breaking) of the queue") { queue && renderQueue && dynamicColor }
 
 
     private val pendingInteractions = ConcurrentLinkedQueue()

From e82bb80b79e55d5396465ed35f41bb631f4d900c Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 00:29:44 +0100
Subject: [PATCH 279/364] post-processing property checks on break manager
 block updates and added ignore drop warnings setting in task flow debug

---
 .../interaction/request/breaking/BreakManager.kt   |  9 +++++++--
 .../request/breaking/BrokenBlockHandler.kt         | 14 ++++++++------
 .../lambda/module/modules/client/TaskFlowModule.kt |  7 ++++++-
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 4e9251b06..06af3d915 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -31,6 +31,7 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.graphics.renderer.esp.builders.buildFilled
 import com.lambda.graphics.renderer.esp.builders.buildOutline
 import com.lambda.interaction.construction.context.BreakContext
+import com.lambda.interaction.construction.processing.ProcessorRegistry
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
 import com.lambda.interaction.request.PositionBlocking
 import com.lambda.interaction.request.Priority
@@ -55,6 +56,7 @@ import com.lambda.util.BlockUtils.emptyState
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.isNotEmpty
+import com.lambda.util.BlockUtils.matches
 import com.lambda.util.Communication.warn
 import com.lambda.util.item.ItemUtils.block
 import com.lambda.util.math.lerp
@@ -151,9 +153,12 @@ object BreakManager : RequestHandler(
                 .filterNotNull()
                 .firstOrNull { it.context.blockPos == event.pos }
                 ?.let { info ->
+                    val currentState = info.context.cachedState
                     // if not broken
-                    if (isNotBroken(info.context.cachedState, event.newState)) {
-                        this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.cachedState.emptyState}")
+                    if (isNotBroken(currentState, event.newState)) {
+                        // check to see if its just some small property changes, e.g. redstone ore changing the LIT property
+                        if (!currentState.matches(event.newState, ProcessorRegistry.postProcessedProperties))
+                            this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.cachedState.emptyState}")
                         // update the checked state
                         info.context.cachedState = event.newState
                         return@listen
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index f47a69439..e9c305138 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -24,6 +24,7 @@ import com.lambda.event.events.EntityEvent
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
+import com.lambda.interaction.construction.processing.ProcessorRegistry
 import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode
 import com.lambda.interaction.request.breaking.BreakManager.lastPosStarted
 import com.lambda.interaction.request.breaking.BreakManager.matchesBlockItem
@@ -58,7 +59,7 @@ object BrokenBlockHandler {
             if (!loaded) return@let
 
             if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out")
-            else warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
+            else if (!TaskFlowModule.ignoreItemDropWarnings) warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
 
             if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
                 world.setBlockState(info.context.blockPos, info.context.cachedState)
@@ -75,12 +76,13 @@ object BrokenBlockHandler {
                     ?: if (reBreak?.context?.blockPos == event.pos) reBreak
                     else null
             }?.let { pending ->
-                // return if the state hasn't changed
-                if (event.newState.matches(pending.context.cachedState))
-                    return@listen
-
+                val currentState = pending.context.cachedState
                 // return if the block's not broken
-                if (isNotBroken(pending.context.cachedState, event.newState)) {
+                if (isNotBroken(currentState, event.newState)) {
+                    // return if the state hasn't changed
+                    if (event.newState.matches(currentState, ProcessorRegistry.postProcessedProperties))
+                        return@listen
+
                     if (!pending.isReBreaking) {
                         this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}")
                         pending.stopPending()
diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt
index ce43bda29..7f3cc9438 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt
@@ -17,7 +17,11 @@
 
 package com.lambda.module.modules.client
 
-import com.lambda.config.groups.*
+import com.lambda.config.groups.BuildSettings
+import com.lambda.config.groups.HotbarSettings
+import com.lambda.config.groups.InteractionSettings
+import com.lambda.config.groups.InventorySettings
+import com.lambda.config.groups.RotationSettings
 import com.lambda.event.events.RenderEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.construction.result.Drawable
@@ -43,6 +47,7 @@ object TaskFlowModule : Module(
 
     val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree") { page == Page.Debug }
     val shrinkFactor by setting("Shrink Factor", 0.001, 0.0..1.0, 0.001) { page == Page.Debug }
+    val ignoreItemDropWarnings by setting("Ignore Drop Warnings", false, "Hides the item drop warnings from the break manager") { page == Page.Debug }
 
     @Volatile
     var drawables = listOf()

From 4800ed237a7f0da2a391366b761f658429ff5179 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 00:42:48 +0100
Subject: [PATCH 280/364] default fudgeFactor to 1

---
 .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index b52bb472b..baed12f23 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -35,7 +35,7 @@ class BreakSettings(
     override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
     override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
     override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() && page == Page.General }
-    override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
+    override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
     override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
     override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }

From 5a2cf1ffe649f3ae7fa400ea65b0355d37c348c7 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 01:24:38 +0100
Subject: [PATCH 281/364] check by not null count in PacketMine and account for
 multiple different hotbar index requiring contexts within the same request

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt  | 5 +++++
 .../kotlin/com/lambda/module/modules/player/PacketMine.kt    | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 06af3d915..809fae0ac 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -325,6 +325,11 @@ object BreakManager : RequestHandler(
         val newBreaks = request.contexts
             .distinctBy { it.blockPos }
             .filter { ctx -> canAccept(ctx, request.build.breaking) }
+            .let { acceptable ->
+                acceptable.firstOrNull()?.let { first ->
+                    acceptable.filter { it.hotbarIndex == first.hotbarIndex }
+                } ?: acceptable
+            }
             .toMutableList()
 
         // Update the current break infos or cancel if abandoned
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 6dbc25d35..b6944ecaa 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -171,7 +171,7 @@ object PacketMine : Module(
     }
 
     private fun SafeContext.requestBreakManager(requestPositions: Collection, reBreaking: Boolean = false) {
-        if (requestPositions.isEmpty()) return
+        if (requestPositions.count { it != null } <= 0) return
         val breakContexts = breakContexts(requestPositions)
         if (!reBreaking) {
             queuePositions.retainAllPositions(breakContexts)

From b4a0e13f735fd0e74d57e6cc6b69df5c05b8f6af Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 11:43:44 +0100
Subject: [PATCH 282/364] disable finishOnDone for nuker

---
 .../src/main/kotlin/com/lambda/module/modules/player/Nuker.kt   | 2 +-
 common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt       | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index 499dc7710..a55098469 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -74,7 +74,7 @@ object Nuker : Module(
                 }
 
                 selection
-            }.build()
+            }.build(finishOnDone = false)
             // ToDo: Add build setting delegates
 
             task?.run()
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index 7356cdcc2..22ed89207 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -82,7 +82,6 @@ class BuildTask @Ta5kBuilder constructor(
     private var placements = 0
     private var breaks = 0
     private val dropsToCollect = mutableSetOf()
-//    private var goodPositions = setOf()
 
     private val onItemDrop: ((item: ItemEntity) -> Unit)?
         get() = if (collectDrops) {

From 97c60d2dd41060945c90f7d00f002a42cb5367f4 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 17:56:17 +0100
Subject: [PATCH 283/364] check break cooldown before accepting contexts and
 set cooldown if not vanilla instant breakable

---
 .../interaction/request/breaking/BreakManager.kt   |  8 +++++---
 .../com/lambda/module/modules/player/Nuker.kt      |  8 --------
 .../com/lambda/module/modules/player/PacketMine.kt | 14 +++++++-------
 3 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 809fae0ac..14c1accc2 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -429,6 +429,8 @@ object BreakManager : RequestHandler(
         requestCtx: BreakContext,
         request: BreakRequest
     ): BreakInfo? {
+        if (breakCooldown > 0) return null
+
         val breakInfo = BreakInfo(requestCtx, Primary, request)
         primaryBreak?.let { primaryInfo ->
             if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) {
@@ -526,7 +528,7 @@ object BreakManager : RequestHandler(
         runSafe {
             setBreakingTextureStage(player, world, -1)
             if (isPrimary) {
-                abortBreakPacket(world, interaction)
+                if (breaking) abortBreakPacket(world, interaction)
                 nullify()
             } else if (isSecondary && breakConfig.unsafeCancels) {
                 makeRedundant()
@@ -606,7 +608,6 @@ object BreakManager : RequestHandler(
                 }
                 else -> {}
             }
-            if (breakCooldown > 0) return false
             if (!startBreaking(info)) {
                 info.nullify()
                 info.internalOnCancel()
@@ -718,8 +719,10 @@ object BreakManager : RequestHandler(
         }
 
         val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig)
+        info.vanillaInstantBreakable = breakDelta >= 1
         if (notEmpty && breakDelta >= info.getBreakThreshold()) {
             onBlockBreak(info)
+            if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay
         } else {
             info.apply {
                 breaking = true
@@ -736,7 +739,6 @@ object BreakManager : RequestHandler(
         }
 
         info.startBreakPacket(world, interaction)
-        info.vanillaInstantBreakable = breakDelta >= 1
 
         if (info.isSecondary || (!info.vanillaInstantBreakable && breakDelta >= info.breakConfig.breakThreshold)) {
             info.stopBreakPacket(world, interaction)
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index a55098469..9b7d66f50 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -83,13 +83,5 @@ object Nuker : Module(
         onDisable {
             task?.cancel()
         }
-
-//        listener {
-//            task?.let {
-//                if (!it.isRunning) return@listener
-//
-//                info(it.info)
-//            }
-//        }
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index b6944ecaa..6b109090b 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -68,13 +68,13 @@ object PacketMine : Module(
     private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once")
         .onValueChange { _, to -> if (!to) queuePositions.clear() }
     private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue }
-    private val renderQueue by setting("Render Queue", true, "Adds renders to signify what block positions are queued") { queue }
-    private val renderSize by setting("Render Size", 0.3f, 0.01f..1f, 0.01f, "The scale of the queue renders") { queue && renderQueue }
-    private val renderMode by setting("Render Mode", RenderMode.State, "The style of the queue renders") { queue && renderQueue }
-    private val dynamicColor by setting("Dynamic Color", true, "Interpolates the color between start and end") { queue && renderQueue }
-    private val staticColor by setting("Color", Color(255, 0, 0, 60).brighter()) { queue && renderQueue && !dynamicColor }
-    private val startColor by setting("Start Color", Color(255, 255, 0, 60).brighter(), "The color of the start (closest to breaking) of the queue") { queue && renderQueue && dynamicColor }
-    private val endColor by setting("End Color", Color(255, 0, 0, 60).brighter(), "The color of the end (farthest from breaking) of the queue") { queue && renderQueue && dynamicColor }
+    private val renderQueue by setting("Render Queue", true, "Adds renders to signify what block positions are queued")
+    private val renderSize by setting("Render Size", 0.3f, 0.01f..1f, 0.01f, "The scale of the queue renders") { renderQueue }
+    private val renderMode by setting("Render Mode", RenderMode.State, "The style of the queue renders") { renderQueue }
+    private val dynamicColor by setting("Dynamic Color", true, "Interpolates the color between start and end") { renderQueue }
+    private val staticColor by setting("Color", Color(255, 0, 0, 60).brighter()) { renderQueue && !dynamicColor }
+    private val startColor by setting("Start Color", Color(255, 255, 0, 60).brighter(), "The color of the start (closest to breaking) of the queue") { renderQueue && dynamicColor }
+    private val endColor by setting("End Color", Color(255, 0, 0, 60).brighter(), "The color of the end (farthest from breaking) of the queue") { renderQueue && dynamicColor }
 
 
     private val pendingInteractions = ConcurrentLinkedQueue()

From 1f63a8bfc630a13cc85a0816c30dca12066f2782 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 20:51:01 +0100
Subject: [PATCH 284/364] fixe for when unsafe cancels is disabled, and
 improvements for reviving abandoned and redundant break infos

---
 .../interaction/request/breaking/BreakInfo.kt |  8 ++--
 .../request/breaking/BreakManager.kt          | 48 ++++++++++++++++---
 .../request/breaking/BreakRequest.kt          | 26 ++++++----
 .../request/breaking/BrokenBlockHandler.kt    |  5 +-
 .../module/modules/player/PacketMine.kt       |  4 +-
 .../kotlin/com/lambda/task/tasks/BuildTask.kt |  4 +-
 6 files changed, 72 insertions(+), 23 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 5d077d836..dabc756a0 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -40,6 +40,7 @@ data class BreakInfo(
     var updatedProgressThisTick = false
 
     var breaking = false
+    var abandoned = false
     var breakingTicks = 0
     var soundsCooldown = 0.0f
 
@@ -78,13 +79,10 @@ data class BreakInfo(
         request.onCancel?.invoke(context.blockPos)
     }
 
-    fun updateInfo(context: BreakContext, request: BreakRequest) {
+    fun updateInfo(context: BreakContext, request: BreakRequest? = null) {
         updatedThisTick = true
         this.context = context
-        this.request = request
-        if (isRedundant) {
-            type = BreakType.Secondary
-        }
+        request?.let { this.request = it }
     }
 
     fun tickStats() {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 14c1accc2..ba0633ac6 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -30,8 +30,13 @@ import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.graphics.renderer.esp.builders.buildFilled
 import com.lambda.graphics.renderer.esp.builders.buildOutline
+import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
+import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.processing.ProcessorRegistry
+import com.lambda.interaction.construction.result.BreakResult
+import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
+import com.lambda.interaction.construction.verify.TargetState
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
 import com.lambda.interaction.request.PositionBlocking
 import com.lambda.interaction.request.Priority
@@ -126,6 +131,28 @@ object BreakManager : RequestHandler(
     override fun load(): String {
         super.load()
 
+        listen(priority = Int.MIN_VALUE) {
+            // Cancelled but double breaking so requires break manager to continue the simulation
+            breakInfos
+                .asSequence()
+                .filterNotNull()
+                .filter { it.abandoned && !it.isRedundant }
+                .forEach { info ->
+                    with (info.request) {
+                        info.context.blockPos
+                            .toStructure(TargetState.Empty)
+                            .toBlueprint()
+                            .simulate(player.eyePos, interact, rotation, inventory, build)
+                            .asSequence()
+                            .filterIsInstance()
+                            .sorted()
+                            .let { sim ->
+                                info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
+                            }
+                    }
+                }
+        }
+
         listen(priority = Int.MIN_VALUE) {
             if (breakCooldown > 0) {
                 breakCooldown--
@@ -200,7 +227,7 @@ object BreakManager : RequestHandler(
                         world,
                         info.context.blockPos,
                         info.breakConfig,
-                        player.inventory.getStack(info.context.hotbarIndex)
+                        if (!info.isRedundant) player.inventory.getStack(info.context.hotbarIndex) else null
                     )
                     val progress = (info.breakingTicks * breakDelta).let {
                         if (info.isPrimary) it * (2 - info.breakConfig.breakThreshold)
@@ -337,9 +364,17 @@ object BreakManager : RequestHandler(
             .filterNotNull()
             .forEach { info ->
                 newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos }?.let { ctx ->
-                    if (!info.updatedThisTick) {
+                    if (!info.updatedThisTick || info.abandoned) {
                         info.updateInfo(ctx, request)
-                        info.request.onUpdate?.invoke(info.context.blockPos)
+                        if (info.isRedundant) {
+                            info.type = BreakType.Secondary
+                            info.request.onStart?.invoke(info.context.blockPos)
+                        } else if (info.abandoned) {
+                            info.abandoned = false
+                            info.request.onStart?.invoke(info.context.blockPos)
+                        } else {
+                            info.request.onUpdate?.invoke(info.context.blockPos)
+                        }
                     }
                     newBreaks.remove(ctx)
                     return@forEach
@@ -530,8 +565,9 @@ object BreakManager : RequestHandler(
             if (isPrimary) {
                 if (breaking) abortBreakPacket(world, interaction)
                 nullify()
-            } else if (isSecondary && breakConfig.unsafeCancels) {
-                makeRedundant()
+            } else if (isSecondary) {
+                if (breakConfig.unsafeCancels) makeRedundant()
+                else abandoned = true
             }
 
             internalOnCancel()
@@ -755,7 +791,7 @@ object BreakManager : RequestHandler(
         item: ItemStack? = null
     ) = runSafe {
         var delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.mainHandStack)
-        // This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, its broken
+        //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
         if (config.desyncFix) {
             val nextTickPrediction = buildPlayerPrediction().next()
             if (player.isOnGround && !nextTickPrediction.onGround) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index 1f0f3d721..b2dbae649 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -18,6 +18,8 @@
 package com.lambda.interaction.request.breaking
 
 import com.lambda.config.groups.BuildConfig
+import com.lambda.config.groups.InteractionConfig
+import com.lambda.config.groups.InventoryConfig
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.request.Request
@@ -34,10 +36,12 @@ annotation class BreakRequestBuilder
 
 data class BreakRequest(
     val contexts: Collection,
-    val build: BuildConfig,
-    val rotation: RotationConfig,
+    val pendingInteractions: MutableCollection,
     val hotbar: HotbarConfig,
-    val pendingInteractions: MutableCollection
+    val rotation: RotationConfig,
+    val inventory: InventoryConfig,
+    val interact: InteractionConfig,
+    val build: BuildConfig
 ) : Request(build.breaking) {
     var onStart: ((BlockPos) -> Unit)? = null
     var onUpdate: ((BlockPos) -> Unit)? = null
@@ -53,12 +57,14 @@ data class BreakRequest(
     @BreakRequestBuilder
     class RequestBuilder(
         contexts: Collection,
-        build: BuildConfig,
+        pendingInteractions: MutableCollection,
         rotation: RotationConfig,
         hotbar: HotbarConfig,
-        pendingInteractions: MutableCollection
+        interact: InteractionConfig,
+        inventory: InventoryConfig,
+        build: BuildConfig
     ) {
-        val request = BreakRequest(contexts, build, rotation, hotbar, pendingInteractions)
+        val request = BreakRequest(contexts, pendingInteractions, hotbar, rotation, inventory, interact, build)
 
         @BreakRequestBuilder
         fun onStart(callback: (BlockPos) -> Unit) {
@@ -103,11 +109,13 @@ data class BreakRequest(
         @BreakRequestBuilder
         fun breakRequest(
             contexts: Collection,
-            build: BuildConfig,
+            pendingInteractions: MutableCollection,
             rotation: RotationConfig,
             hotbar: HotbarConfig,
-            pendingInteractions: MutableCollection,
+            interact: InteractionConfig,
+            inventory: InventoryConfig,
+            build: BuildConfig,
             builder: RequestBuilder.() -> Unit
-        ) = RequestBuilder(contexts, build, rotation, hotbar, pendingInteractions).apply(builder).build()
+        ) = RequestBuilder(contexts, pendingInteractions, rotation, hotbar, interact, inventory, build).apply(builder).build()
     }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index e9c305138..cae9d8649 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -92,7 +92,10 @@ object BrokenBlockHandler {
                     return@listen
                 }
 
-                if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak || pending.isRedundant) {
+                if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak
+                    || pending.isRedundant
+                    || (pending.isReBreaking && !pending.breakConfig.reBreak)
+                    ) {
                     destroyBlock(pending)
                 }
                 pending.internalOnBreak()
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 6b109090b..c83a766a2 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -176,7 +176,9 @@ object PacketMine : Module(
         if (!reBreaking) {
             queuePositions.retainAllPositions(breakContexts)
         }
-        val request = breakRequest(breakContexts, build, rotation, hotbar, pendingInteractions) {
+        val request = breakRequest(
+            breakContexts, pendingInteractions, rotation, hotbar, interact, inventory, build,
+        ) {
             onStart { queuePositions.removePos(it); addBreak(it) }
             onUpdate { queuePositions.removePos(it) }
             onStop { removeBreak(it); breaks++ }
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index 22ed89207..704544806 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -156,7 +156,9 @@ class BuildTask @Ta5kBuilder constructor(
                                 requestContexts.addAll(breakResults.map { it.context })
                             }
 
-                            val request = breakRequest(requestContexts, build, rotation, hotbar, pendingInteractions) {
+                            val request = breakRequest(
+                                requestContexts, pendingInteractions, rotation, hotbar, interact, inventory, build,
+                            ) {
                                 onStop { breaks++ }
                                 onItemDrop?.let { onItemDrop ->
                                     onItemDrop { onItemDrop(it) }

From afee12ae2170be5b7794db8dfbd809fc2734815d Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 14 Jul 2025 21:01:42 +0100
Subject: [PATCH 285/364] use max value for tick pre in break manager to get
 ahead of the request handler onOpen

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index ba0633ac6..7bad79a89 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -131,7 +131,7 @@ object BreakManager : RequestHandler(
     override fun load(): String {
         super.load()
 
-        listen(priority = Int.MIN_VALUE) {
+        listen(priority = Int.MAX_VALUE) {
             // Cancelled but double breaking so requires break manager to continue the simulation
             breakInfos
                 .asSequence()

From b05a988ab74af0077a3efc73554cb63b12609651 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 00:14:52 +0100
Subject: [PATCH 286/364] isAir check in updateBreakProgress to account for
 unloaded chunks and cancelled checks when calling internalOnCancel

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 7bad79a89..fef1cf4f1 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -561,6 +561,7 @@ object BreakManager : RequestHandler(
      */
     private fun BreakInfo.cancelBreak() =
         runSafe {
+            if (isRedundant || abandoned) return@runSafe
             setBreakingTextureStage(player, world, -1)
             if (isPrimary) {
                 if (breaking) abortBreakPacket(world, interaction)
@@ -657,9 +658,9 @@ object BreakManager : RequestHandler(
         }
 
         val blockState = blockState(ctx.blockPos)
-        if (blockState.isEmpty) {
+        if (blockState.isEmpty || blockState.isAir) {
             info.nullify()
-            info.internalOnCancel()
+            if (!info.isRedundant) info.internalOnCancel()
             return false
         }
 

From 941b8224b088cc2a4225ed627700cf4d187fcbf1 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 00:18:44 +0100
Subject: [PATCH 287/364] inlined usages of internalOnCancel

---
 .../com/lambda/interaction/request/breaking/BreakInfo.kt    | 4 ----
 .../com/lambda/interaction/request/breaking/BreakManager.kt | 6 +++---
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index dabc756a0..26c83bf91 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -75,10 +75,6 @@ data class BreakInfo(
         }
     }
 
-    fun internalOnCancel() {
-        request.onCancel?.invoke(context.blockPos)
-    }
-
     fun updateInfo(context: BreakContext, request: BreakRequest? = null) {
         updatedThisTick = true
         this.context = context
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index fef1cf4f1..f1665efc7 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -571,7 +571,7 @@ object BreakManager : RequestHandler(
                 else abandoned = true
             }
 
-            internalOnCancel()
+            request.onCancel?.invoke(context.blockPos)
         }
 
     /**
@@ -647,7 +647,7 @@ object BreakManager : RequestHandler(
             }
             if (!startBreaking(info)) {
                 info.nullify()
-                info.internalOnCancel()
+                info.request.onCancel?.invoke(info.context.blockPos)
                 return false
             }
             val swing = config.swing
@@ -660,7 +660,7 @@ object BreakManager : RequestHandler(
         val blockState = blockState(ctx.blockPos)
         if (blockState.isEmpty || blockState.isAir) {
             info.nullify()
-            if (!info.isRedundant) info.internalOnCancel()
+            if (!info.isRedundant) info.request.onCancel?.invoke(info.context.blockPos)
             return false
         }
 

From 280e92ea79e14c2ff0a0e1f2a8460ebb59355895 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 03:34:43 +0100
Subject: [PATCH 288/364] missed an internalOnCancel

---
 .../lambda/interaction/request/breaking/BrokenBlockHandler.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index cae9d8649..4ecf525d5 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -65,7 +65,7 @@ object BrokenBlockHandler {
                 world.setBlockState(info.context.blockPos, info.context.cachedState)
             }
         }
-        info.internalOnCancel()
+        info.request.onCancel?.invoke(info.context.blockPos)
         info.pendingInteractions.remove(info.context)
     }
 

From 943d9423ecac2f98d98b6454a10767a2ed07026e Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 12:31:16 +0100
Subject: [PATCH 289/364] ActionInfo interface and PostActionHandler class to
 reduce duplicate code

---
 .../lambda/interaction/request/ActionInfo.kt  | 25 ++++++++++
 .../interaction/request/PostActionHandler.kt  | 49 +++++++++++++++++++
 .../interaction/request/breaking/BreakInfo.kt |  9 ++--
 .../request/breaking/BreakManager.kt          | 10 ++--
 .../request/breaking/BrokenBlockHandler.kt    | 49 +++----------------
 .../interacting/InteractedBlockHandler.kt     | 30 ++----------
 .../request/interacting/InteractionInfo.kt    |  7 +--
 .../request/interacting/InteractionManager.kt | 15 +++---
 .../interaction/request/placing/PlaceInfo.kt  |  7 +--
 .../request/placing/PlaceManager.kt           | 16 +++---
 .../request/placing/PlacedBlockHandler.kt     | 40 ++-------------
 11 files changed, 123 insertions(+), 134 deletions(-)
 create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt
 create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt
new file mode 100644
index 000000000..ff9176353
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.interaction.request
+
+import com.lambda.interaction.construction.context.BuildContext
+
+interface ActionInfo {
+    val context: BuildContext
+    val pendingInteractionsList: MutableCollection
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt
new file mode 100644
index 000000000..fa31f4d1e
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.interaction.request
+
+import com.lambda.config.groups.BuildConfig
+import com.lambda.event.events.ConnectionEvent
+import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
+import com.lambda.interaction.request.breaking.BrokenBlockHandler
+import com.lambda.util.collections.LimitedDecayQueue
+
+abstract class PostActionHandler {
+    abstract val pendingActions: LimitedDecayQueue
+
+    init {
+        listenUnsafe(priority = Int.MIN_VALUE) {
+            pendingActions.clear()
+        }
+    }
+
+    fun T.startPending() {
+        pendingActions.add(this)
+        pendingInteractionsList.add(context)
+    }
+
+    fun T.stopPending() {
+        pendingActions.remove(this)
+        pendingInteractionsList.remove(context)
+    }
+
+    fun setPendingConfigs(build: BuildConfig) {
+        BrokenBlockHandler.pendingActions.setSizeLimit(build.breaking.maxPendingBreaks)
+        BrokenBlockHandler.pendingActions.setDecayTime(build.interactionTimeout * 50L)
+    }
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 26c83bf91..185695da4 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -18,6 +18,7 @@
 package com.lambda.interaction.request.breaking
 
 import com.lambda.interaction.construction.context.BreakContext
+import com.lambda.interaction.request.ActionInfo
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.client.network.ClientPlayerInteractionManager
@@ -28,14 +29,13 @@ import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action
 
 data class BreakInfo(
-    var context: BreakContext,
+    override var context: BreakContext,
     var type: BreakType,
     var request: BreakRequest
-) {
+) : ActionInfo {
     val breakConfig get() = request.build.breaking
-    val pendingInteractions get() = request.pendingInteractions
+    override val pendingInteractionsList get() = request.pendingInteractions
 
-    var activeAge = 0
     var updatedThisTick = true
     var updatedProgressThisTick = false
 
@@ -82,7 +82,6 @@ data class BreakInfo(
     }
 
     fun tickStats() {
-        activeAge++
         updatedThisTick = false
         updatedProgressThisTick = false
     }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index f1665efc7..9ca7de679 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -48,7 +48,7 @@ import com.lambda.interaction.request.breaking.BreakManager.processRequest
 import com.lambda.interaction.request.breaking.BreakType.Primary
 import com.lambda.interaction.request.breaking.BreakType.ReBreak
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
-import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingBreaks
+import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending
 import com.lambda.interaction.request.interacting.InteractionManager
@@ -98,9 +98,9 @@ object BreakManager : RequestHandler(
         set(value) { breakInfos[1] = value }
     private val breakInfos = arrayOfNulls(2)
 
-    private val pendingBreakCount get() = breakInfos.count { it != null } + pendingBreaks.size
+    private val pendingBreakCount get() = breakInfos.count { it != null } + pendingActions.size
     override val blockedPositions
-        get() = breakInfos.mapNotNull { it?.context?.blockPos } + pendingBreaks.map { it.context.blockPos }
+        get() = breakInfos.mapNotNull { it?.context?.blockPos } + pendingActions.map { it.context.blockPos }
 
     private var activeRequest: BreakRequest? = null
 
@@ -283,7 +283,7 @@ object BreakManager : RequestHandler(
      * @see updateBreakProgress
      */
     private fun SafeContext.processRequest(breakRequest: BreakRequest?) {
-        pendingBreaks.cleanUp()
+        pendingActions.cleanUp()
 
         repeat(2) {
             breakRequest?.let { request ->
@@ -486,7 +486,7 @@ object BreakManager : RequestHandler(
         }
 
         primaryBreak = breakInfo
-        setPendingConfigs(request)
+        setPendingConfigs(request.build)
         return primaryBreak
     }
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 4ecf525d5..71fc392aa 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -19,12 +19,11 @@ package com.lambda.interaction.request.breaking
 
 import com.lambda.Lambda.mc
 import com.lambda.context.SafeContext
-import com.lambda.event.events.ConnectionEvent
 import com.lambda.event.events.EntityEvent
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.interaction.construction.processing.ProcessorRegistry
+import com.lambda.interaction.request.PostActionHandler
 import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode
 import com.lambda.interaction.request.breaking.BreakManager.lastPosStarted
 import com.lambda.interaction.request.breaking.BreakManager.matchesBlockItem
@@ -35,7 +34,6 @@ import com.lambda.util.BlockUtils.fluidState
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.matches
-import com.lambda.util.Communication.info
 import com.lambda.util.Communication.warn
 import com.lambda.util.collections.LimitedDecayQueue
 import com.lambda.util.player.gamemode
@@ -49,8 +47,8 @@ import net.minecraft.util.math.ChunkSectionPos
  *
  * @see BreakManager
  */
-object BrokenBlockHandler {
-    val pendingBreaks = LimitedDecayQueue(
+object BrokenBlockHandler : PostActionHandler() {
+    override val pendingActions = LimitedDecayQueue(
         TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
     ) { info ->
         mc.world?.let { world ->
@@ -66,13 +64,13 @@ object BrokenBlockHandler {
             }
         }
         info.request.onCancel?.invoke(info.context.blockPos)
-        info.pendingInteractions.remove(info.context)
+        info.pendingInteractionsList.remove(info.context)
     }
 
     init {
-        listen(priority = Int.MIN_VALUE + 1) { event ->
+        listen(priority = Int.MIN_VALUE) { event ->
             run {
-                pendingBreaks.firstOrNull { it.context.blockPos == event.pos }
+                pendingActions.firstOrNull { it.context.blockPos == event.pos }
                     ?: if (reBreak?.context?.blockPos == event.pos) reBreak
                     else null
             }?.let { pending ->
@@ -109,10 +107,10 @@ object BrokenBlockHandler {
             }
         }
 
-        listen(priority = Int.MIN_VALUE + 1) {
+        listen(priority = Int.MIN_VALUE) {
             if (it.entity !is ItemEntity) return@listen
             run {
-                pendingBreaks.firstOrNull { info -> matchesBlockItem(info, it.entity) }
+                pendingActions.firstOrNull { info -> matchesBlockItem(info, it.entity) }
                     ?: reBreak?.let { info ->
                         return@run if (matchesBlockItem(info, it.entity)) info
                         else null
@@ -128,37 +126,6 @@ object BrokenBlockHandler {
                 return@listen
             }
         }
-
-        listenUnsafe(priority = Int.MIN_VALUE + 1) {
-            pendingBreaks.clear()
-        }
-    }
-
-    /**
-     * Adds the [info] to the [BrokenBlockHandler], and requesters, pending interaction collections.
-     */
-    fun BreakInfo.startPending() {
-        pendingBreaks.add(this)
-        pendingInteractions.add(context)
-    }
-
-    /**
-     * Removes the [info] from the [BrokenBlockHandler], and requesters, pending interaction collections.
-     */
-    fun BreakInfo.stopPending() {
-        if (!isReBreaking) {
-            pendingBreaks.remove(this)
-            pendingInteractions.remove(context)
-        }
-    }
-
-    /**
-     * Sets the size limit and decay time for the [pendingBreaks] [LimitedDecayQueue]
-     * using the [request]'s configs
-     */
-    fun setPendingConfigs(request: BreakRequest) {
-        pendingBreaks.setSizeLimit(request.build.breaking.maxPendingBreaks)
-        pendingBreaks.setDecayTime(request.build.interactionTimeout * 50L)
     }
 
     /**
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
index 046bcf99d..218691a5d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
@@ -19,10 +19,9 @@ package com.lambda.interaction.request.interacting
 
 import com.lambda.Lambda.mc
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.event.events.ConnectionEvent
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
+import com.lambda.interaction.request.PostActionHandler
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.matches
 import com.lambda.util.Communication.info
@@ -32,8 +31,8 @@ import net.minecraft.block.BlockState
 import net.minecraft.util.Hand
 import net.minecraft.util.math.BlockPos
 
-object InteractedBlockHandler {
-    val pendingInteractions = LimitedDecayQueue(
+object InteractedBlockHandler : PostActionHandler() {
+    override val pendingActions = LimitedDecayQueue(
         TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
     ) {
         info("${it::class.simpleName} at ${it.context.blockPos.toShortString()} timed out")
@@ -45,10 +44,10 @@ object InteractedBlockHandler {
 
     init {
         listen(priority = Int.MIN_VALUE) { event ->
-            pendingInteractions
+            pendingActions
                 .firstOrNull { it.context.blockPos == event.pos }
                 ?.let { info ->
-                    removePendingInteract(info)
+                    info.stopPending()
 
                     if (!matchesTargetState(event.pos, info.context.expectedState, event.newState))
                         return@listen
@@ -59,25 +58,6 @@ object InteractedBlockHandler {
                         }
                 }
         }
-
-        listenUnsafe {
-            pendingInteractions.clear()
-        }
-    }
-
-    fun addPendingInteract(info: InteractionInfo) {
-        pendingInteractions.add(info)
-        info.pendingInteractionsList.add(info.context)
-    }
-
-    fun removePendingInteract(info: InteractionInfo) {
-        pendingInteractions.remove(info)
-        info.pendingInteractionsList.remove(info.context)
-    }
-
-    fun setPendingConfigs(request: InteractionRequest) {
-        pendingInteractions.setSizeLimit(request.build.maxPendingInteractions)
-        pendingInteractions.setDecayTime(request.build.interactionTimeout * 50L)
     }
 
     private fun matchesTargetState(pos: BlockPos, targetState: BlockState, newState: BlockState) =
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
index 28a2e0e5c..af3a793fd 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
@@ -20,9 +20,10 @@ package com.lambda.interaction.request.interacting
 import com.lambda.config.groups.InteractionConfig
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.InteractionContext
+import com.lambda.interaction.request.ActionInfo
 
 data class InteractionInfo(
-    val context: InteractionContext,
-    val pendingInteractionsList: MutableCollection,
+    override val context: InteractionContext,
+    override val pendingInteractionsList: MutableCollection,
     val interact: InteractionConfig
-)
\ No newline at end of file
+) : ActionInfo
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index ac6f6e077..0800db7e0 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -29,13 +29,12 @@ import com.lambda.interaction.request.ManagerUtils.isPosBlocked
 import com.lambda.interaction.request.PositionBlocking
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.breaking.BreakManager
-import com.lambda.interaction.request.interacting.InteractedBlockHandler.addPendingInteract
-import com.lambda.interaction.request.interacting.InteractedBlockHandler.pendingInteractions
+import com.lambda.interaction.request.interacting.InteractedBlockHandler.pendingActions
 import com.lambda.interaction.request.interacting.InteractedBlockHandler.setPendingConfigs
+import com.lambda.interaction.request.interacting.InteractedBlockHandler.startPending
 import com.lambda.interaction.request.interacting.InteractionManager.activeRequest
 import com.lambda.interaction.request.interacting.InteractionManager.processRequest
 import com.lambda.interaction.request.placing.PlaceManager
-import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements
 import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
 import net.minecraft.util.Hand
 
@@ -54,7 +53,7 @@ object InteractionManager : RequestHandler(
     private var maxInteractionsThisTick = 0
 
     override val blockedPositions
-        get() = pendingInteractions.map { it.context.blockPos }
+        get() = pendingActions.map { it.context.blockPos }
 
     init {
         listen(priority = Int.MIN_VALUE) {
@@ -79,7 +78,7 @@ object InteractionManager : RequestHandler(
     }
 
     fun SafeContext.processRequest(request: InteractionRequest) {
-        pendingInteractions.cleanUp()
+        pendingActions.cleanUp()
         
         if (request.fresh) populateFrom(request)
 
@@ -94,7 +93,7 @@ object InteractionManager : RequestHandler(
             if (!ctx.requestDependencies(request)) return
 
             if (interact.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) {
-                addPendingInteract(InteractionInfo(ctx, request.pendingInteractionsList, interact))
+                InteractionInfo(ctx, request.pendingInteractionsList, interact).startPending()
             }
             if (interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
                 interaction.interactBlock(player, Hand.MAIN_HAND, ctx.result)
@@ -110,12 +109,12 @@ object InteractionManager : RequestHandler(
     }
 
     private fun populateFrom(request: InteractionRequest) {
-        setPendingConfigs(request)
+        setPendingConfigs(request.build)
         potentialInteractions = request.contexts
             .filter { !isPosBlocked(it.blockPos) }
             .toMutableList()
 
-        val pendingLimit =  (request.build.maxPendingInteractions - pendingPlacements.size).coerceAtLeast(0)
+        val pendingLimit =  (request.build.maxPendingInteractions - pendingActions.size).coerceAtLeast(0)
         maxInteractionsThisTick = (request.build.interactionsPerTick.coerceAtMost(pendingLimit))
     }
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt
index 78daf953b..9047ed645 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt
@@ -19,10 +19,11 @@ package com.lambda.interaction.request.placing
 
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.PlaceContext
+import com.lambda.interaction.request.ActionInfo
 
 data class PlaceInfo(
-    val context: PlaceContext,
+    override val context: PlaceContext,
+    override val pendingInteractionsList: MutableCollection,
     val onPlace: () -> Unit,
-    val pendingInteractionsList: MutableCollection,
     val placeConfig: PlaceConfig
-)
\ No newline at end of file
+) : ActionInfo
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index faf0cd6f5..3efa1c394 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -33,9 +33,9 @@ import com.lambda.interaction.request.breaking.BreakManager
 import com.lambda.interaction.request.interacting.InteractionManager
 import com.lambda.interaction.request.placing.PlaceManager.activeRequest
 import com.lambda.interaction.request.placing.PlaceManager.processRequest
-import com.lambda.interaction.request.placing.PlacedBlockHandler.addPendingPlace
-import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements
+import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingActions
 import com.lambda.interaction.request.placing.PlacedBlockHandler.setPendingConfigs
+import com.lambda.interaction.request.placing.PlacedBlockHandler.startPending
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.Communication.warn
 import com.lambda.util.player.gamemode
@@ -78,7 +78,7 @@ object PlaceManager : RequestHandler(
         { player -> shouldSneak == player.isSneaking }
 
     override val blockedPositions
-        get() = pendingPlacements.map { it.context.blockPos }
+        get() = pendingActions.map { it.context.blockPos }
 
     fun Any.onPlace(
         alwaysListen: Boolean = false,
@@ -132,7 +132,7 @@ object PlaceManager : RequestHandler(
      * @see placeBlock
      */
     fun SafeContext.processRequest(request: PlaceRequest) {
-        pendingPlacements.cleanUp()
+        pendingActions.cleanUp()
 
         if (request.fresh) populateFrom(request)
 
@@ -162,12 +162,12 @@ object PlaceManager : RequestHandler(
     private fun populateFrom(request: PlaceRequest) {
         val place = request.build.placing
 
-        setPendingConfigs(request)
+        setPendingConfigs(request.build)
         potentialPlacements = request.contexts
             .filter { !isPosBlocked(it.blockPos) }
             .toMutableList()
 
-        val pendingLimit =  (place.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0)
+        val pendingLimit =  (place.maxPendingPlacements - pendingActions.size).coerceAtLeast(0)
         maxPlacementsThisTick = (place.placementsPerTick.coerceAtMost(pendingLimit))
     }
 
@@ -274,9 +274,7 @@ object PlaceManager : RequestHandler(
         val stackInHand = player.getStackInHand(hand)
         val stackCountPre = stackInHand.count
         if (placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) {
-            addPendingPlace(
-                PlaceInfo(placeContext, request.onPlace, request.pendingInteractions, placeConfig)
-            )
+            PlaceInfo(placeContext, request.pendingInteractions, request.onPlace, placeConfig).startPending()
         }
 
         if (placeConfig.airPlace == PlaceConfig.AirPlaceMode.Grim) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
index a5c775e98..4358d196c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
@@ -18,12 +18,10 @@
 package com.lambda.interaction.request.placing
 
 import com.lambda.Lambda.mc
-import com.lambda.event.events.ConnectionEvent
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
+import com.lambda.interaction.request.PostActionHandler
 import com.lambda.interaction.request.placing.PlaceManager.placeSound
-import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.item
 import com.lambda.util.BlockUtils.matches
@@ -32,8 +30,8 @@ import com.lambda.util.Communication.warn
 import com.lambda.util.collections.LimitedDecayQueue
 import net.minecraft.item.BlockItem
 
-object PlacedBlockHandler {
-    val pendingPlacements = LimitedDecayQueue(
+object PlacedBlockHandler : PostActionHandler() {
+    override val pendingActions = LimitedDecayQueue(
         TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
     ) {
         info("${it::class.simpleName} at ${it.context.blockPos.toShortString()} timed out")
@@ -45,10 +43,10 @@ object PlacedBlockHandler {
 
     init {
         listen(priority = Int.MIN_VALUE) { event ->
-            pendingPlacements
+            pendingActions
                 .firstOrNull { it.context.blockPos == event.pos }
                 ?.let { info ->
-                    removePendingPlace(info)
+                    info.stopPending()
 
                     // return if the block wasn't placed properly
                     if (!event.newState.matches(info.context.expectedState)) {
@@ -64,33 +62,5 @@ object PlacedBlockHandler {
                     info.onPlace()
                 }
         }
-
-        listenUnsafe {
-            pendingPlacements.clear()
-        }
-    }
-
-    /**
-     * Adds the info to the [PlacedBlockHandler], and requesters, pending interaction collections.
-     */
-    fun addPendingPlace(info: PlaceInfo) {
-        pendingPlacements.add(info)
-        info.pendingInteractionsList.add(info.context)
-    }
-
-    /**
-     * Removes the info from the [PlacedBlockHandler], and requesters, pending interaction collections.
-     */
-    private fun removePendingPlace(info: PlaceInfo) {
-        pendingPlacements.remove(info)
-        info.pendingInteractionsList.remove(info.context)
-    }
-
-    /**
-     * Sets the size limit and decay time for the [pendingPlacements] using the [request]'s configs
-     */
-    fun setPendingConfigs(request: PlaceRequest) {
-        pendingPlacements.setSizeLimit(request.build.placing.maxPendingPlacements)
-        pendingPlacements.setDecayTime(request.build.interactionTimeout * 50L)
     }
 }
\ No newline at end of file

From f492c5a54fab084c8f6dd67cba2355cabadc6a0b Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 13:01:54 +0100
Subject: [PATCH 290/364] fixed config variable in Request

---
 .../interaction/construction/context/BreakContext.kt      | 2 +-
 .../construction/context/InteractionContext.kt            | 2 +-
 .../main/kotlin/com/lambda/interaction/request/Request.kt | 5 ++---
 .../lambda/interaction/request/breaking/BreakRequest.kt   | 3 ++-
 .../lambda/interaction/request/hotbar/HotbarManager.kt    | 4 ++--
 .../lambda/interaction/request/hotbar/HotbarRequest.kt    | 8 ++++----
 .../interaction/request/interacting/InteractionManager.kt | 8 ++++----
 .../interaction/request/interacting/InteractionRequest.kt | 4 ++--
 .../lambda/interaction/request/placing/PlaceRequest.kt    | 3 ++-
 .../interaction/request/rotating/RotationRequest.kt       | 4 ++--
 10 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
index 4eb228a45..e65ca034b 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
@@ -67,6 +67,6 @@ data class BreakContext(
 
     fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean {
         val request = HotbarRequest(hotbarIndex, breakRequest.hotbar, breakRequest.hotbar.keepTicks.coerceAtLeast(minKeepTicks))
-        return request.hotbar.request(request, false).done
+        return request.config.request(request, false).done
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
index 4bf0762a1..4db49e42e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
@@ -67,7 +67,7 @@ class InteractionContext(
 
     fun requestDependencies(request: InteractionRequest): Boolean {
         val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false)
-        val validRotation = if (request.interact.rotate) {
+        val validRotation = if (request.config.rotate) {
             request.rotation.request(rotation, false).done
         } else true
         return hotbarRequest.done && validRotation
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
index 97c8db0a3..4f20ae8be 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
@@ -17,9 +17,8 @@
 
 package com.lambda.interaction.request
 
-abstract class Request(
-    val config: RequestConfig<*>
-) {
+abstract class Request {
+    abstract val config: RequestConfig<*>
     var fresh = true
 
     abstract val done: Boolean
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index b2dbae649..f0f8610a8 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -42,7 +42,8 @@ data class BreakRequest(
     val inventory: InventoryConfig,
     val interact: InteractionConfig,
     val build: BuildConfig
-) : Request(build.breaking) {
+) : Request() {
+    override val config = build.breaking
     var onStart: ((BlockPos) -> Unit)? = null
     var onUpdate: ((BlockPos) -> Unit)? = null
     var onStop: ((BlockPos) -> Unit)? = null
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index b8d59dab1..439556208 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -75,7 +75,7 @@ object HotbarManager : RequestHandler(
     }
 
     override fun SafeContext.handleRequest(request: HotbarRequest) {
-        val config = request.hotbar
+        val config = request.config
         maxSwapsThisTick = config.swapsPerTick
         swapDelay = swapDelay.coerceAtMost(config.swapDelay)
 
@@ -114,7 +114,7 @@ object HotbarManager : RequestHandler(
     private fun SafeContext.checkResetSwap() {
         activeRequest?.let { activeInfo ->
             val canStopSwap = swapsThisTick < maxSwapsThisTick
-            if (activeInfo.keepTicks <= 0 && tickStage in activeInfo.hotbar.sequenceStageMask && canStopSwap) {
+            if (activeInfo.keepTicks <= 0 && tickStage in activeInfo.config.sequenceStageMask && canStopSwap) {
                 activeRequest = null
                 interaction.syncSelectedSlot()
             }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
index c0eb9e272..ffdadf357 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
@@ -21,10 +21,10 @@ import com.lambda.interaction.request.Request
 
 class HotbarRequest(
     val slot: Int,
-    val hotbar: HotbarConfig,
-    var keepTicks: Int = hotbar.keepTicks,
-    var swapPause: Int = hotbar.swapPause
-) : Request(hotbar) {
+    override val config: HotbarConfig,
+    var keepTicks: Int = config.keepTicks,
+    var swapPause: Int = config.swapPause
+) : Request() {
     var activeRequestAge = 0
     var swapPauseAge = 0
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index 0800db7e0..734946ba3 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -87,15 +87,15 @@ object InteractionManager : RequestHandler(
         val iterator = potentialInteractions.iterator()
         while (iterator.hasNext()) {
             if (interactionsThisTick + 1 > maxInteractionsThisTick) break
-            val interact = request.interact
+            val config = request.config
             val ctx = iterator.next()
 
             if (!ctx.requestDependencies(request)) return
 
-            if (interact.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) {
-                InteractionInfo(ctx, request.pendingInteractionsList, interact).startPending()
+            if (config.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) {
+                InteractionInfo(ctx, request.pendingInteractionsList, config).startPending()
             }
-            if (interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
+            if (config.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
                 interaction.interactBlock(player, Hand.MAIN_HAND, ctx.result)
             } else {
                 interaction.sendSequencedPacket(world) { sequence ->
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
index c0c0be422..b40b416a3 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
@@ -32,11 +32,11 @@ data class InteractionRequest(
     val contexts: Collection,
     val onInteract: ((BlockPos) -> Unit)?,
     val pendingInteractionsList: MutableCollection,
-    val interact: InteractionConfig,
+    override val config: InteractionConfig,
     val build: BuildConfig,
     val hotbar: HotbarConfig,
     val rotation: RotationConfig
-) : Request(interact) {
+) : Request() {
     override val done: Boolean
         get() = contexts.all { mc.world?.getBlockState(it.blockPos)?.matches(it.expectedState) == true }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
index c598dcc9c..744b1f598 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
@@ -34,7 +34,8 @@ data class PlaceRequest(
     val hotbar: HotbarConfig,
     val pendingInteractions: MutableCollection,
     val onPlace: () -> Unit
-) : Request(build.placing) {
+) : Request() {
+    override val config = build.placing
     override val done: Boolean
         get() = runSafe {
             contexts.all { it.expectedState.matches(blockState(it.blockPos)) }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
index ea800c1c4..ddf95452a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
@@ -24,12 +24,12 @@ import com.lambda.threading.runSafe
 data class RotationRequest(
     val target: RotationTarget,
     val mode: RotationMode,
-    val rot: RotationConfig,
+    override val config: RotationConfig,
     var keepTicks: Int = 3,
     var decayTicks: Int = 0,
     val turnSpeed: () -> Double = { 180.0 },
     val speedMultiplier: Double = 1.0
-) : Request(rot) {
+) : Request() {
     var age = 0
 
     constructor(

From d7a1715130b764828e85d6effde911edece19977 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 14:33:04 +0100
Subject: [PATCH 291/364] linear renders accounting for fudge factor

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 9ca7de679..1d8bc83c0 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -232,7 +232,7 @@ object BreakManager : RequestHandler(
                     val progress = (info.breakingTicks * breakDelta).let {
                         if (info.isPrimary) it * (2 - info.breakConfig.breakThreshold)
                         else it
-                    }.toDouble()
+                    }.toDouble() * (1 - (breakDelta * config.fudgeFactor))
                     val state = info.context.cachedState
                     val boxes = state.getOutlineShape(world, info.context.blockPos).boundingBoxes.map {
                         it.offset(info.context.blockPos)

From c3ade02f8a97a81278c65dd6714edef83e449f72 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 14:45:08 +0100
Subject: [PATCH 292/364] dont call onCancel when abandoning as the break isnt
 actually cancelled, but kept going by the break manager itself if
 unsafeCancels is disabled

---
 .../lambda/interaction/request/breaking/BreakManager.kt   | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 1d8bc83c0..5bebe6b69 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -566,12 +566,14 @@ object BreakManager : RequestHandler(
             if (isPrimary) {
                 if (breaking) abortBreakPacket(world, interaction)
                 nullify()
+                request.onCancel?.invoke(context.blockPos)
             } else if (isSecondary) {
-                if (breakConfig.unsafeCancels) makeRedundant()
+                if (breakConfig.unsafeCancels) {
+                    makeRedundant()
+                    request.onCancel?.invoke(context.blockPos)
+                }
                 else abandoned = true
             }
-
-            request.onCancel?.invoke(context.blockPos)
         }
 
     /**

From 7b0c2466f588deb787a268d4cff31346be39aaeb Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 15 Jul 2025 22:59:58 +0100
Subject: [PATCH 293/364] was dumb and didnt fix renders the way i thought i
 did. Also only reset breaking texture if redundant or primary when cancelling

---
 .../lambda/interaction/request/breaking/BreakInfo.kt |  4 ++--
 .../interaction/request/breaking/BreakManager.kt     | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 185695da4..eca4cfdb7 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -101,8 +101,8 @@ data class BreakInfo(
 
     private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int {
         val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, player.mainHandStack)
-        val progress = (breakDelta * breakingTicks) / getBreakThreshold()
-        return if (progress > 0.0f) (progress * 10.0f).toInt() else -1
+        val progress = (breakDelta * breakingTicks) / (getBreakThreshold() + (breakDelta * breakConfig.fudgeFactor))
+        return if (progress > 0.0f) (progress * 10.0f).toInt().coerceAtMost(10) else -1
     }
 
     fun getBreakThreshold() = type.getBreakThreshold(breakConfig)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 5bebe6b69..d2b9bc184 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -229,10 +229,8 @@ object BreakManager : RequestHandler(
                         info.breakConfig,
                         if (!info.isRedundant) player.inventory.getStack(info.context.hotbarIndex) else null
                     )
-                    val progress = (info.breakingTicks * breakDelta).let {
-                        if (info.isPrimary) it * (2 - info.breakConfig.breakThreshold)
-                        else it
-                    }.toDouble() * (1 - (breakDelta * config.fudgeFactor))
+                    val threshold = if (info.isPrimary) info.breakConfig.breakThreshold else 1f
+                    val progress = (info.breakingTicks * breakDelta).toDouble() / (threshold + (breakDelta * config.fudgeFactor))
                     val state = info.context.cachedState
                     val boxes = state.getOutlineShape(world, info.context.blockPos).boundingBoxes.map {
                         it.offset(info.context.blockPos)
@@ -562,17 +560,19 @@ object BreakManager : RequestHandler(
     private fun BreakInfo.cancelBreak() =
         runSafe {
             if (isRedundant || abandoned) return@runSafe
-            setBreakingTextureStage(player, world, -1)
             if (isPrimary) {
                 if (breaking) abortBreakPacket(world, interaction)
                 nullify()
+                setBreakingTextureStage(player, world, -1)
                 request.onCancel?.invoke(context.blockPos)
             } else if (isSecondary) {
                 if (breakConfig.unsafeCancels) {
                     makeRedundant()
+                    setBreakingTextureStage(player, world, -1)
                     request.onCancel?.invoke(context.blockPos)
+                } else {
+                    abandoned = true
                 }
-                else abandoned = true
             }
         }
 

From 165de50463bec02c10b6a2e794991067f55311f2 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 16 Jul 2025 02:42:41 +0100
Subject: [PATCH 294/364] swing hand type setting for interactions

---
 .../kotlin/com/lambda/config/groups/InteractionConfig.kt     | 5 +++++
 .../kotlin/com/lambda/config/groups/InteractionSettings.kt   | 1 +
 .../interaction/request/interacting/InteractionManager.kt    | 4 ++++
 3 files changed, 10 insertions(+)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
index cbe9d2bf3..070b593ce 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
@@ -79,6 +79,11 @@ abstract class InteractionConfig(
      */
     abstract val swingHand: Boolean
 
+    /**
+     * The style of hand swing to use
+     */
+    abstract val interactSwingType: BuildConfig.SwingType
+
     override fun requestInternal(request: InteractionRequest, queueIfClosed: Boolean) {
         InteractionManager.request(request, queueIfClosed)
     }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
index 471a9e1da..a1626c7f8 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
@@ -66,6 +66,7 @@ class InteractionSettings(
 
     // Swing
     override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", visibility = vis)
+    override val interactSwingType by c.setting("Interact Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swingHand }
 
     companion object {
         const val DEFAULT_ATTACK_REACH = 3.0
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index 734946ba3..1afef557f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -35,6 +35,7 @@ import com.lambda.interaction.request.interacting.InteractedBlockHandler.startPe
 import com.lambda.interaction.request.interacting.InteractionManager.activeRequest
 import com.lambda.interaction.request.interacting.InteractionManager.processRequest
 import com.lambda.interaction.request.placing.PlaceManager
+import com.lambda.util.player.swingHand
 import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
 import net.minecraft.util.Hand
 
@@ -102,6 +103,9 @@ object InteractionManager : RequestHandler(
                     PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, ctx.result, sequence)
                 }
             }
+            if (request.config.swingHand) {
+                swingHand(request.config.interactSwingType, Hand.MAIN_HAND)
+            }
             request.onInteract?.invoke(ctx.blockPos)
             interactionsThisTick++
             iterator.remove()

From fe14b56580da8e8ee82f22e7787d339463cfcb7d Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 16 Jul 2025 03:08:55 +0100
Subject: [PATCH 295/364] omit top half DOUBLE_BLOCK_HALF property holders

---
 .../processing/ProcessorRegistry.kt           | 24 +++++++-----
 .../preprocessors/OmitPreProcessor.kt         | 39 +++++++++++++++++++
 .../construction/simulation/BuildSimulator.kt |  2 +-
 3 files changed, 54 insertions(+), 11 deletions(-)
 create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/OmitPreProcessor.kt

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt
index d8ae95056..85fae3a30 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt
@@ -28,7 +28,7 @@ import net.minecraft.util.math.BlockPos
 
 object ProcessorRegistry : Loadable {
     private val processors = getInstances()
-    private val processorCache = mutableMapOf()
+    private val processorCache = mutableMapOf()
 
     val postProcessedProperties = setOf(
         Properties.EXTENDED,
@@ -107,22 +107,26 @@ object ProcessorRegistry : Loadable {
 
     override fun load() = "Loaded ${processors.size} pre processors"
 
-    fun TargetState.getProcessingInfo(pos: BlockPos): PreProcessingInfo =
-        (this as? TargetState.State)?.let { state ->
-            val get: () -> PreProcessingInfo = {
-
+    fun TargetState.getProcessingInfo(pos: BlockPos): PreProcessingInfo? {
+        return if (this is TargetState.State) {
+            val targetState = this as? TargetState.State ?: return null
+            val get: () -> PreProcessingInfo? = get@ {
                 val infoAccumulator = PreProcessingInfoAccumulator()
 
                 processors.forEach {
-                    if (!it.acceptsState(state.blockState)) return@forEach
-                    it.preProcess(state.blockState, pos, infoAccumulator)
+                    if (!it.acceptsState(targetState.blockState)) return@forEach
+                    it.preProcess(targetState.blockState, pos, infoAccumulator)
+                    if (infoAccumulator.shouldBeOmitted) {
+                        return@get null
+                    }
                 }
 
                 infoAccumulator.complete()
             }
-            if (isExemptFromCache(state)) get()
-            else processorCache.getOrPut(state.blockState, get)
-        } ?: PreProcessingInfo.DEFAULT
+            if (isExemptFromCache(targetState)) get()
+            else processorCache.getOrPut(targetState.blockState, get)
+        } else PreProcessingInfo.DEFAULT
+    }
 
     private fun isExemptFromCache(state: TargetState.State) =
         state.blockState.block is SlabBlock && state.blockState.get(Properties.SLAB_TYPE) == SlabType.DOUBLE
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/OmitPreProcessor.kt b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/OmitPreProcessor.kt
new file mode 100644
index 000000000..30837ad06
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/processing/preprocessors/OmitPreProcessor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.interaction.construction.processing.preprocessors
+
+import com.lambda.interaction.construction.processing.PlacementProcessor
+import com.lambda.interaction.construction.processing.PreProcessingInfoAccumulator
+import net.minecraft.block.BlockState
+import net.minecraft.block.enums.DoubleBlockHalf
+import net.minecraft.state.property.Properties
+import net.minecraft.util.math.BlockPos
+
+// Collected using reflections and then accessed from a collection in ProcessorRegistry
+@Suppress("unused")
+object OmitPreProcessor : PlacementProcessor() {
+    override fun acceptsState(state: BlockState) = true
+
+    override fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) {
+        if (Properties.DOUBLE_BLOCK_HALF in state.properties) {
+            if (state.get(Properties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER) {
+                accumulator.omitPlacement()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index f765feb2a..e2dd93502 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -97,7 +97,7 @@ object BuildSimulator {
         build: BuildConfig = TaskFlowModule.build,
     ) = runSafe {
         structure.entries.flatMap { (pos, target) ->
-            val preProcessing = target.getProcessingInfo(pos)
+            val preProcessing = target.getProcessingInfo(pos) ?: return@flatMap emptySet()
             checkRequirements(pos, target, build).let {
                 if (it.isEmpty()) return@let
                 return@flatMap it

From 837f5a9c42f0ba81fdb4a9ad205c4c7b1931da33 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 16 Jul 2025 12:08:12 +0100
Subject: [PATCH 296/364] separated interact settings into their own page in
 the build config and removed unused priority var's

---
 .../com/lambda/config/groups/BreakSettings.kt |  4 +-
 .../com/lambda/config/groups/BuildConfig.kt   |  3 +
 .../com/lambda/config/groups/BuildSettings.kt | 11 ++-
 .../lambda/config/groups/HotbarSettings.kt    |  4 +-
 .../lambda/config/groups/InteractConfig.kt    | 34 +++++++++
 .../lambda/config/groups/InteractSettings.kt  | 30 ++++++++
 .../lambda/config/groups/InteractionConfig.kt | 24 +-----
 .../config/groups/InteractionSettings.kt      | 10 +--
 .../com/lambda/config/groups/PlaceSettings.kt |  6 +-
 .../lambda/config/groups/RotationSettings.kt  |  4 +-
 .../construction/simulation/BuildSimulator.kt | 76 +++++++++----------
 .../construction/simulation/Simulation.kt     |  6 +-
 .../interaction/request/RequestConfig.kt      |  4 +-
 .../request/breaking/BreakConfig.kt           |  5 +-
 .../request/hotbar/HotbarConfig.kt            |  5 +-
 .../request/interacting/InteractionInfo.kt    |  4 +-
 .../request/interacting/InteractionRequest.kt |  4 +-
 .../request/placing/PlaceConfig.kt            |  4 +-
 .../request/rotating/RotationConfig.kt        |  5 +-
 .../rotating/visibilty/RotationTargets.kt     |  6 +-
 .../lambda/module/modules/client/Baritone.kt  |  2 +-
 .../module/modules/client/TaskFlowModule.kt   |  2 +-
 .../lambda/module/modules/combat/KillAura.kt  |  9 ++-
 .../lambda/module/modules/movement/Speed.kt   |  2 +-
 .../lambda/module/modules/player/AntiAim.kt   |  2 +-
 .../lambda/module/modules/player/Scaffold.kt  |  7 +-
 .../kotlin/com/lambda/task/tasks/BuildTask.kt | 22 +++---
 .../com/lambda/task/tasks/OpenContainer.kt    |  4 +-
 .../com/lambda/task/tasks/PlaceContainer.kt   |  2 +-
 29 files changed, 166 insertions(+), 135 deletions(-)
 create mode 100644 common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt
 create mode 100644 common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index baed12f23..530b86fdf 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -19,16 +19,14 @@ package com.lambda.config.groups
 
 import com.lambda.config.Configurable
 import com.lambda.event.events.TickEvent
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.breaking.BreakConfig
 import com.lambda.util.BlockUtils.allSigns
 import java.awt.Color
 
 class BreakSettings(
     c: Configurable,
-    priority: Priority = 0,
     vis: () -> Boolean = { true }
-) : BreakConfig(priority) {
+) : BreakConfig() {
     val page by c.setting("Break Page", Page.General, visibility = vis)
     override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
     override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt
index 792b1efdf..3f531a3d5 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt
@@ -32,6 +32,9 @@ interface BuildConfig {
     // Placing
     val placing: PlaceSettings
 
+    // Interacting
+    val interacting: InteractSettings
+
     enum class SwingType {
         Vanilla,
         Server,
diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
index 12374504f..25b5523de 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
@@ -26,7 +26,7 @@ class BuildSettings(
     vis: () -> Boolean = { true }
 ) : BuildConfig {
     enum class Page {
-        General, Break, Place
+        General, Break, Place, Interact
     }
 
     private val page by c.setting("Build Page", Page.General, "Current page", vis)
@@ -44,5 +44,12 @@ class BuildSettings(
     // Placing
     override val placing = PlaceSettings(c) { page == Page.Place && vis() }
 
-    override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && ((page == Page.Place && placing.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) || (page == Page.Break && breaking.breakConfirmation != BreakConfirmationMode.None)) }
+    //Interacting
+    override val interacting = InteractSettings(c) { page == Page.Interact && vis() }
+
+    override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") {
+        vis() && ((page == Page.Place && placing.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None)
+                || (page == Page.Break && breaking.breakConfirmation != BreakConfirmationMode.None)
+                || (page == Page.Interact && interacting.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.None))
+    }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
index 9de102178..8c866e266 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
@@ -19,14 +19,12 @@ package com.lambda.config.groups
 
 import com.lambda.config.Configurable
 import com.lambda.event.events.TickEvent
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.hotbar.HotbarConfig
 
 class HotbarSettings(
     c: Configurable,
-    priority: Priority = 0,
     vis: () -> Boolean = { true }
-) : HotbarConfig(priority) {
+) : HotbarConfig() {
     override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", visibility = vis)
     override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", visibility = vis)
     override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt
new file mode 100644
index 000000000..99ec90468
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.config.groups
+
+import com.lambda.config.groups.InteractionConfig.InteractConfirmationMode
+import com.lambda.interaction.request.RequestConfig
+import com.lambda.interaction.request.interacting.InteractionManager
+import com.lambda.interaction.request.interacting.InteractionRequest
+
+abstract class InteractConfig : RequestConfig() {
+    abstract val rotate: Boolean
+    abstract val swingHand: Boolean
+    abstract val interactSwingType: BuildConfig.SwingType
+    abstract val interactConfirmationMode: InteractConfirmationMode
+
+    override fun requestInternal(request: InteractionRequest, queueIfClosed: Boolean) {
+        InteractionManager.request(request, queueIfClosed)
+    }
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
new file mode 100644
index 000000000..ecb6b3500
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.config.groups
+
+import com.lambda.config.Configurable
+
+class InteractSettings(
+    c: Configurable,
+    vis: () -> Boolean =  { true }
+) : InteractConfig() {
+    override val rotate by c.setting("Rotate For Interact", true, "Rotates the player to look at the block when interacting", visibility = vis)
+    override val swingHand by c.setting("Swing On Interact", true, "Swings the players hand after interacting", visibility = vis)
+    override val interactSwingType by c.setting("Interact Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swingHand }
+    override val interactConfirmationMode by c.setting("Interact Confirmation Mode", InteractionConfig.InteractConfirmationMode.InteractThenAwait, "The style of confirmation for interactions", visibility = vis)
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
index 070b593ce..3817e4d14 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
@@ -22,9 +22,7 @@ import com.lambda.interaction.request.interacting.InteractionManager
 import com.lambda.interaction.request.interacting.InteractionRequest
 import com.lambda.interaction.request.rotating.visibilty.PointSelection
 
-abstract class InteractionConfig(
-    priority: Int
-) : RequestConfig(priority) {
+abstract class InteractionConfig : RequestConfig() {
     /**
      * Maximum entity interaction distance
      */
@@ -35,11 +33,6 @@ abstract class InteractionConfig(
      */
     abstract val interactReach: Double
 
-    /**
-     * Rotates the player for block interactions. For example, right-clicking a chest to open it.
-     */
-    abstract val rotate: Boolean
-
     /**
      * Maximum possible interaction distance
      *
@@ -69,21 +62,6 @@ abstract class InteractionConfig(
      */
     abstract val pointSelection: PointSelection
 
-    /**
-     * The method of confirming the interaction had taken place server side
-     */
-    abstract val interactConfirmationMode: InteractConfirmationMode
-
-    /**
-     * Whether to swing the hand when interacting.
-     */
-    abstract val swingHand: Boolean
-
-    /**
-     * The style of hand swing to use
-     */
-    abstract val interactSwingType: BuildConfig.SwingType
-
     override fun requestInternal(request: InteractionRequest, queueIfClosed: Boolean) {
         InteractionManager.request(request, queueIfClosed)
     }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
index a1626c7f8..1dae68bf8 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
@@ -26,14 +26,12 @@ class InteractionSettings(
     c: Configurable,
     private val usage: InteractionMask,
     vis: () -> Boolean = { true },
-) : InteractionConfig(0) {
+) : InteractionConfig() {
     // Reach
     private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", visibility = vis)
     private val attackReachSetting = if (usage.entity) c.setting("Attack Reach", DEFAULT_ATTACK_REACH, 1.0..10.0, 0.01, "Maximum entity interaction distance") { vis() && !useDefaultReach } else null
     private val interactReachSetting = if (usage.block) c.setting("Interact Reach", DEFAULT_INTERACT_REACH, 1.0..10.0, 0.01, "Maximum block interaction distance") { vis() && !useDefaultReach } else null
 
-    override val rotate by c.setting("Rotate For Interactions", true, "Rotates the player for block interactions. For example, right-clicking a chest to open it", visibility = vis)
-
     override val attackReach: Double get() {
         check(usage.entity) {
             "Given interaction config has no attack reach implementation"
@@ -62,12 +60,6 @@ class InteractionSettings(
     override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", visibility = vis)
     override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The strategy to select the best hit point", visibility = vis)
 
-    override val interactConfirmationMode by c.setting("Interact Confirmation Mode", InteractConfirmationMode.InteractThenAwait, "The style of confirmation used when interacting", visibility = vis)
-
-    // Swing
-    override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", visibility = vis)
-    override val interactSwingType by c.setting("Interact Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swingHand }
-
     companion object {
         const val DEFAULT_ATTACK_REACH = 3.0
         const val DEFAULT_INTERACT_REACH = 4.5
diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
index c9ff99ff7..3f61ad31c 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
@@ -19,14 +19,12 @@ package com.lambda.config.groups
 
 import com.lambda.config.Configurable
 import com.lambda.event.events.TickEvent
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.placing.PlaceConfig
 
 class PlaceSettings(
     c: Configurable,
-    priority: Priority = 0,
     vis: () -> Boolean = { true }
-) : PlaceConfig(priority) {
+) : PlaceConfig() {
     override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", visibility = vis)
     override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces", visibility = vis)
     override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() }
@@ -34,7 +32,7 @@ class PlaceSettings(
     override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation", visibility = vis)
     override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements", visibility = vis)
     override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick", visibility = vis)
-    override val swing by c.setting("Swing", true, "Swings the players hand when placing", visibility = vis)
+    override val swing by c.setting("Swing On Place", true, "Swings the players hand when placing", visibility = vis)
     override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing }
     override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds", visibility = vis)
 }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt
index 9cd78b7bd..f0f88d13d 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt
@@ -18,7 +18,6 @@
 package com.lambda.config.groups
 
 import com.lambda.config.Configurable
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.interaction.request.rotating.RotationMode
 import kotlin.math.PI
@@ -30,9 +29,8 @@ import kotlin.random.Random
 
 class RotationSettings(
     c: Configurable,
-    priority: Priority = 0,
     vis: () -> Boolean = { true }
-) : RotationConfig(priority) {
+) : RotationConfig() {
     override var rotationMode by c.setting("Mode", RotationMode.Sync, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", visibility = vis)
 
     /** How many ticks to keep the rotation before resetting */
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index e2dd93502..b3216f2ec 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -91,7 +91,7 @@ import kotlin.math.pow
 object BuildSimulator {
     fun Blueprint.simulate(
         eye: Vec3d,
-        interact: InteractionConfig = TaskFlowModule.interact,
+        interactionConfig: InteractionConfig = TaskFlowModule.interaction,
         rotation: RotationConfig = TaskFlowModule.rotation,
         inventory: InventoryConfig = TaskFlowModule.inventory,
         build: BuildConfig = TaskFlowModule.build,
@@ -102,15 +102,15 @@ object BuildSimulator {
                 if (it.isEmpty()) return@let
                 return@flatMap it
             }
-            checkPostProcessResults(pos, eye, preProcessing, target, interact, build.placing, rotation, inventory).let {
+            checkPostProcessResults(pos, eye, preProcessing, target, interactionConfig, build.placing, rotation, inventory).let {
                 if (it.isEmpty()) return@let
                 return@flatMap it
             }
-            checkPlaceResults(pos, eye, preProcessing, target, build.placing, interact, rotation, inventory).let {
+            checkPlaceResults(pos, eye, preProcessing, target, build.placing, interactionConfig, rotation, inventory).let {
                 if (it.isEmpty()) return@let
                 return@flatMap it
             }
-            checkBreakResults(pos, eye, preProcessing, build.breaking, interact, rotation, inventory, build).let {
+            checkBreakResults(pos, eye, preProcessing, build.breaking, interactionConfig, rotation, inventory, build).let {
                 if (it.isEmpty()) return@let
                 return@flatMap it
             }
@@ -178,7 +178,7 @@ object BuildSimulator {
         eye: Vec3d,
         preProcessing: PreProcessingInfo,
         targetState: TargetState,
-        interact: InteractionConfig,
+        interactionConfig: InteractionConfig,
         place: PlaceConfig,
         rotation: RotationConfig,
         inventory: InventoryConfig
@@ -199,7 +199,7 @@ object BuildSimulator {
             val airPlace = placing && place.airPlace.isEnabled()
 
             boxes.forEach { box ->
-                val refinedSides = if (interact.checkSideVisibility) {
+                val refinedSides = if (interactionConfig.checkSideVisibility) {
                     box.getVisibleSurfaces(eye).let { visibleSides ->
                         sides?.let { specific ->
                             visibleSides.intersect(specific)
@@ -207,34 +207,34 @@ object BuildSimulator {
                     }
                 } else sides ?: Direction.entries.toSet()
 
-                scanSurfaces(box, refinedSides, interact.resolution, preProcessing.surfaceScan) { hitSide, vec ->
+                scanSurfaces(box, refinedSides, interactionConfig.resolution, preProcessing.surfaceScan) { hitSide, vec ->
                     val distSquared = eye distSq vec
-                    if (distSquared > interact.interactReach.pow(2)) {
+                    if (distSquared > interactionConfig.interactReach.pow(2)) {
                         misses.add(vec)
                         return@scanSurfaces
                     }
 
                     val newRotation = eye.rotationTo(vec)
 
-                    val hit = if (interact.strictRayCast) {
-                        val rayCast = newRotation.rayCast(interact.interactReach, eye)
+                    val hit = if (interactionConfig.strictRayCast) {
+                        val rayCast = newRotation.rayCast(interactionConfig.interactReach, eye)
                         when {
                             rayCast != null && (!airPlace || eye distSq rayCast.pos <= distSquared) ->
                                 rayCast.blockResult
 
                             airPlace -> {
-                                val hitVec = newRotation.castBox(box, interact.interactReach, eye)
+                                val hitVec = newRotation.castBox(box, interactionConfig.interactReach, eye)
                                 BlockHitResult(hitVec, hitSide, pos, false)
                             }
 
                             else -> null
                         }
                     } else {
-                        val hitVec = newRotation.castBox(box, interact.interactReach, eye)
+                        val hitVec = newRotation.castBox(box, interactionConfig.interactReach, eye)
                         BlockHitResult(hitVec, hitSide, pos, false)
                     } ?: return@scanSurfaces
 
-                    val checked = CheckedHit(hit, newRotation, interact.interactReach)
+                    val checked = CheckedHit(hit, newRotation, interactionConfig.interactReach)
                     if (hit.blockResult?.blockPos != pos) {
                         blockedHits.add(vec)
                         return@scanSurfaces
@@ -254,7 +254,7 @@ object BuildSimulator {
                 return@interactBlock
             }
 
-            interact.pointSelection.select(validHits)?.let { checkedHit ->
+            interactionConfig.pointSelection.select(validHits)?.let { checkedHit ->
                 val checkedResult = checkedHit.hit
                 val rotationTarget = lookAt(checkedHit.targetRotation, 0.001)
                 val context = InteractionContext(
@@ -309,7 +309,7 @@ object BuildSimulator {
                 }
                 Properties.SLAB_TYPE -> {
                     if (targetState.blockState.get(Properties.SLAB_TYPE) != SlabType.DOUBLE) return@forEach
-                    checkPlaceResults(pos, eye, preProcessing, targetState, place, interact, rotation, inventory).let { placeResults ->
+                    checkPlaceResults(pos, eye, preProcessing, targetState, place, interactionConfig, rotation, inventory).let { placeResults ->
                         acc.addAll(placeResults)
                     }
                 }
@@ -329,7 +329,7 @@ object BuildSimulator {
         preProcessing: PreProcessingInfo,
         targetState: TargetState,
         place: PlaceConfig,
-        interact: InteractionConfig,
+        interactionConfig: InteractionConfig,
         rotation: RotationConfig,
         inventory: InventoryConfig
     ): Set {
@@ -358,14 +358,14 @@ object BuildSimulator {
 
             val validHits = mutableListOf()
             val misses = mutableSetOf()
-            val reachSq = interact.interactReach.pow(2)
+            val reachSq = interactionConfig.interactReach.pow(2)
 
             boxes.forEach { box ->
-                val sides = if (interact.checkSideVisibility) {
+                val sides = if (interactionConfig.checkSideVisibility) {
                     box.getVisibleSurfaces(eye).intersect(setOf(hitSide))
                 } else setOf(hitSide)
 
-                scanSurfaces(box, sides, interact.resolution, preProcessing.surfaceScan) { _, vec ->
+                scanSurfaces(box, sides, interactionConfig.resolution, preProcessing.surfaceScan) { _, vec ->
                     val distSquared = eye distSq vec
                     if (distSquared > reachSq) {
                         misses.add(vec)
@@ -374,25 +374,25 @@ object BuildSimulator {
 
                     val newRotation = eye.rotationTo(vec)
 
-                    val hit = if (interact.strictRayCast) {
-                        val rayCast = newRotation.rayCast(interact.interactReach, eye)
+                    val hit = if (interactionConfig.strictRayCast) {
+                        val rayCast = newRotation.rayCast(interactionConfig.interactReach, eye)
                         when {
                             rayCast != null && (!place.airPlace.isEnabled() || eye distSq rayCast.pos <= distSquared) ->
                                 rayCast.blockResult
 
                             place.airPlace.isEnabled() -> {
-                                val hitVec = newRotation.castBox(box, interact.interactReach, eye)
+                                val hitVec = newRotation.castBox(box, interactionConfig.interactReach, eye)
                                 BlockHitResult(hitVec, hitSide, hitPos, false)
                             }
 
                             else -> null
                         }
                     } else {
-                        val hitVec = newRotation.castBox(box, interact.interactReach, eye)
+                        val hitVec = newRotation.castBox(box, interactionConfig.interactReach, eye)
                         BlockHitResult(hitVec, hitSide, hitPos, false)
                     } ?: return@scanSurfaces
 
-                    val checked = CheckedHit(hit, newRotation, interact.interactReach)
+                    val checked = CheckedHit(hit, newRotation, interactionConfig.interactReach)
                     if (!checked.verify()) return@scanSurfaces
 
                     validHits.add(checked)
@@ -409,7 +409,7 @@ object BuildSimulator {
                 return@forEach
             }
 
-            interact.pointSelection.select(validHits)?.let { checkedHit ->
+            interactionConfig.pointSelection.select(validHits)?.let { checkedHit ->
                 val optimalStack = targetState.getStack(world, pos)
 
                 // ToDo: For each hand and sneak or not?
@@ -568,7 +568,7 @@ object BuildSimulator {
         eye: Vec3d,
         preProcessing: PreProcessingInfo,
         breaking: BreakConfig,
-        interact: InteractionConfig,
+        interactionConfig: InteractionConfig,
         rotation: RotationConfig,
         inventory: InventoryConfig,
         build: BuildConfig
@@ -595,7 +595,7 @@ object BuildSimulator {
 
         /* liquid needs to be submerged first to be broken */
         if (!state.fluidState.isEmpty && state.isReplaceable) {
-            val submerge = checkPlaceResults(pos, eye, preProcessing, TargetState.Solid, build.placing, interact, rotation, inventory)
+            val submerge = checkPlaceResults(pos, eye, preProcessing, TargetState.Solid, build.placing, interactionConfig, rotation, inventory)
             acc.add(BreakResult.Submerge(pos, state, submerge))
             acc.addAll(submerge)
             return acc
@@ -610,9 +610,9 @@ object BuildSimulator {
             acc.add(BreakResult.BlockedByLiquid(pos, state))
             adjacentLiquids.forEach { liquidPos ->
                 val submerge = if (blockState(liquidPos).isReplaceable) {
-                    checkPlaceResults(liquidPos, eye, preProcessing, TargetState.Solid, build.placing, interact, rotation, inventory)
+                    checkPlaceResults(liquidPos, eye, preProcessing, TargetState.Solid, build.placing, interactionConfig, rotation, inventory)
                 } else {
-                    checkBreakResults(liquidPos, eye, preProcessing, breaking, interact, rotation, inventory, build)
+                    checkBreakResults(liquidPos, eye, preProcessing, breaking, interactionConfig, rotation, inventory, build)
                 }
                 acc.addAll(submerge)
             }
@@ -620,7 +620,7 @@ object BuildSimulator {
         }
 
         val currentRotation = RotationManager.activeRotation
-        val currentCast = currentRotation.rayCast(interact.interactReach, eye)
+        val currentCast = currentRotation.rayCast(interactionConfig.interactReach, eye)
 
         val voxelShape = state.getOutlineShape(world, pos)
         voxelShape.getClosestPointTo(eye).ifPresent {
@@ -636,7 +636,7 @@ object BuildSimulator {
         if (boxes.any { it.contains(eye) }) {
             currentCast?.blockResult?.let { blockHit ->
                 val rotationRequest = RotationRequest(
-                    lookAtBlock(pos, config = interact), rotation
+                    lookAtBlock(pos, config = interactionConfig), rotation
                 )
                 val breakContext = BreakContext(
                     blockHit,
@@ -652,11 +652,11 @@ object BuildSimulator {
 
         val validHits = mutableListOf()
         val misses = mutableSetOf()
-        val reachSq = interact.interactReach.pow(2)
+        val reachSq = interactionConfig.interactReach.pow(2)
 
         boxes.forEach { box ->
             // ToDo: Rewrite Rotation request system to allow support for all sim features and use the rotation finder
-            scanSurfaces(box, Direction.entries.toSet(), interact.resolution) { side, vec ->
+            scanSurfaces(box, Direction.entries.toSet(), interactionConfig.resolution) { side, vec ->
                 if (eye distSq vec > reachSq) {
                     misses.add(vec)
                     return@scanSurfaces
@@ -664,14 +664,14 @@ object BuildSimulator {
 
                 val newRotation = eye.rotationTo(vec)
 
-                val hit = if (interact.strictRayCast) {
-                    newRotation.rayCast(interact.interactReach, eye)?.blockResult
+                val hit = if (interactionConfig.strictRayCast) {
+                    newRotation.rayCast(interactionConfig.interactReach, eye)?.blockResult
                 } else {
-                    val hitVec = newRotation.castBox(box, interact.interactReach, eye)
+                    val hitVec = newRotation.castBox(box, interactionConfig.interactReach, eye)
                     BlockHitResult(hitVec, side, pos, false)
                 } ?: return@scanSurfaces
 
-                val checked = CheckedHit(hit, newRotation, interact.interactReach)
+                val checked = CheckedHit(hit, newRotation, interactionConfig.interactReach)
                 if (!checked.verify()) return@scanSurfaces
 
                 validHits.add(checked)
@@ -684,7 +684,7 @@ object BuildSimulator {
             return acc
         }
 
-        val bestHit = interact.pointSelection.select(validHits) ?: return acc
+        val bestHit = interactionConfig.pointSelection.select(validHits) ?: return acc
         val blockHit = bestHit.hit.blockResult ?: return acc
         val target = lookAt(bestHit.targetRotation, 0.001)
         val rotationRequest = RotationRequest(target, rotation)
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt
index b42edf8c0..8a5cbe402 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt
@@ -41,7 +41,7 @@ import java.awt.Color
 
 data class Simulation(
     val blueprint: Blueprint,
-    val interact: InteractionConfig = TaskFlowModule.interact,
+    val interactionConfig: InteractionConfig = TaskFlowModule.interaction,
     val rotation: RotationConfig = TaskFlowModule.rotation,
     val inventory: InventoryConfig = TaskFlowModule.inventory,
     val build: BuildConfig = TaskFlowModule.build,
@@ -63,7 +63,7 @@ data class Simulation(
             if (!playerFitsIn(blockPos)) return@getOrPut emptySet()
         }
 
-        blueprint.simulate(view, interact, rotation, inventory, build)
+        blueprint.simulate(view, interactionConfig, rotation, inventory, build)
     }
 
     fun goodPositions() = cache
@@ -84,7 +84,7 @@ data class Simulation(
         fun Vec3d.playerBox(): Box = Box(x - 0.3, y, z - 0.3, x + 0.3, y + 1.8, z + 0.3).contract(1.0E-6)
 
         fun Blueprint.simulation(
-            interact: InteractionConfig = TaskFlowModule.interact,
+            interact: InteractionConfig = TaskFlowModule.interaction,
             rotation: RotationConfig = TaskFlowModule.rotation,
             inventory: InventoryConfig = TaskFlowModule.inventory,
             build: BuildConfig = TaskFlowModule.build,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt
index 8472a9b4b..56a9d0e6f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt
@@ -17,9 +17,7 @@
 
 package com.lambda.interaction.request
 
-abstract class RequestConfig (
-    val priority: Priority
-) {
+abstract class RequestConfig  {
     protected abstract fun requestInternal(request: R, queueIfClosed: Boolean = true)
 
     fun request(request: R, queueIfClosed: Boolean = true): R =
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index 6461ca975..a23f5f360 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -19,14 +19,11 @@ package com.lambda.interaction.request.breaking
 
 import com.lambda.config.groups.BuildConfig
 import com.lambda.event.Event
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.RequestConfig
 import net.minecraft.block.Block
 import java.awt.Color
 
-abstract class BreakConfig(
-    priority: Priority = 0
-) : RequestConfig(priority) {
+abstract class BreakConfig : RequestConfig() {
     abstract val breakMode: BreakMode
     abstract val reBreak: Boolean
     abstract val unsafeCancels: Boolean
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt
index c987e2e61..44ccb403d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt
@@ -18,7 +18,6 @@
 package com.lambda.interaction.request.hotbar
 
 import com.lambda.event.Event
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.RequestConfig
 
 /**
@@ -26,9 +25,7 @@ import com.lambda.interaction.request.RequestConfig
  *
  * @param priority The priority of this configuration.
  */
-abstract class HotbarConfig(
-    priority: Priority
-) : RequestConfig(priority) {
+abstract class HotbarConfig : RequestConfig() {
 
     /**
      * The number of ticks to keep the current hotbar selection active.
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
index af3a793fd..a5584b0ae 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.request.interacting
 
-import com.lambda.config.groups.InteractionConfig
+import com.lambda.config.groups.InteractConfig
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.InteractionContext
 import com.lambda.interaction.request.ActionInfo
@@ -25,5 +25,5 @@ import com.lambda.interaction.request.ActionInfo
 data class InteractionInfo(
     override val context: InteractionContext,
     override val pendingInteractionsList: MutableCollection,
-    val interact: InteractionConfig
+    val interact: InteractConfig
 ) : ActionInfo
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
index b40b416a3..6315917ef 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
@@ -19,7 +19,7 @@ package com.lambda.interaction.request.interacting
 
 import com.lambda.Lambda.mc
 import com.lambda.config.groups.BuildConfig
-import com.lambda.config.groups.InteractionConfig
+import com.lambda.config.groups.InteractConfig
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.InteractionContext
 import com.lambda.interaction.request.Request
@@ -32,7 +32,7 @@ data class InteractionRequest(
     val contexts: Collection,
     val onInteract: ((BlockPos) -> Unit)?,
     val pendingInteractionsList: MutableCollection,
-    override val config: InteractionConfig,
+    override val config: InteractConfig,
     val build: BuildConfig,
     val hotbar: HotbarConfig,
     val rotation: RotationConfig
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt
index d2b1a56d4..ca6746f11 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt
@@ -21,9 +21,7 @@ import com.lambda.config.groups.BuildConfig
 import com.lambda.event.Event
 import com.lambda.interaction.request.RequestConfig
 
-abstract class PlaceConfig(
-    priority: Int
-) : RequestConfig(priority) {
+abstract class PlaceConfig : RequestConfig() {
     abstract val rotateForPlace: Boolean
     abstract val airPlace: AirPlaceMode
     protected abstract val axisRotateSetting: Boolean
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
index 7891d416b..86cc17743 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
@@ -17,7 +17,6 @@
 
 package com.lambda.interaction.request.rotating
 
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.RequestConfig
 
 /**
@@ -25,7 +24,7 @@ import com.lambda.interaction.request.RequestConfig
  *
  * @param priority The priority of this configuration.
  */
-abstract class RotationConfig(priority: Priority) : RequestConfig(priority) {
+abstract class RotationConfig : RequestConfig() {
     /**
      * - [RotationMode.Silent] Spoofing server-side rotation.
      * - [RotationMode.Sync] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim).
@@ -55,7 +54,7 @@ abstract class RotationConfig(priority: Priority) : RequestConfig Rotation?) =
 @RotationDsl
 fun lookAtHit(
     hit: HitResult,
-    config: InteractionConfig = TaskFlowModule.interact,
+    config: InteractionConfig = TaskFlowModule.interaction,
 ): RotationTarget? {
     return when (hit) {
         is BlockHitResult -> lookAtBlock(hit.blockPos, setOf(hit.side), SurfaceScan.DEFAULT, config)
@@ -105,7 +105,7 @@ fun lookAtHit(
 @RotationDsl
 fun lookAtEntity(
     entity: LivingEntity,
-    config: InteractionConfig = TaskFlowModule.interact
+    config: InteractionConfig = TaskFlowModule.interaction
 ): RotationTarget {
     val requestedHit = entityHit(entity, config.attackReach)
 
@@ -135,7 +135,7 @@ fun lookAtBlock(
     pos: BlockPos,
     sides: Set = ALL_SIDES,
     surfaceScan: SurfaceScan = SurfaceScan.DEFAULT,
-    config: InteractionConfig = TaskFlowModule.interact,
+    config: InteractionConfig = TaskFlowModule.interaction,
 ): RotationTarget {
     val requestedHit = blockHit(pos, sides, config.interactReach)
 
diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt
index 8ac615b68..a6e7ac9ae 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt
@@ -26,5 +26,5 @@ object Baritone : Module(
     description = "Baritone configuration",
     defaultTags = setOf(ModuleTag.CLIENT)
 ) {
-    val rotation = RotationSettings(this, Int.MAX_VALUE)
+    val rotation = RotationSettings(this)
 }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt
index 7f3cc9438..b36118b53 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt
@@ -41,7 +41,7 @@ object TaskFlowModule : Module(
     private val page by setting("Page", Page.Build)
     val build = BuildSettings(this) { page == Page.Build }
     val rotation = RotationSettings(this) { page == Page.Rotation }
-    val interact = InteractionSettings(this, InteractionMask.Both) { page == Page.Interaction }
+    val interaction = InteractionSettings(this, InteractionMask.Both) { page == Page.Interaction }
     val inventory = InventorySettings(this) { page == Page.Inventory }
     val hotbar = HotbarSettings(this) { page == Page.Hotbar }
 
diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
index cc9938f58..28fbb3927 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
@@ -17,6 +17,7 @@
 
 package com.lambda.module.modules.combat
 
+import com.lambda.config.groups.InteractSettings
 import com.lambda.config.groups.InteractionSettings
 import com.lambda.config.groups.RotationSettings
 import com.lambda.config.groups.Targeting
@@ -51,10 +52,11 @@ object KillAura : Module(
     description = "Attacks entities",
     defaultTags = setOf(ModuleTag.COMBAT, ModuleTag.RENDER)
 ) {
-    private val page by setting("Page", Page.Interact)
+    private val page by setting("Page", Page.Interaction)
 
     // Interact
-    private val interactionSettings = InteractionSettings(this, InteractionMask.Entity) { page == Page.Interact }
+    private val interactionSettings = InteractionSettings(this, InteractionMask.Entity) { page == Page.Interaction }
+    private val interactSettings = InteractSettings(this) { page == Page.Interact }
     private val swap by setting("Swap", true, "Swap to the item with the highest damage")
     private val attackMode by setting("Attack Mode", AttackMode.Cooldown) { page == Page.Interact }
     private val cooldownOffset by setting("Cooldown Offset", 0, -5..5, 1) { page == Page.Interact && attackMode == AttackMode.Cooldown }
@@ -83,6 +85,7 @@ object KillAura : Module(
     private var onGroundTicks = 0
 
     enum class Page {
+        Interaction,
         Interact,
         Targeting,
         Aiming
@@ -163,7 +166,7 @@ object KillAura : Module(
 
         // Attack
         interaction.attackEntity(player, target)
-        if (interactionSettings.swingHand) player.swingHand(Hand.MAIN_HAND)
+        if (interactSettings.swingHand) player.swingHand(Hand.MAIN_HAND)
 
         lastAttackTime = System.currentTimeMillis()
         hitDelay = (hitDelay1..hitDelay2).random() * 50
diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt
index a8e7fd462..f5dd5b4b1 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt
@@ -67,7 +67,7 @@ object Speed : Module(
     private val ncpTimerBoost by setting("Timer Boost", 1.08, 1.0..1.1, 0.01) { mode == Mode.NCP_STRAFE }
 
     // Grim
-    private val rotationConfig = RotationConfig.Instant(RotationMode.Sync, Int.MIN_VALUE + 1)
+    private val rotationConfig = RotationConfig.Instant(RotationMode.Sync)
 
     private var prevTickJumping = false
 
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
index 11ad66c84..01d60dbd0 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
@@ -67,7 +67,7 @@ object AntiAim : Module(
     private val yawSpeed by setting("Yaw Speed", 30, 1..90, 1, "Yaw rotation degrees per tick", "°") { page == Page.General && yaw != YawMode.None }
     private val pitchSpeed by setting("Pitch Speed", 30, 1..90, 1, "Pitch rotation degrees per tick", "°") { page == Page.General && pitch != PitchMode.None }
 
-    private val rotation = RotationSettings(this, -1) { page == Page.Rotation }
+    private val rotation = RotationSettings(this) { page == Page.Rotation }
 
     private var currentYaw = 0.0f
     private var currentPitch = 0.0f
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
index 6cd4697ed..565e80745 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
@@ -17,6 +17,7 @@
 
 package com.lambda.module.modules.player
 
+import com.lambda.config.groups.InteractSettings
 import com.lambda.config.groups.InteractionSettings
 import com.lambda.config.groups.RotationSettings
 import com.lambda.context.SafeContext
@@ -87,6 +88,7 @@ object Scaffold : Module(
     private val optimalPitch by setting("Optimal Pitch", 81.0, 70.0..85.0, 0.05) { page == Page.ROTATION }
 
     private val interactionConfig = InteractionSettings(this, InteractionMask.Block) { page == Page.INTERACTION }
+    private val interactConfig = InteractSettings(this) { page == Page.INTERACT }
 
     // Placement
     private var placeInfo: PlaceInfo? = null
@@ -117,6 +119,7 @@ object Scaffold : Module(
     private enum class Page {
         GENERAL,
         ROTATION,
+        INTERACT,
         INTERACTION
     }
 
@@ -223,7 +226,7 @@ object Scaffold : Module(
         // Dividing the surface by segments and iterating through them
         val pointScan = mutableSetOf().apply {
             val box = Box(info.clickPos)
-            val sides = if (TaskFlowModule.interact.checkSideVisibility) {
+            val sides = if (TaskFlowModule.interaction.checkSideVisibility) {
                 box.getVisibleSurfaces(eye)
             } else Direction.entries.toSet()
             scanSurfaces(
@@ -301,7 +304,7 @@ object Scaffold : Module(
         }
 
         // Run placement
-        placeBlock(blockResult ?: return, Hand.MAIN_HAND, interactionConfig.swingHand)
+        placeBlock(blockResult ?: return, Hand.MAIN_HAND, interactConfig.swingHand)
         renderInfo.add(info to currentTime)
     }
 
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index 704544806..b03c92695 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -67,7 +67,7 @@ class BuildTask @Ta5kBuilder constructor(
     private val collectDrops: Boolean = TaskFlowModule.build.collectDrops,
     private val build: BuildConfig = TaskFlowModule.build,
     private val rotation: RotationConfig = TaskFlowModule.rotation,
-    private val interact: InteractionConfig = TaskFlowModule.interact,
+    private val interactionConfig: InteractionConfig = TaskFlowModule.interaction,
     private val inventory: InventoryConfig = TaskFlowModule.inventory,
     private val hotbar: HotbarConfig = TaskFlowModule.hotbar,
 ) : Task() {
@@ -96,7 +96,7 @@ class BuildTask @Ta5kBuilder constructor(
         listen {
             if (collectDrops()) return@listen
 
-            val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build)
+            val results = blueprint.simulate(player.eyePos, interactionConfig, rotation, inventory, build)
 
             TaskFlowModule.drawables = results
                 .filterIsInstance()
@@ -126,7 +126,7 @@ class BuildTask @Ta5kBuilder constructor(
                 is BuildResult.NotVisible,
                 is PlaceResult.NoIntegrity -> {
                     if (!build.pathing) return@listen
-                    val sim = blueprint.simulation(interact, rotation, inventory, build)
+                    val sim = blueprint.simulation(interactionConfig, rotation, inventory, build)
                     val goal = BuildGoal(sim, player.blockPos)
                     BaritoneUtils.setGoalAndPath(goal)
                 }
@@ -157,7 +157,7 @@ class BuildTask @Ta5kBuilder constructor(
                             }
 
                             val request = breakRequest(
-                                requestContexts, pendingInteractions, rotation, hotbar, interact, inventory, build,
+                                requestContexts, pendingInteractions, rotation, hotbar, interactionConfig, inventory, build,
                             ) {
                                 onStop { breaks++ }
                                 onItemDrop?.let { onItemDrop ->
@@ -186,12 +186,12 @@ class BuildTask @Ta5kBuilder constructor(
                                 .take(emptyPendingInteractionSlots)
                                 .map { it.context }
 
-                            interact.request(
+                            build.interacting.request(
                                 InteractionRequest(
                                     interactResults,
                                     null,
                                     pendingInteractions,
-                                    interact,
+                                    build.interacting,
                                     build,
                                     hotbar,
                                     rotation
@@ -256,7 +256,7 @@ class BuildTask @Ta5kBuilder constructor(
             collectDrops: Boolean = TaskFlowModule.build.collectDrops,
             build: BuildConfig = TaskFlowModule.build,
             rotation: RotationConfig = TaskFlowModule.rotation,
-            interact: InteractionConfig = TaskFlowModule.interact,
+            interact: InteractionConfig = TaskFlowModule.interaction,
             inventory: InventoryConfig = TaskFlowModule.inventory,
             blueprint: () -> Blueprint,
         ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory)
@@ -267,7 +267,7 @@ class BuildTask @Ta5kBuilder constructor(
             collectDrops: Boolean = TaskFlowModule.build.collectDrops,
             build: BuildConfig = TaskFlowModule.build,
             rotation: RotationConfig = TaskFlowModule.rotation,
-            interact: InteractionConfig = TaskFlowModule.interact,
+            interact: InteractionConfig = TaskFlowModule.interaction,
             inventory: InventoryConfig = TaskFlowModule.inventory,
         ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory)
 
@@ -277,7 +277,7 @@ class BuildTask @Ta5kBuilder constructor(
             collectDrops: Boolean = TaskFlowModule.build.collectDrops,
             build: BuildConfig = TaskFlowModule.build,
             rotation: RotationConfig = TaskFlowModule.rotation,
-            interact: InteractionConfig = TaskFlowModule.interact,
+            interact: InteractionConfig = TaskFlowModule.interaction,
             inventory: InventoryConfig = TaskFlowModule.inventory,
         ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory)
 
@@ -288,7 +288,7 @@ class BuildTask @Ta5kBuilder constructor(
             collectDrops: Boolean = true,
             build: BuildConfig = TaskFlowModule.build,
             rotation: RotationConfig = TaskFlowModule.rotation,
-            interact: InteractionConfig = TaskFlowModule.interact,
+            interact: InteractionConfig = TaskFlowModule.interaction,
             inventory: InventoryConfig = TaskFlowModule.inventory,
         ) = BuildTask(
             blockPos.toStructure(TargetState.Air).toBlueprint(),
@@ -302,7 +302,7 @@ class BuildTask @Ta5kBuilder constructor(
             collectDrops: Boolean = TaskFlowModule.build.collectDrops,
             build: BuildConfig = TaskFlowModule.build,
             rotation: RotationConfig = TaskFlowModule.rotation,
-            interact: InteractionConfig = TaskFlowModule.interact,
+            interact: InteractionConfig = TaskFlowModule.interaction,
             inventory: InventoryConfig = TaskFlowModule.inventory,
         ) = BuildTask(
             blockPos.toStructure(TargetState.Air).toBlueprint(),
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
index 808756401..d7d9faeda 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
@@ -18,10 +18,10 @@
 package com.lambda.task.tasks
 
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.interaction.request.rotating.visibilty.lookAtBlock
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
@@ -36,7 +36,7 @@ class OpenContainer @Ta5kBuilder constructor(
     private val waitForSlotLoad: Boolean = true,
     private val rotate: Boolean = true,
     private val rotation: RotationConfig = TaskFlowModule.rotation,
-    private val interact: InteractionConfig = TaskFlowModule.interact,
+    private val interact: InteractionConfig = TaskFlowModule.interaction,
     private val sides: Set = Direction.entries.toSet(),
 ) : Task() {
     override val name get() = "${containerState.description(inScope)} at ${blockPos.toShortString()}"
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index 100e58e36..05b1ba40e 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -44,7 +44,7 @@ class PlaceContainer @Ta5kBuilder constructor(
     val stack: ItemStack,
     val build: BuildConfig = TaskFlowModule.build,
     val rotation: RotationConfig = TaskFlowModule.rotation,
-    val interact: InteractionConfig = TaskFlowModule.interact,
+    val interact: InteractionConfig = TaskFlowModule.interaction,
     val inventory: InventoryConfig = TaskFlowModule.inventory,
 ) : Task() {
     private val startStack: ItemStack = stack.copy()

From e0c55e7c1c1da18dc5209b8bb2c41e712c5b79a3 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 16 Jul 2025 23:36:52 +0100
Subject: [PATCH 297/364] start to an inventory manager

---
 .../lambda/config/groups/InventoryConfig.kt   | 25 +++++----
 .../lambda/config/groups/InventorySettings.kt |  2 +-
 .../lambda/event/events/UpdateManagerEvent.kt |  1 +
 .../request/inventory/InventoryManager.kt     | 52 +++++++++++++++++++
 .../request/inventory/InventoryRequest.kt     | 28 ++++++++++
 5 files changed, 98 insertions(+), 10 deletions(-)
 create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
 create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt

diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
index 3c090e088..4e093ef8f 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
@@ -21,18 +21,21 @@ import com.lambda.interaction.material.ContainerSelection
 import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer
 import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.request.RequestConfig
+import com.lambda.interaction.request.inventory.InventoryManager
+import com.lambda.interaction.request.inventory.InventoryRequest
 import net.minecraft.block.Block
 
-interface InventoryConfig {
-    val disposables: Set
-    val swapWithDisposables: Boolean
-    val providerPriority: Priority
-    val storePriority: Priority
+abstract class InventoryConfig : RequestConfig() {
+    abstract val disposables: Set
+    abstract val swapWithDisposables: Boolean
+    abstract val providerPriority: Priority
+    abstract val storePriority: Priority
 
-    val accessShulkerBoxes: Boolean
-    val accessEnderChest: Boolean
-    val accessChests: Boolean
-    val accessStashes: Boolean
+    abstract val accessShulkerBoxes: Boolean
+    abstract val accessEnderChest: Boolean
+    abstract val accessChests: Boolean
+    abstract val accessStashes: Boolean
 
     val containerSelection: ContainerSelection get() = selectContainer {
         val allowedContainers = mutableSetOf().apply {
@@ -45,6 +48,10 @@ interface InventoryConfig {
         ofAnyType(*allowedContainers.toTypedArray())
     }
 
+    override fun requestInternal(request: InventoryRequest, queueIfClosed: Boolean) {
+        InventoryManager.request(request, queueIfClosed)
+    }
+
     enum class Priority {
         WithMinItems,
         WithMaxItems;
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
index de5363dad..493df97b5 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
@@ -23,7 +23,7 @@ import com.lambda.util.item.ItemUtils
 class InventorySettings(
     c: Configurable,
     vis: () -> Boolean = { true },
-) : InventoryConfig {
+) : InventoryConfig() {
     val page by c.setting("Inventory Page", Page.Container, "The page to open when the module is enabled", vis)
 
     override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container}
diff --git a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt
index 9d1771cdf..a16b8ffcf 100644
--- a/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt
+++ b/common/src/main/kotlin/com/lambda/event/events/UpdateManagerEvent.kt
@@ -21,6 +21,7 @@ import com.lambda.event.Event
 
 sealed class UpdateManagerEvent {
     data object Rotation : Event
+    data object Inventory: Event
     data object Hotbar : Event
     data object Break : Event
     data object Place : Event
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
new file mode 100644
index 000000000..732660585
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.interaction.request.inventory
+
+import com.lambda.context.SafeContext
+import com.lambda.event.EventFlow.post
+import com.lambda.event.events.TickEvent
+import com.lambda.event.events.UpdateManagerEvent
+
+import com.lambda.interaction.request.RequestHandler
+import com.lambda.interaction.request.inventory.InventoryManager.activeRequest
+import com.lambda.interaction.request.inventory.InventoryManager.processRequest
+
+object InventoryManager : RequestHandler(
+    1,
+    TickEvent.Pre,
+    TickEvent.Input.Pre,
+    TickEvent.Input.Post,
+    TickEvent.Player.Post,
+    onOpen = { activeRequest?.let { processRequest(it) } }
+) {
+    var activeRequest: InventoryRequest? = null
+
+    override fun load(): String {
+        return "Loaded Inventory Manager"
+    }
+
+    override fun SafeContext.handleRequest(request: InventoryRequest) {
+        TODO("Not yet implemented")
+    }
+
+    fun SafeContext.processRequest(request: InventoryRequest) {
+
+    }
+
+    override fun preEvent() = UpdateManagerEvent.Inventory.post()
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
new file mode 100644
index 000000000..39a83fd00
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.interaction.request.inventory
+
+import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.Request
+
+class InventoryRequest(
+    override val config: InventoryConfig
+) : Request() {
+    override val done: Boolean
+        get() = TODO("Not yet implemented")
+}
\ No newline at end of file

From bb99955b828935ad7019c2eda2c900581e004a22 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 18 Jul 2025 03:30:40 +0100
Subject: [PATCH 298/364] instant client sided changes for swapping hand stacks

---
 .../transfer/transaction/SwapHandsTransaction.kt      | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/SwapHandsTransaction.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/SwapHandsTransaction.kt
index 13d34b2f5..9c56873ac 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/SwapHandsTransaction.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/SwapHandsTransaction.kt
@@ -17,17 +17,16 @@
 
 package com.lambda.interaction.material.transfer.transaction
 
-import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.material.transfer.InventoryTransaction
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
+import net.minecraft.util.Hand
 import net.minecraft.util.math.BlockPos
 import net.minecraft.util.math.Direction
 
 class SwapHandsTransaction @Ta5kBuilder constructor() : InventoryTransaction() {
     override val name: String get() = "Swap Hand Stacks"
-    private var confirming = false
 
     init {
         listen {
@@ -35,6 +34,9 @@ class SwapHandsTransaction @Ta5kBuilder constructor() : InventoryTransaction() {
                 failure("Spectators cannot swap hands")
                 return@listen
             }
+            val offhandStack = player.getStackInHand(Hand.OFF_HAND)
+            player.setStackInHand(Hand.OFF_HAND, player.getStackInHand(Hand.MAIN_HAND))
+            player.setStackInHand(Hand.MAIN_HAND, offhandStack)
             connection.sendPacket(
                 PlayerActionC2SPacket(
                     PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND,
@@ -42,11 +44,6 @@ class SwapHandsTransaction @Ta5kBuilder constructor() : InventoryTransaction() {
                     Direction.DOWN
                 )
             )
-            confirming = true
-        }
-
-        listen {
-            if (it.slot != player.inventory.selectedSlot) return@listen
             finish()
         }
     }

From ff6b87bff079ea494da344edd6f1022a84b4bb69 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 18 Jul 2025 14:53:57 +0100
Subject: [PATCH 299/364] better avoidLiquids logic

---
 .../construction/result/BreakResult.kt        |  4 +-
 .../interaction/construction/result/Rank.kt   |  2 +-
 .../construction/simulation/BuildSimulator.kt | 85 ++++++++++++++++---
 3 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
index b44735a84..92dbedd9a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
@@ -140,11 +140,11 @@ sealed class BreakResult : BuildResult() {
     /**
      * The block is blocked by another liquid block that first has to be submerged.
      */
-    data class BlockedByLiquid(
+    data class BlockedByFluid(
         override val blockPos: BlockPos,
         val blockState: BlockState,
     ) : Drawable, BreakResult() {
-        override val rank = Rank.BREAK_IS_BLOCKED_BY_LIQUID
+        override val rank = Rank.BREAK_IS_BLOCKED_BY_FLUID
         private val color = Color(50, 12, 112, 100)
 
         override fun SafeContext.buildRenderer() {
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt
index 2bf302366..d4c7ec210 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt
@@ -38,7 +38,7 @@ enum class Rank {
     BREAK_RESTRICTED,
     PLACE_NO_INTEGRITY,
     BREAK_SUBMERGE,
-    BREAK_IS_BLOCKED_BY_LIQUID,
+    BREAK_IS_BLOCKED_BY_FLUID,
     UNBREAKABLE,
     BREAK_NO_PERMISSION,
     PLACE_SCAFFOLD_EXCEEDED,
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index b3216f2ec..d3a1904d1 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -68,11 +68,16 @@ import com.lambda.util.player.copyPlayer
 import com.lambda.util.player.gamemode
 import com.lambda.util.world.raycast.RayCastUtils.blockResult
 import net.minecraft.block.BlockState
+import net.minecraft.block.FallingBlock
 import net.minecraft.block.OperatorBlock
 import net.minecraft.block.SlabBlock
+import net.minecraft.block.Waterloggable
 import net.minecraft.block.enums.SlabType
 import net.minecraft.block.pattern.CachedBlockPosition
 import net.minecraft.enchantment.Enchantments
+import net.minecraft.fluid.FlowableFluid
+import net.minecraft.fluid.LavaFluid
+import net.minecraft.fluid.WaterFluid
 import net.minecraft.item.BlockItem
 import net.minecraft.item.Item
 import net.minecraft.item.ItemPlacementContext
@@ -601,22 +606,76 @@ object BuildSimulator {
             return acc
         }
 
-        val adjacentLiquids = Direction.entries.filter {
-            it != Direction.DOWN && !blockState(pos.offset(it)).fluidState.isEmpty
-        }.map { pos.offset(it) }
+        if (breaking.avoidLiquids) {
+            val affectedBlocks = hashSetOf(pos)
+            val checkQueue = hashSetOf(pos)
 
-        /* block has liquids next to it that will leak when broken */
-        if (adjacentLiquids.isNotEmpty() && breaking.avoidLiquids) {
-            acc.add(BreakResult.BlockedByLiquid(pos, state))
-            adjacentLiquids.forEach { liquidPos ->
-                val submerge = if (blockState(liquidPos).isReplaceable) {
-                    checkPlaceResults(liquidPos, eye, preProcessing, TargetState.Solid, build.placing, interactionConfig, rotation, inventory)
-                } else {
-                    checkBreakResults(liquidPos, eye, preProcessing, breaking, interactionConfig, rotation, inventory, build)
+            while (checkQueue.isNotEmpty()) {
+                val checkPos = checkQueue.first()
+                checkQueue.remove(checkPos)
+                for (offset in Direction.entries) {
+                    val adjacentPos = checkPos.offset(offset)
+
+                    if (blockState(adjacentPos).block !is FallingBlock) continue
+                    if (adjacentPos in affectedBlocks) continue
+
+                    if (offset == Direction.UP || FallingBlock.canFallThrough(blockState(adjacentPos.down()))) {
+                        checkQueue.add(adjacentPos)
+                        affectedBlocks.add(adjacentPos)
+                    }
                 }
-                acc.addAll(submerge)
             }
-            return acc
+
+            val affectedFluids = affectedBlocks.fold(hashMapOf()) { accumulator, affectedPos ->
+                Direction.entries.forEach { offset ->
+                    if (offset == Direction.DOWN) return@forEach
+
+                    val offsetPos = affectedPos.offset(offset)
+                    val offsetState = blockState(offsetPos)
+                    val fluidState = offsetState.fluidState
+                    val fluid = fluidState.fluid
+
+                    if (fluidState.isEmpty || fluid !is FlowableFluid) return@forEach
+
+                    if (offset == Direction.UP) {
+                        accumulator.put(offsetPos, offsetState)
+                        return@fold accumulator
+                    }
+
+                    if (offsetState.block is Waterloggable && !fluidState.isEmpty) {
+                        accumulator.put(offsetPos, offsetState)
+                        return@fold accumulator
+                    }
+
+                    val levelDecreasePerBlock =
+                        when (fluid) {
+                            is WaterFluid -> fluid.getLevelDecreasePerBlock(world)
+                            is LavaFluid -> fluid.getLevelDecreasePerBlock(world)
+                            else -> 0
+                        }
+
+                    if (fluidState.level - levelDecreasePerBlock > 0) {
+                        accumulator.put(offsetPos, offsetState)
+                        return@fold accumulator
+                    }
+                }
+
+                return@fold accumulator
+            }
+
+            /* block has liquids next to it that will leak when broken */
+            if (affectedFluids.isNotEmpty()) {
+                acc.add(BreakResult.BlockedByFluid(pos, state))
+                affectedFluids.entries.forEach { fluid  ->
+                    val submerge = if (fluid.value.isReplaceable) {
+                        checkPlaceResults(fluid.key, eye, preProcessing, TargetState.Solid, build.placing, interactionConfig, rotation, inventory)
+                    } else {
+                        checkBreakResults(fluid.key, eye, preProcessing, breaking, interactionConfig, rotation, inventory, build)
+                    }
+                    acc.addAll(submerge)
+                }
+                return acc
+            }
         }
 
         val currentRotation = RotationManager.activeRotation

From de62f07f858bab4caa4bbf65aa299494988a8845 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 18 Jul 2025 15:16:59 +0100
Subject: [PATCH 300/364] fixed disposables logic. Was using >= 0 rather than >
 0

---
 .../lambda/config/groups/InventorySettings.kt   |  4 ++--
 .../construction/simulation/BuildSimulator.kt   |  4 ++--
 .../construction/verify/StateMatcher.kt         |  3 ++-
 .../construction/verify/TargetState.kt          | 17 +++++++++--------
 .../material/container/ContainerManager.kt      |  2 +-
 .../kotlin/com/lambda/util/item/ItemUtils.kt    |  1 +
 6 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
index 493df97b5..a5fdb478a 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
@@ -28,8 +28,8 @@ class InventorySettings(
 
     override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container}
     override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones") { vis() && page == Page.Container}
-    override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container}
-    override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container}
+    override val providerPriority by c.setting("Provider Priority", Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container}
+    override val storePriority by c.setting("Store Priority", Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container}
 
     override val accessShulkerBoxes by c.setting("Access Shulker Boxes", true, "Allow access to the player's shulker boxes") { vis() && page == Page.Access}
     override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest") { vis() && page == Page.Access}
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index d3a1904d1..8a6c23b5f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -415,7 +415,7 @@ object BuildSimulator {
             }
 
             interactionConfig.pointSelection.select(validHits)?.let { checkedHit ->
-                val optimalStack = targetState.getStack(world, pos)
+                val optimalStack = targetState.getStack(world, pos, inventory)
 
                 // ToDo: For each hand and sneak or not?
                 val fakePlayer = copyPlayer(player).apply {
@@ -655,7 +655,7 @@ object BuildSimulator {
                         }
 
                     if (fluidState.level - levelDecreasePerBlock > 0) {
-                        accumulator.put(offsetPos, offsetState)
+                        accumulator[offsetPos] = offsetState
                         return@fold accumulator
                     }
                 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
index 5b0105cf6..ef3c2ac81 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
@@ -17,6 +17,7 @@
 
 package com.lambda.interaction.construction.verify
 
+import com.lambda.config.groups.InventoryConfig
 import net.minecraft.block.BlockState
 import net.minecraft.client.world.ClientWorld
 import net.minecraft.item.ItemStack
@@ -25,6 +26,6 @@ import net.minecraft.util.math.BlockPos
 
 interface StateMatcher {
     fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection> = emptySet()): Boolean
-    fun getStack(world: ClientWorld, pos: BlockPos): ItemStack
+    fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack
     fun isEmpty(): Boolean
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
index d3b1570eb..7c7f2c4de 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
@@ -17,6 +17,7 @@
 
 package com.lambda.interaction.construction.verify
 
+import com.lambda.config.groups.InventoryConfig
 import com.lambda.interaction.material.container.ContainerManager.findDisposable
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.isEmpty
@@ -43,7 +44,7 @@ sealed class TargetState(val type: Type) : StateMatcher {
         override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) =
             state.isEmpty
 
-        override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack =
             ItemStack.EMPTY
 
         override fun isEmpty() = true
@@ -55,7 +56,7 @@ sealed class TargetState(val type: Type) : StateMatcher {
         override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) =
             state.isAir
 
-        override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack =
             ItemStack.EMPTY
 
         override fun isEmpty() = true
@@ -67,8 +68,8 @@ sealed class TargetState(val type: Type) : StateMatcher {
         override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) =
             state.isSolidBlock(world, pos)
 
-        override fun getStack(world: ClientWorld, pos: BlockPos) =
-            findDisposable()?.stacks?.firstOrNull {
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig) =
+            findDisposable(inventory)?.stacks?.firstOrNull {
                 it.item.block in TaskFlowModule.inventory.disposables
             } ?: ItemStack(Items.NETHERRACK)
 
@@ -82,7 +83,7 @@ sealed class TargetState(val type: Type) : StateMatcher {
             world.getBlockState(pos.offset(direction)).isSolidBlock(world, pos.offset(direction))
                     || state.isSolidBlock(world, pos)
 
-        override fun getStack(world: ClientWorld, pos: BlockPos) =
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig) =
             findDisposable()?.stacks?.firstOrNull {
                 it.item.block in TaskFlowModule.inventory.disposables
             } ?: ItemStack(Items.NETHERRACK)
@@ -96,7 +97,7 @@ sealed class TargetState(val type: Type) : StateMatcher {
         override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) =
             state.matches(blockState, ignoredProperties)
 
-        override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack =
             blockState.block.getPickStack(world, pos, blockState)
 
         override fun isEmpty() = blockState.isEmpty
@@ -108,7 +109,7 @@ sealed class TargetState(val type: Type) : StateMatcher {
         override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) =
             state.block == block
 
-        override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack =
             block.getPickStack(world, pos, block.defaultState)
 
         override fun isEmpty() = block.defaultState.isEmpty
@@ -123,7 +124,7 @@ sealed class TargetState(val type: Type) : StateMatcher {
         override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld, ignoredProperties: Collection>) =
             state.block == block
 
-        override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
+        override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack =
             itemStack
 
         override fun isEmpty() = false
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
index b71bd98c6..bfe38a833 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
@@ -125,7 +125,7 @@ object ContainerManager : Loadable {
             .filter { inventory.containerSelection.matches(it) }
 
     fun findDisposable(inventory: InventoryConfig = TaskFlowModule.inventory) = container().find { container ->
-        inventory.disposables.any { container.materialAvailable(it.item.select()) >= 0 }
+        inventory.disposables.any { container.materialAvailable(it.item.select()) > 0 }
     }
 
     class NoContainerFound(selection: StackSelection) : Exception("No container found matching $selection")
diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt
index e5bb6d2bf..58113c73f 100644
--- a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt
@@ -105,6 +105,7 @@ object ItemUtils {
 
     val defaultDisposables = setOf(
         Blocks.DIRT,
+        Blocks.GRASS_BLOCK,
         Blocks.COBBLESTONE,
         Blocks.GRANITE,
         Blocks.DIORITE,

From e19e5af9ed5e48402968da8f0f10afc5f81af63d Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 18 Jul 2025 20:51:10 +0100
Subject: [PATCH 301/364] fillFluids setting in nuker and better fluid removal
 logic in build sim. Also improved sorting for place contexts with a fluid
 level != 0

---
 .../construction/context/PlaceContext.kt      |  3 ++
 .../construction/simulation/BuildSimulator.kt | 30 +++++++++----------
 .../com/lambda/module/modules/player/Nuker.kt |  6 ++--
 .../main/kotlin/com/lambda/util/BlockUtils.kt |  4 +--
 4 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
index afa32d260..a4adaac99 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
@@ -50,6 +50,9 @@ data class PlaceContext(
         when (other) {
             is PlaceContext -> compareBy {
                 BlockUtils.fluids.indexOf(it.cachedState.fluidState.fluid)
+            }.thenByDescending {
+                if (it.cachedState.fluidState.level != 0) it.blockPos.y
+                else 0
             }.thenByDescending {
                 it.cachedState.fluidState.level
             }.thenBy {
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 8a6c23b5f..3f868de83 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -342,7 +342,15 @@ object BuildSimulator {
         val currentState = blockState(pos)
 
         val statePromoting = currentState.block is SlabBlock && targetState.matches(currentState, pos, world, preProcessing.ignore)
-        if (targetState.isEmpty() || (!currentState.isReplaceable && !statePromoting)) return acc
+        // If the target state is air then the only possible blocks it could place are to remove liquids so we use the Solid TargetState
+        val nextTargetState = if (targetState is TargetState.Air) {
+            TargetState.Solid
+        } else if (targetState.isEmpty()) {
+            // Otherwise if the target state is empty, there's no situation where placement would be required so we can return
+            return acc
+        } else targetState
+        // For example, slabs count as state promoting because you are placing another block to promote the current state to the target state
+        if (!currentState.isReplaceable && !statePromoting) return acc
 
         preProcessing.sides.forEach { neighbor ->
             val hitPos = if (!place.airPlace.isEnabled() && (currentState.isEmpty || statePromoting))
@@ -415,7 +423,7 @@ object BuildSimulator {
             }
 
             interactionConfig.pointSelection.select(validHits)?.let { checkedHit ->
-                val optimalStack = targetState.getStack(world, pos, inventory)
+                val optimalStack = nextTargetState.getStack(world, pos, inventory)
 
                 // ToDo: For each hand and sneak or not?
                 val fakePlayer = copyPlayer(player).apply {
@@ -424,6 +432,7 @@ object BuildSimulator {
 
                 val checkedResult = checkedHit.hit
 
+                // ToDo: Override the stack used for this to account for blocks where replaceability is dependent on the held item
                 val usageContext = ItemUsageContext(
                     fakePlayer,
                     Hand.MAIN_HAND,
@@ -476,8 +485,8 @@ object BuildSimulator {
                     resultState = blockItem.getPlacementState(context)
                         ?: return@placeState PlaceResult.BlockedByEntity(pos)
 
-                    return@placeState if (!targetState.matches(resultState, pos, world, preProcessing.ignore))
-                        PlaceResult.NoIntegrity(pos, resultState, context, (targetState as? TargetState.State)?.blockState)
+                    return@placeState if (!nextTargetState.matches(resultState, pos, world, preProcessing.ignore))
+                        PlaceResult.NoIntegrity(pos, resultState, context, (nextTargetState as? TargetState.State)?.blockState)
                     else null
                 }
 
@@ -638,12 +647,12 @@ object BuildSimulator {
                     if (fluidState.isEmpty || fluid !is FlowableFluid) return@forEach
 
                     if (offset == Direction.UP) {
-                        accumulator.put(offsetPos, offsetState)
+                        accumulator[offsetPos] = offsetState
                         return@fold accumulator
                     }
 
                     if (offsetState.block is Waterloggable && !fluidState.isEmpty) {
-                        accumulator.put(offsetPos, offsetState)
+                        accumulator[offsetPos] = offsetState
                         return@fold accumulator
                     }
 
@@ -663,17 +672,8 @@ object BuildSimulator {
                 return@fold accumulator
             }
 
-            /* block has liquids next to it that will leak when broken */
             if (affectedFluids.isNotEmpty()) {
                 acc.add(BreakResult.BlockedByFluid(pos, state))
-                affectedFluids.entries.forEach { fluid  ->
-                    val submerge = if (fluid.value.isReplaceable) {
-                        checkPlaceResults(fluid.key, eye, preProcessing, TargetState.Solid, build.placing, interactionConfig, rotation, inventory)
-                    } else {
-                        checkBreakResults(fluid.key, eye, preProcessing, breaking, interactionConfig, rotation, inventory, build)
-                    }
-                    acc.addAll(submerge)
-                }
                 return acc
             }
         }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index 9b7d66f50..9c114853b 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -28,7 +28,6 @@ import com.lambda.task.tasks.BuildTask.Companion.build
 import com.lambda.util.BaritoneUtils
 import com.lambda.util.BlockUtils.blockPos
 import com.lambda.util.BlockUtils.blockState
-import com.lambda.util.BlockUtils.isNotEmpty
 import net.minecraft.util.math.BlockPos
 
 object Nuker : Module(
@@ -39,6 +38,7 @@ object Nuker : Module(
     private val height by setting("Height", 4, 1..8, 1)
     private val width by setting("Width", 4, 1..8, 1)
     private val flatten by setting("Flatten", true)
+    private val fillFluids by setting("Fill Fluids", false, "Removes liquids by filling them in before breaking")
     private val instantOnly by setting("Instant Only", false)
     private val fillFloor by setting("Fill Floor", false)
     private val baritoneSelection by setting("Baritone Selection", false, "Restricts nuker to your baritone selection")
@@ -51,7 +51,7 @@ object Nuker : Module(
                 val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width)
                     .asSequence()
                     .map { it.blockPos }
-                    .filter { blockState(it).isNotEmpty }
+                    .filter { !blockState(it).isAir }
                     .filter { !flatten || it.y >= player.blockPos.y }
                     .filter { !instantOnly || blockState(it).getHardness(world, it) <= TaskFlowModule.build.breaking.breakThreshold }
                     .filter { pos ->
@@ -64,7 +64,7 @@ object Nuker : Module(
                                     && pos.z >= min.z && pos.z <= max.z
                         }
                     }
-                    .associateWith { TargetState.Air }
+                    .associateWith { if (fillFluids) TargetState.Air else TargetState.Empty }
 
                 if (fillFloor) {
                     val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width)
diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
index 1d2930eca..3272911bd 100644
--- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
@@ -230,10 +230,10 @@ object BlockUtils {
     )
 
     val fluids = listOf(
-        Fluids.LAVA,
         Fluids.FLOWING_LAVA,
-        Fluids.WATER,
+        Fluids.LAVA,
         Fluids.FLOWING_WATER,
+        Fluids.WATER,
         Fluids.EMPTY,
     )
 

From 48db54785b0ace58a792c880fcfc264907f17c81 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 19 Jul 2025 01:28:12 +0100
Subject: [PATCH 302/364] fix supporting block checks? And add setting to
 toggle it

---
 .../main/kotlin/com/lambda/config/groups/BreakSettings.kt   | 1 +
 .../interaction/construction/simulation/BuildSimulator.kt   | 6 +-----
 .../com/lambda/interaction/request/breaking/BreakConfig.kt  | 1 +
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 530b86fdf..959b8e309 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -46,6 +46,7 @@ class BreakSettings(
     override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.General }
     override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block") { vis() && page == Page.General }
     override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill") { vis() && page == Page.General }
+    override val avoidSupporting by c.setting("Avoid Supporting", true, "Avoids breaking the block supporting the player") { vis() && page == Page.General }
     override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.General }
     override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.General }
     override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() && page == Page.General }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 3f868de83..9787eb398 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -597,12 +597,8 @@ object BuildSimulator {
         }
 
         /* player is standing on top of the block */
-        val pBox = player.boundingBox
-        val aabb = pBox.withMinY(pBox.minY - 1.0E-6)
-        world.findSupportingBlockPos(player, aabb).orElse(null)?.let { support ->
+        if (breaking.avoidSupporting) player.supportingBlockPos.orElse(null)?.let { support ->
             if (support != pos) return@let
-            val belowSupport = blockState(support.down())
-            if (belowSupport.isSolidSurface(world, support, player, Direction.UP)) return@let
             acc.add(BreakResult.PlayerOnTop(pos, state))
             return acc
         }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index a23f5f360..0e5cf7587 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -44,6 +44,7 @@ abstract class BreakConfig : RequestConfig() {
     abstract val breaksPerTick: Int
     abstract val suitableToolsOnly: Boolean
     abstract val avoidLiquids: Boolean
+    abstract val avoidSupporting: Boolean
     abstract val breakWeakBlocks: Boolean
     abstract val forceSilkTouch: Boolean
     abstract val forceFortunePickaxe: Boolean

From 40022f92b2013a011d368d243be0a545fb252740 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 19 Jul 2025 02:05:57 +0100
Subject: [PATCH 303/364] comment out desyncFix for now as its broken and wont
 be fixed for a while

---
 .../com/lambda/config/groups/BreakSettings.kt |  2 +-
 .../request/breaking/BreakConfig.kt           |  3 +-
 .../request/breaking/BreakManager.kt          | 31 +++++++++----------
 3 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 959b8e309..eee7e9123 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -34,7 +34,7 @@ class BreakSettings(
     override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
     override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() && page == Page.General }
     override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
-    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
+//    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
     override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
     override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() && page == Page.General }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index 0e5cf7587..977f975e9 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -30,7 +30,8 @@ abstract class BreakConfig : RequestConfig() {
     abstract val breakThreshold: Float
     abstract val doubleBreak: Boolean
     abstract val fudgeFactor: Int
-    abstract val desyncFix: Boolean
+    //ToDo: Needs a more advanced player simulation implementation to predict the next ticks onGround / submerged status
+//    abstract val desyncFix: Boolean
     abstract val breakDelay: Int
     abstract val breakStageMask: Set
     abstract val swing: SwingMode
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index d2b9bc184..fdb68f7be 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -66,16 +66,13 @@ import com.lambda.util.Communication.warn
 import com.lambda.util.item.ItemUtils.block
 import com.lambda.util.math.lerp
 import com.lambda.util.player.gamemode
-import com.lambda.util.player.prediction.buildPlayerPrediction
 import com.lambda.util.player.swingHand
 import net.minecraft.block.BlockState
 import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.client.sound.PositionedSoundInstance
 import net.minecraft.client.sound.SoundInstance
-import net.minecraft.enchantment.EnchantmentHelper
 import net.minecraft.entity.ItemEntity
 import net.minecraft.item.ItemStack
-import net.minecraft.registry.tag.FluidTags
 import net.minecraft.sound.SoundCategory
 import net.minecraft.util.Hand
 import net.minecraft.util.math.BlockPos
@@ -793,21 +790,21 @@ object BreakManager : RequestHandler(
         config: BreakConfig,
         item: ItemStack? = null
     ) = runSafe {
-        var delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.mainHandStack)
+        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.mainHandStack)
         //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
-        if (config.desyncFix) {
-            val nextTickPrediction = buildPlayerPrediction().next()
-            if (player.isOnGround && !nextTickPrediction.onGround) {
-                delta /= 5.0f
-            }
-
-            val affectedThisTick = player.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(player)
-            val simulatedPlayer = nextTickPrediction.predictionEntity.player
-            val affectedNextTick = simulatedPlayer.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(simulatedPlayer)
-            if (!affectedThisTick && affectedNextTick) {
-                delta /= 5.0f
-            }
-        }
+//        if (config.desyncFix) {
+//            val nextTickPrediction = buildPlayerPrediction().next()
+//            if (player.isOnGround && !nextTickPrediction.onGround) {
+//                delta /= 5.0f
+//            }
+//
+//            val affectedThisTick = player.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(player)
+//            val simulatedPlayer = nextTickPrediction.predictionEntity.player
+//            val affectedNextTick = simulatedPlayer.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(simulatedPlayer)
+//            if (!affectedThisTick && affectedNextTick) {
+//                delta /= 5.0f
+//            }
+//        }
         delta
     } ?: 0f
 

From 0a6e7dee6b102dd2dca7b07368d25083da70bc0f Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 19 Jul 2025 02:49:11 +0100
Subject: [PATCH 304/364] sorter setting for breaks

---
 .../com/lambda/config/groups/BreakSettings.kt     |  1 +
 .../construction/context/BreakContext.kt          | 15 +++++++++++++--
 .../construction/simulation/BuildSimulator.kt     |  3 ++-
 .../interaction/request/breaking/BreakConfig.kt   |  8 ++++++++
 .../com/lambda/module/modules/player/AntiAim.kt   |  5 ++---
 5 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index eee7e9123..7ef184b6e 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -29,6 +29,7 @@ class BreakSettings(
 ) : BreakConfig() {
     val page by c.setting("Break Page", Page.General, visibility = vis)
     override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
+    override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed") { vis() && page == Page.General }
     override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
     override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
     override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
index e65ca034b..117825108 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
@@ -20,23 +20,27 @@ package com.lambda.interaction.construction.context
 import com.lambda.context.SafeContext
 import com.lambda.graphics.renderer.esp.DirectionMask
 import com.lambda.graphics.renderer.esp.DirectionMask.exclude
+import com.lambda.interaction.request.breaking.BreakConfig
 import com.lambda.interaction.request.breaking.BreakRequest
 import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.hotbar.HotbarRequest
 import com.lambda.interaction.request.rotating.RotationRequest
+import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.emptyState
 import net.minecraft.block.BlockState
 import net.minecraft.block.FallingBlock
 import net.minecraft.util.hit.BlockHitResult
 import net.minecraft.util.math.BlockPos
 import java.awt.Color
+import kotlin.random.Random
 
 data class BreakContext(
     override val result: BlockHitResult,
     override val rotation: RotationRequest,
     override var hotbarIndex: Int,
     override var cachedState: BlockState,
-    var instantBreak: Boolean
+    var instantBreak: Boolean,
+    val sortMode: BreakConfig.SortMode = TaskFlowModule.build.breaking.sorter
 ) : BuildContext() {
     private val baseColor = Color(222, 0, 0, 25)
     private val sideColor = Color(222, 0, 0, 100)
@@ -44,6 +48,8 @@ data class BreakContext(
     override val blockPos: BlockPos = result.blockPos
     override val expectedState: BlockState = cachedState.emptyState
 
+    val random = Random.nextDouble()
+
     override fun compareTo(other: BuildContext): Int {
         return when (other) {
             is BreakContext -> compareByDescending {
@@ -51,7 +57,12 @@ data class BreakContext(
             }.thenBy {
                 it.instantBreak
             }.thenBy {
-                it.rotation.target.angleDistance
+                when (sortMode) {
+                    BreakConfig.SortMode.Closest -> it.distance
+                    BreakConfig.SortMode.Farthest -> -it.distance
+                    BreakConfig.SortMode.Rotation -> it.rotation.target.angleDistance
+                    BreakConfig.SortMode.Random -> it.random
+                }
             }.thenBy {
                 it.hotbarIndex == HotbarManager.serverSlot
             }.compare(this, other)
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 9787eb398..29249e39b 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -698,7 +698,8 @@ object BuildSimulator {
                     rotationRequest,
                     player.inventory.selectedSlot,
                     state,
-                    instantBreakable(state, pos, breaking.breakThreshold)
+                    instantBreakable(state, pos, breaking.breakThreshold),
+                    breaking.sorter
                 )
                 acc.add(BreakResult.Break(pos, breakContext))
                 return acc
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index 977f975e9..eaf256801 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -25,6 +25,7 @@ import java.awt.Color
 
 abstract class BreakConfig : RequestConfig() {
     abstract val breakMode: BreakMode
+    abstract val sorter: SortMode
     abstract val reBreak: Boolean
     abstract val unsafeCancels: Boolean
     abstract val breakThreshold: Float
@@ -77,6 +78,13 @@ abstract class BreakConfig : RequestConfig() {
         Packet
     }
 
+    enum class SortMode {
+        Closest,
+        Farthest,
+        Rotation,
+        Random
+    }
+
     enum class SwingMode {
         Constant,
         StartAndEnd,
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
index 01d60dbd0..eff263baa 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
@@ -72,7 +72,6 @@ object AntiAim : Module(
     private var currentYaw = 0.0f
     private var currentPitch = 0.0f
 
-    private val random = Random(0)
     private var jitterRight = true
     private var jitterUp = true
     private var pitchingUp = true
@@ -90,7 +89,7 @@ object AntiAim : Module(
                     LeftRight.Right -> currentYaw + yawSpeed
                 }
                 YawMode.Jitter -> {
-                    val delta = random.nextFloat() * (yawSpeed)
+                    val delta = Random.nextFloat() * (yawSpeed)
                     if (jitterRight) {
                         jitterRight = false
                         currentYaw + delta
@@ -131,7 +130,7 @@ object AntiAim : Module(
                     }
                 }
                 PitchMode.Jitter -> {
-                    val delta = random.nextFloat() * (pitchSpeed)
+                    val delta = Random.nextFloat() * (pitchSpeed)
                     if (jitterUp) {
                         jitterUp = false
                         currentPitch - delta

From 9483f60148be8ab80c50c096c6229c2a5bfa338a Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 19 Jul 2025 23:01:57 +0100
Subject: [PATCH 305/364] stage checks when cancelling block breaks

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index fdb68f7be..ae1b60184 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -464,7 +464,7 @@ object BreakManager : RequestHandler(
         val breakInfo = BreakInfo(requestCtx, Primary, request)
         primaryBreak?.let { primaryInfo ->
             if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) {
-                if (!primaryInfo.updatedThisTick) {
+                if (!primaryInfo.updatedThisTick && tickStage in primaryInfo.breakConfig.breakStageMask) {
                     primaryInfo.cancelBreak()
                     return@let
                 } else return null
@@ -475,6 +475,8 @@ object BreakManager : RequestHandler(
                 return secondaryBreak
             }
 
+            if (tickStage !in primaryInfo.breakConfig.breakStageMask) return null
+
             primaryInfo.stopBreakPacket(world, interaction)
             primaryInfo.makeSecondary()
             return@let

From 01a095f7facab08019bddfd5b5f2c30e3ab175cb Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 19 Jul 2025 23:34:22 +0100
Subject: [PATCH 306/364] removed abort break packet usage entirely as it
 doesnt really do anything and is only causing tick stage complications

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt  | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index ae1b60184..e4f5b4f15 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -464,7 +464,7 @@ object BreakManager : RequestHandler(
         val breakInfo = BreakInfo(requestCtx, Primary, request)
         primaryBreak?.let { primaryInfo ->
             if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) {
-                if (!primaryInfo.updatedThisTick && tickStage in primaryInfo.breakConfig.breakStageMask) {
+                if (!primaryInfo.updatedThisTick) {
                     primaryInfo.cancelBreak()
                     return@let
                 } else return null
@@ -475,8 +475,6 @@ object BreakManager : RequestHandler(
                 return secondaryBreak
             }
 
-            if (tickStage !in primaryInfo.breakConfig.breakStageMask) return null
-
             primaryInfo.stopBreakPacket(world, interaction)
             primaryInfo.makeSecondary()
             return@let
@@ -560,7 +558,6 @@ object BreakManager : RequestHandler(
         runSafe {
             if (isRedundant || abandoned) return@runSafe
             if (isPrimary) {
-                if (breaking) abortBreakPacket(world, interaction)
                 nullify()
                 setBreakingTextureStage(player, world, -1)
                 request.onCancel?.invoke(context.blockPos)

From b5bc2b4d75081b4be9c8db451c49b8d974b0c9f2 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 23 Jul 2025 15:31:15 +0100
Subject: [PATCH 307/364] RequestConfig refactor and submitting from the
 request itself rather than the configs as theyre not coupled outside the
 request anymore

---
 .../com/lambda/config/groups/BreakSettings.kt |  7 +-
 .../lambda/config/groups/HotbarSettings.kt    |  2 +-
 .../lambda/config/groups/InteractSettings.kt  |  3 +-
 .../lambda/config/groups/InteractionConfig.kt | 23 ++---
 .../config/groups/InteractionSettings.kt      |  4 +-
 .../lambda/config/groups/InventorySettings.kt |  6 +-
 .../com/lambda/config/groups/PlaceSettings.kt |  4 +-
 .../lambda/config/groups/RotationSettings.kt  |  4 +-
 .../construction/context/BreakContext.kt      | 12 +--
 .../context/InteractionContext.kt             | 10 +--
 .../construction/context/PlaceContext.kt      |  6 +-
 .../construction/result/BreakResult.kt        |  2 +-
 .../construction/result/BuildResult.kt        |  2 +-
 .../construction/simulation/BuildSimulator.kt |  2 +-
 .../construction/simulation/Simulation.kt     |  2 +-
 .../construction/verify/StateMatcher.kt       |  2 +-
 .../construction/verify/TargetState.kt        |  2 +-
 .../material/container/ContainerManager.kt    |  2 +-
 .../material/container/MaterialContainer.kt   |  2 +-
 .../material/transfer/SlotTransfer.kt         |  2 +-
 .../com/lambda/interaction/request/Request.kt |  4 +-
 .../interaction/request/RequestConfig.kt      |  7 +-
 .../interaction/request/RequestHandler.kt     |  2 +-
 .../request/breaking/BreakConfig.kt           | 84 +++++++++----------
 .../request/breaking/BreakManager.kt          | 15 +++-
 .../request/breaking/BreakRequest.kt          | 11 ++-
 .../request/hotbar/HotbarConfig.kt            | 23 ++---
 .../request/hotbar/HotbarManager.kt           | 13 ++-
 .../request/hotbar/HotbarRequest.kt           |  9 +-
 .../request/interacting}/InteractConfig.kt    | 21 ++---
 ...teractionRequest.kt => InteractRequest.kt} |  8 +-
 .../interacting/InteractedBlockHandler.kt     |  4 +-
 .../request/interacting/InteractionInfo.kt    |  5 +-
 .../request/interacting/InteractionManager.kt | 21 +++--
 .../request/inventory}/InventoryConfig.kt     | 48 +++++------
 .../request/inventory/InventoryRequest.kt     |  6 +-
 .../request/placing/PlaceConfig.kt            | 26 +++---
 .../request/placing/PlaceManager.kt           | 10 ++-
 .../request/placing/PlaceRequest.kt           |  5 +-
 .../request/rotating/RotationConfig.kt        | 22 ++---
 .../request/rotating/RotationManager.kt       | 18 ++--
 .../request/rotating/RotationRequest.kt       | 23 +++--
 .../rotating/visibilty/RotationTarget.kt      |  4 +-
 .../lambda/module/modules/debug/SilentSwap.kt |  2 +-
 .../lambda/module/modules/player/AntiAim.kt   |  3 +-
 .../module/modules/player/PacketMine.kt       |  2 +-
 .../com/lambda/task/tasks/AcquireMaterial.kt  |  2 +-
 .../kotlin/com/lambda/task/tasks/BuildTask.kt | 24 ++----
 .../com/lambda/task/tasks/PlaceContainer.kt   |  4 +-
 49 files changed, 249 insertions(+), 276 deletions(-)
 rename common/src/main/kotlin/com/lambda/{config/groups => interaction/request/interacting}/InteractConfig.kt (52%)
 rename common/src/main/kotlin/com/lambda/interaction/request/interacting/{InteractionRequest.kt => InteractRequest.kt} (90%)
 rename common/src/main/kotlin/com/lambda/{config/groups => interaction/request/inventory}/InventoryConfig.kt (58%)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 7ef184b6e..039f0716e 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -20,13 +20,18 @@ package com.lambda.config.groups
 import com.lambda.config.Configurable
 import com.lambda.event.events.TickEvent
 import com.lambda.interaction.request.breaking.BreakConfig
+import com.lambda.interaction.request.breaking.BreakConfig.AnimationMode
+import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode
+import com.lambda.interaction.request.breaking.BreakConfig.BreakMode
+import com.lambda.interaction.request.breaking.BreakConfig.SortMode
+import com.lambda.interaction.request.breaking.BreakConfig.SwingMode
 import com.lambda.util.BlockUtils.allSigns
 import java.awt.Color
 
 class BreakSettings(
     c: Configurable,
     vis: () -> Boolean = { true }
-) : BreakConfig() {
+) : BreakConfig {
     val page by c.setting("Break Page", Page.General, visibility = vis)
     override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
     override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed") { vis() && page == Page.General }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
index 8c866e266..a65855985 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
@@ -24,7 +24,7 @@ import com.lambda.interaction.request.hotbar.HotbarConfig
 class HotbarSettings(
     c: Configurable,
     vis: () -> Boolean = { true }
-) : HotbarConfig() {
+) : HotbarConfig {
     override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", visibility = vis)
     override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", visibility = vis)
     override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
index ecb6b3500..a8a7e3ed1 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
@@ -18,11 +18,12 @@
 package com.lambda.config.groups
 
 import com.lambda.config.Configurable
+import com.lambda.interaction.request.interacting.InteractConfig
 
 class InteractSettings(
     c: Configurable,
     vis: () -> Boolean =  { true }
-) : InteractConfig() {
+) : InteractConfig {
     override val rotate by c.setting("Rotate For Interact", true, "Rotates the player to look at the block when interacting", visibility = vis)
     override val swingHand by c.setting("Swing On Interact", true, "Swings the players hand after interacting", visibility = vis)
     override val interactSwingType by c.setting("Interact Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swingHand }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
index 3817e4d14..211beee5a 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt
@@ -17,54 +17,47 @@
 
 package com.lambda.config.groups
 
-import com.lambda.interaction.request.RequestConfig
-import com.lambda.interaction.request.interacting.InteractionManager
-import com.lambda.interaction.request.interacting.InteractionRequest
 import com.lambda.interaction.request.rotating.visibilty.PointSelection
 
-abstract class InteractionConfig : RequestConfig() {
+interface InteractionConfig {
     /**
      * Maximum entity interaction distance
      */
-    abstract val attackReach: Double
+    val attackReach: Double
 
     /**
      * Maximum block interaction distance
      */
-    abstract val interactReach: Double
+    val interactReach: Double
 
     /**
      * Maximum possible interaction distance
      *
      * Equals to `max(attackReach, placeReach)` if both are present. Equals to one of them otherwise
      */
-    abstract val scanReach: Double
+    val scanReach: Double
 
     /**
      * Whether to include the environment to the ray cast context.
      *
      * if false: skips walls for entities, skips entities for blocks.
      */
-    abstract val strictRayCast: Boolean
+    val strictRayCast: Boolean
 
     /**
      * Whether to check if an AABB side is visible.
      */
-    abstract val checkSideVisibility: Boolean
+    val checkSideVisibility: Boolean
 
     /**
      * Grid divisions count per surface of the hit box.
      */
-    abstract val resolution: Int
+    val resolution: Int
 
     /**
      * The way to select the best point.
      */
-    abstract val pointSelection: PointSelection
-
-    override fun requestInternal(request: InteractionRequest, queueIfClosed: Boolean) {
-        InteractionManager.request(request, queueIfClosed)
-    }
+    val pointSelection: PointSelection
 
     enum class InteractConfirmationMode {
         None,
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
index 1dae68bf8..399a95fca 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt
@@ -26,7 +26,7 @@ class InteractionSettings(
     c: Configurable,
     private val usage: InteractionMask,
     vis: () -> Boolean = { true },
-) : InteractionConfig() {
+) : InteractionConfig {
     // Reach
     private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", visibility = vis)
     private val attackReachSetting = if (usage.entity) c.setting("Attack Reach", DEFAULT_ATTACK_REACH, 1.0..10.0, 0.01, "Maximum entity interaction distance") { vis() && !useDefaultReach } else null
@@ -40,7 +40,7 @@ class InteractionSettings(
         return if (useDefaultReach) DEFAULT_ATTACK_REACH else attackReachSetting!!.value
     }
 
-    override val interactReach: Double get()  {
+    override val interactReach: Double get() {
         check(usage.block) {
             "Given interaction config has no place reach implementation"
         }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
index a5fdb478a..e7a5bc7de 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
@@ -18,12 +18,14 @@
 package com.lambda.config.groups
 
 import com.lambda.config.Configurable
+import com.lambda.interaction.request.inventory.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig.Priority
 import com.lambda.util.item.ItemUtils
 
 class InventorySettings(
     c: Configurable,
-    vis: () -> Boolean = { true },
-) : InventoryConfig() {
+    vis: () -> Boolean = { true }
+) : InventoryConfig {
     val page by c.setting("Inventory Page", Page.Container, "The page to open when the module is enabled", vis)
 
     override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container}
diff --git a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
index 3f61ad31c..4118f7a24 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
@@ -20,11 +20,13 @@ package com.lambda.config.groups
 import com.lambda.config.Configurable
 import com.lambda.event.events.TickEvent
 import com.lambda.interaction.request.placing.PlaceConfig
+import com.lambda.interaction.request.placing.PlaceConfig.AirPlaceMode
+import com.lambda.interaction.request.placing.PlaceConfig.PlaceConfirmationMode
 
 class PlaceSettings(
     c: Configurable,
     vis: () -> Boolean = { true }
-) : PlaceConfig() {
+) : PlaceConfig {
     override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", visibility = vis)
     override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces", visibility = vis)
     override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt
index f0f88d13d..d3b4f06dc 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt
@@ -30,7 +30,7 @@ import kotlin.random.Random
 class RotationSettings(
     c: Configurable,
     vis: () -> Boolean = { true }
-) : RotationConfig() {
+) : RotationConfig {
     override var rotationMode by c.setting("Mode", RotationMode.Sync, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", visibility = vis)
 
     /** How many ticks to keep the rotation before resetting */
@@ -66,4 +66,4 @@ class RotationSettings(
 
         return sqrt(-2.0 * ln(u1)) * cos(2.0 * PI * u2)
     }
-}
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
index 117825108..0af6dd153 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
@@ -76,8 +76,10 @@ data class BreakContext(
         withState(cachedState, blockPos, sideColor, result.side)
     }
 
-    fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean {
-        val request = HotbarRequest(hotbarIndex, breakRequest.hotbar, breakRequest.hotbar.keepTicks.coerceAtLeast(minKeepTicks))
-        return request.config.request(request, false).done
-    }
-}
+    fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean =
+        HotbarRequest(
+            hotbarIndex,
+            breakRequest.hotbar,
+            breakRequest.hotbar.keepTicks.coerceAtLeast(minKeepTicks)
+        ).submit(false).done
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
index 4db49e42e..b8102d009 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
@@ -22,7 +22,7 @@ import com.lambda.graphics.renderer.esp.DirectionMask
 import com.lambda.graphics.renderer.esp.DirectionMask.exclude
 import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.hotbar.HotbarRequest
-import com.lambda.interaction.request.interacting.InteractionRequest
+import com.lambda.interaction.request.interacting.InteractRequest
 import com.lambda.interaction.request.rotating.RotationRequest
 import com.lambda.util.BlockUtils
 import com.lambda.util.BlockUtils.blockState
@@ -65,11 +65,9 @@ class InteractionContext(
         withState(blockState(result.blockPos), result.blockPos, sideColor, result.side)
     }
 
-    fun requestDependencies(request: InteractionRequest): Boolean {
-        val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false)
-        val validRotation = if (request.config.rotate) {
-            request.rotation.request(rotation, false).done
-        } else true
+    fun requestDependencies(request: InteractRequest): Boolean {
+        val hotbarRequest = HotbarRequest(hotbarIndex, request.hotbar).submit(false)
+        val validRotation = if (request.rotate) rotation.submit(false).done else true
         return hotbarRequest.done && validRotation
     }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
index a4adaac99..ba6b46c3f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
@@ -76,9 +76,9 @@ data class PlaceContext(
     }
 
     fun requestDependencies(request: PlaceRequest): Boolean {
-        val hotbarRequest = request.hotbar.request(HotbarRequest(hotbarIndex, request.hotbar), false)
-        val validRotation = if (request.build.placing.rotateForPlace) {
-            request.rotation.request(rotation, false).done && currentDirIsValid
+        val hotbarRequest = HotbarRequest(hotbarIndex, request.hotbar).submit(false)
+        val validRotation = if (request.rotateForPlace) {
+            rotation.submit(false).done && currentDirIsValid
         } else true
         return hotbarRequest.done && validRotation
     }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
index 92dbedd9a..7e180779a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
@@ -19,7 +19,7 @@ package com.lambda.interaction.construction.result
 
 import baritone.api.pathing.goals.GoalBlock
 import baritone.api.pathing.goals.GoalInverted
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.material.StackSelection.Companion.selectStack
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt
index 2b37e5a34..459e99c3d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt
@@ -19,7 +19,7 @@ package com.lambda.interaction.construction.result
 
 import baritone.api.pathing.goals.GoalBlock
 import baritone.api.pathing.goals.GoalNear
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.material.StackSelection
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 29249e39b..a89365f50 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -19,7 +19,6 @@ package com.lambda.interaction.construction.simulation
 
 import com.lambda.config.groups.BuildConfig
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.blueprint.Blueprint
 import com.lambda.interaction.construction.context.BreakContext
@@ -39,6 +38,7 @@ import com.lambda.interaction.material.StackSelection.Companion.selectStack
 import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial
 import com.lambda.interaction.material.container.MaterialContainer
 import com.lambda.interaction.request.breaking.BreakConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.placing.PlaceConfig
 import com.lambda.interaction.request.rotating.Rotation.Companion.rotation
 import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt
index 8a5cbe402..e2ea6c893 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt
@@ -19,12 +19,12 @@ package com.lambda.interaction.construction.simulation
 
 import com.lambda.config.groups.BuildConfig
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.blueprint.Blueprint
 import com.lambda.interaction.construction.result.BuildResult
 import com.lambda.interaction.construction.result.Drawable
 import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.threading.runSafe
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
index ef3c2ac81..0afb3f976 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.construction.verify
 
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import net.minecraft.block.BlockState
 import net.minecraft.client.world.ClientWorld
 import net.minecraft.item.ItemStack
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
index 7c7f2c4de..5709da5f2 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.construction.verify
 
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.material.container.ContainerManager.findDisposable
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.isEmpty
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
index bfe38a833..92131ebed 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.material.container
 
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.core.Loadable
 import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.PlayerEvent
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
index 1cd96bf33..c72e1162b 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.material.container
 
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt
index 07fab754f..2e42df874 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.material.transfer
 
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
index 4f20ae8be..5482cdfda 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
@@ -18,8 +18,10 @@
 package com.lambda.interaction.request
 
 abstract class Request {
-    abstract val config: RequestConfig<*>
+    abstract val config: RequestConfig
     var fresh = true
 
     abstract val done: Boolean
+
+    abstract fun submit(queueIfClosed: Boolean = true): Request
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt
index 56a9d0e6f..e756864d8 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt
@@ -17,9 +17,4 @@
 
 package com.lambda.interaction.request
 
-abstract class RequestConfig  {
-    protected abstract fun requestInternal(request: R, queueIfClosed: Boolean = true)
-
-    fun request(request: R, queueIfClosed: Boolean = true): R =
-        request.apply { requestInternal(this, queueIfClosed) }
-}
\ No newline at end of file
+interface RequestConfig
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt
index 12477e111..f23c77bba 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt
@@ -99,7 +99,7 @@ abstract class RequestHandler(
      */
     fun request(request: R, queueIfClosed: Boolean = true): R {
         if (!acceptingRequests) {
-            val canOverrideQueued = queuedRequest?.run { config === request.config } ?: true
+            val canOverrideQueued = queuedRequest?.run { config === request.config } != false
             if (queueIfClosed && canOverrideQueued) {
                 queuedRequest = request
             }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index eaf256801..390212d20 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -23,55 +23,51 @@ import com.lambda.interaction.request.RequestConfig
 import net.minecraft.block.Block
 import java.awt.Color
 
-abstract class BreakConfig : RequestConfig() {
-    abstract val breakMode: BreakMode
-    abstract val sorter: SortMode
-    abstract val reBreak: Boolean
-    abstract val unsafeCancels: Boolean
-    abstract val breakThreshold: Float
-    abstract val doubleBreak: Boolean
-    abstract val fudgeFactor: Int
+interface BreakConfig : RequestConfig {
+    val breakMode: BreakMode
+    val sorter: SortMode
+    val reBreak: Boolean
+    val unsafeCancels: Boolean
+    val breakThreshold: Float
+    val doubleBreak: Boolean
+    val fudgeFactor: Int
     //ToDo: Needs a more advanced player simulation implementation to predict the next ticks onGround / submerged status
 //    abstract val desyncFix: Boolean
-    abstract val breakDelay: Int
-    abstract val breakStageMask: Set
-    abstract val swing: SwingMode
-    abstract val swingType: BuildConfig.SwingType
-    abstract val sounds: Boolean
-    abstract val particles: Boolean
-    abstract val breakingTexture: Boolean
-    abstract val rotateForBreak: Boolean
-    abstract val breakConfirmation: BreakConfirmationMode
-    abstract val maxPendingBreaks: Int
-    abstract val breaksPerTick: Int
-    abstract val suitableToolsOnly: Boolean
-    abstract val avoidLiquids: Boolean
-    abstract val avoidSupporting: Boolean
-    abstract val breakWeakBlocks: Boolean
-    abstract val forceSilkTouch: Boolean
-    abstract val forceFortunePickaxe: Boolean
-    abstract val minFortuneLevel: Int
-    abstract val ignoredBlocks: Set
+    val breakDelay: Int
+    val breakStageMask: Set
+    val swing: SwingMode
+    val swingType: BuildConfig.SwingType
+    val sounds: Boolean
+    val particles: Boolean
+    val breakingTexture: Boolean
+    val rotateForBreak: Boolean
+    val breakConfirmation: BreakConfirmationMode
+    val maxPendingBreaks: Int
+    val breaksPerTick: Int
+    val suitableToolsOnly: Boolean
+    val avoidLiquids: Boolean
+    val avoidSupporting: Boolean
+    val breakWeakBlocks: Boolean
+    val forceSilkTouch: Boolean
+    val forceFortunePickaxe: Boolean
+    val minFortuneLevel: Int
+    val ignoredBlocks: Set
 
-    abstract val renders: Boolean
-    abstract val fill: Boolean
-    abstract val outline: Boolean
-    abstract val outlineWidth: Int
-    abstract val animation: AnimationMode
+    val renders: Boolean
+    val fill: Boolean
+    val outline: Boolean
+    val outlineWidth: Int
+    val animation: AnimationMode
 
-    abstract val dynamicFillColor: Boolean
-    abstract val staticFillColor: Color
-    abstract val startFillColor: Color
-    abstract val endFillColor: Color
+    val dynamicFillColor: Boolean
+    val staticFillColor: Color
+    val startFillColor: Color
+    val endFillColor: Color
 
-    abstract val dynamicOutlineColor: Boolean
-    abstract val staticOutlineColor: Color
-    abstract val startOutlineColor: Color
-    abstract val endOutlineColor: Color
-
-    override fun requestInternal(request: BreakRequest, queueIfClosed: Boolean) {
-        BreakManager.request(request, queueIfClosed)
-    }
+    val dynamicOutlineColor: Boolean
+    val staticOutlineColor: Color
+    val startOutlineColor: Color
+    val endOutlineColor: Color
 
     enum class BreakMode {
         Vanilla,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index e4f5b4f15..974d28832 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -44,7 +44,18 @@ import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode
 import com.lambda.interaction.request.breaking.BreakConfig.BreakMode
 import com.lambda.interaction.request.breaking.BreakManager.activeRequest
+import com.lambda.interaction.request.breaking.BreakManager.breakInfos
+import com.lambda.interaction.request.breaking.BreakManager.breaks
+import com.lambda.interaction.request.breaking.BreakManager.canAccept
+import com.lambda.interaction.request.breaking.BreakManager.cancelBreak
+import com.lambda.interaction.request.breaking.BreakManager.initNewBreak
+import com.lambda.interaction.request.breaking.BreakManager.instantBreaks
+import com.lambda.interaction.request.breaking.BreakManager.makeRedundant
+import com.lambda.interaction.request.breaking.BreakManager.maxBreaksThisTick
+import com.lambda.interaction.request.breaking.BreakManager.performInstantBreaks
+import com.lambda.interaction.request.breaking.BreakManager.processNewBreaks
 import com.lambda.interaction.request.breaking.BreakManager.processRequest
+import com.lambda.interaction.request.breaking.BreakManager.updateBreakProgress
 import com.lambda.interaction.request.breaking.BreakType.Primary
 import com.lambda.interaction.request.breaking.BreakType.ReBreak
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
@@ -299,7 +310,7 @@ object BreakManager : RequestHandler(
                         rotationRequest = it.firstOrNull { info -> info.breakConfig.rotateForBreak }
                             ?.let { info ->
                                 val rotation = info.context.rotation
-                                if (instantBreaks.isEmpty()) info.request.rotation.request(rotation, false) else rotation
+                                if (instantBreaks.isEmpty()) rotation.submit(false) else rotation
                             }
                     }
                     .asReversed()
@@ -422,7 +433,7 @@ object BreakManager : RequestHandler(
             val ctx = iterator.next()
 
             if (!ctx.requestDependencies(request)) return false
-            rotationRequest = if (request.build.breaking.rotateForBreak) request.rotation.request(ctx.rotation, false) else null
+            rotationRequest = if (request.build.breaking.rotateForBreak) ctx.rotation.submit(false) else null
             if (!rotated || tickStage !in request.build.breaking.breakStageMask) return false
 
             val breakInfo = initNewBreak(ctx, request) ?: return false
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index f0f8610a8..7e0f4f2ca 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -19,11 +19,11 @@ package com.lambda.interaction.request.breaking
 
 import com.lambda.config.groups.BuildConfig
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.request.Request
 import com.lambda.interaction.request.hotbar.HotbarConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.blockState
@@ -37,11 +37,11 @@ annotation class BreakRequestBuilder
 data class BreakRequest(
     val contexts: Collection,
     val pendingInteractions: MutableCollection,
+    val build: BuildConfig,
     val hotbar: HotbarConfig,
     val rotation: RotationConfig,
     val inventory: InventoryConfig,
-    val interact: InteractionConfig,
-    val build: BuildConfig
+    val interact: InteractionConfig
 ) : Request() {
     override val config = build.breaking
     var onStart: ((BlockPos) -> Unit)? = null
@@ -55,6 +55,9 @@ data class BreakRequest(
     override val done: Boolean
         get() = runSafe { contexts.all { blockState(it.blockPos).isEmpty } } == true
 
+    override fun submit(queueIfClosed: Boolean) =
+        BreakManager.request(this, queueIfClosed)
+
     @BreakRequestBuilder
     class RequestBuilder(
         contexts: Collection,
@@ -65,7 +68,7 @@ data class BreakRequest(
         inventory: InventoryConfig,
         build: BuildConfig
     ) {
-        val request = BreakRequest(contexts, pendingInteractions, hotbar, rotation, inventory, interact, build)
+        val request = BreakRequest(contexts, pendingInteractions, build, hotbar, rotation, inventory, interact)
 
         @BreakRequestBuilder
         fun onStart(callback: (BlockPos) -> Unit) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt
index 44ccb403d..43a2c806d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt
@@ -25,43 +25,34 @@ import com.lambda.interaction.request.RequestConfig
  *
  * @param priority The priority of this configuration.
  */
-abstract class HotbarConfig : RequestConfig() {
+interface HotbarConfig : RequestConfig {
 
     /**
      * The number of ticks to keep the current hotbar selection active.
      */
-    abstract val keepTicks: Int
+    val keepTicks: Int
 
     /**
      * The delay, in ticks, between swapping hotbar selections
      */
-    abstract val swapDelay: Int
+    val swapDelay: Int
 
     /**
      * The amount of hotbar selection swaps that can happen per tick
      *
      * Only makes a difference if swapDelay is set to 0
      */
-    abstract val swapsPerTick: Int
+    val swapsPerTick: Int
 
     /**
      * The delay in ticks to pause actions after switching to the slot.
      *
      * Affects the validity state of the request
      */
-    abstract val swapPause: Int
+    val swapPause: Int
 
     /**
      * The sub-tick timings at which hotbar actions can be performed
      */
-    abstract val sequenceStageMask: Set
-
-    /**
-     * Registers a hotbar request with the HotbarManager.
-     *
-     * @param request The hotbar request to register.
-     */
-    override fun requestInternal(request: HotbarRequest, queueIfClosed: Boolean) {
-        HotbarManager.request(request, queueIfClosed)
-    }
-}
+    val sequenceStageMask: Set
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index 439556208..37df7d29f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -75,11 +75,10 @@ object HotbarManager : RequestHandler(
     }
 
     override fun SafeContext.handleRequest(request: HotbarRequest) {
-        val config = request.config
-        maxSwapsThisTick = config.swapsPerTick
-        swapDelay = swapDelay.coerceAtMost(config.swapDelay)
+        maxSwapsThisTick = request.swapsPerTick
+        swapDelay = swapDelay.coerceAtMost(request.swapDelay)
 
-        if (tickStage !in config.sequenceStageMask) return
+        if (tickStage !in request.sequenceStageMask) return
 
         val sameButLonger = activeRequest?.let { active ->
             request.slot == active.slot && request.keepTicks >= active.keepTicks
@@ -96,7 +95,7 @@ object HotbarManager : RequestHandler(
                 }
 
                 swapsThisTick++
-                swapDelay = config.swapDelay
+                swapDelay = request.swapDelay
                 return@swap
             }
 
@@ -112,9 +111,9 @@ object HotbarManager : RequestHandler(
     }
 
     private fun SafeContext.checkResetSwap() {
-        activeRequest?.let { activeInfo ->
+        activeRequest?.let { active ->
             val canStopSwap = swapsThisTick < maxSwapsThisTick
-            if (activeInfo.keepTicks <= 0 && tickStage in activeInfo.config.sequenceStageMask && canStopSwap) {
+            if (active.keepTicks <= 0 && tickStage in active.sequenceStageMask && canStopSwap) {
                 activeRequest = null
                 interaction.syncSelectedSlot()
             }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
index ffdadf357..6ef88613a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt
@@ -22,9 +22,9 @@ import com.lambda.interaction.request.Request
 class HotbarRequest(
     val slot: Int,
     override val config: HotbarConfig,
-    var keepTicks: Int = config.keepTicks,
-    var swapPause: Int = config.swapPause
-) : Request() {
+    override var keepTicks: Int = config.keepTicks,
+    override var swapPause: Int = config.swapPause
+) : Request(), HotbarConfig by config {
     var activeRequestAge = 0
     var swapPauseAge = 0
 
@@ -34,4 +34,7 @@ class HotbarRequest(
 
     override val done: Boolean
         get() = slot == HotbarManager.serverSlot && !swapPaused
+
+    override fun submit(queueIfClosed: Boolean) =
+        HotbarManager.request(this, queueIfClosed)
 }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt
similarity index 52%
rename from common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt
rename to common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt
index 99ec90468..249733bb0 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InteractConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt
@@ -15,20 +15,15 @@
  * along with this program.  If not, see .
  */
 
-package com.lambda.config.groups
+package com.lambda.interaction.request.interacting
 
-import com.lambda.config.groups.InteractionConfig.InteractConfirmationMode
+import com.lambda.config.groups.BuildConfig
+import com.lambda.config.groups.InteractionConfig
 import com.lambda.interaction.request.RequestConfig
-import com.lambda.interaction.request.interacting.InteractionManager
-import com.lambda.interaction.request.interacting.InteractionRequest
 
-abstract class InteractConfig : RequestConfig() {
-    abstract val rotate: Boolean
-    abstract val swingHand: Boolean
-    abstract val interactSwingType: BuildConfig.SwingType
-    abstract val interactConfirmationMode: InteractConfirmationMode
-
-    override fun requestInternal(request: InteractionRequest, queueIfClosed: Boolean) {
-        InteractionManager.request(request, queueIfClosed)
-    }
+interface InteractConfig : RequestConfig {
+    val rotate: Boolean
+    val swingHand: Boolean
+    val interactSwingType: BuildConfig.SwingType
+    val interactConfirmationMode: InteractionConfig.InteractConfirmationMode
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt
similarity index 90%
rename from common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
rename to common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt
index 6315917ef..d544c1aa9 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt
@@ -19,7 +19,6 @@ package com.lambda.interaction.request.interacting
 
 import com.lambda.Lambda.mc
 import com.lambda.config.groups.BuildConfig
-import com.lambda.config.groups.InteractConfig
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.InteractionContext
 import com.lambda.interaction.request.Request
@@ -28,7 +27,7 @@ import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.util.BlockUtils.matches
 import net.minecraft.util.math.BlockPos
 
-data class InteractionRequest(
+data class InteractRequest(
     val contexts: Collection,
     val onInteract: ((BlockPos) -> Unit)?,
     val pendingInteractionsList: MutableCollection,
@@ -36,7 +35,10 @@ data class InteractionRequest(
     val build: BuildConfig,
     val hotbar: HotbarConfig,
     val rotation: RotationConfig
-) : Request() {
+) : Request(), InteractConfig by config {
     override val done: Boolean
         get() = contexts.all { mc.world?.getBlockState(it.blockPos)?.matches(it.expectedState) == true }
+
+    override fun submit(queueIfClosed: Boolean) =
+        InteractionManager.request(this, queueIfClosed)
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
index 218691a5d..5eb2b1a47 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
@@ -36,7 +36,7 @@ object InteractedBlockHandler : PostActionHandler() {
         TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
     ) {
         info("${it::class.simpleName} at ${it.context.blockPos.toShortString()} timed out")
-        if (it.interact.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
+        if (it.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
             mc.world?.setBlockState(it.context.blockPos, it.context.cachedState)
         }
         it.pendingInteractionsList.remove(it.context)
@@ -52,7 +52,7 @@ object InteractedBlockHandler : PostActionHandler() {
                     if (!matchesTargetState(event.pos, info.context.expectedState, event.newState))
                         return@listen
 
-                    if (info.interact.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract)
+                    if (info.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract)
                         with (info.context) {
                             cachedState.onUse(world, player, Hand.MAIN_HAND, result)
                         }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
index a5584b0ae..da14884a7 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt
@@ -17,7 +17,6 @@
 
 package com.lambda.interaction.request.interacting
 
-import com.lambda.config.groups.InteractConfig
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.InteractionContext
 import com.lambda.interaction.request.ActionInfo
@@ -25,5 +24,5 @@ import com.lambda.interaction.request.ActionInfo
 data class InteractionInfo(
     override val context: InteractionContext,
     override val pendingInteractionsList: MutableCollection,
-    val interact: InteractConfig
-) : ActionInfo
\ No newline at end of file
+    private val config: InteractConfig
+) : ActionInfo, InteractConfig by config
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index 1afef557f..fd4a7a4e1 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -39,7 +39,7 @@ import com.lambda.util.player.swingHand
 import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
 import net.minecraft.util.Hand
 
-object InteractionManager : RequestHandler(
+object InteractionManager : RequestHandler(
     0,
     TickEvent.Pre,
     TickEvent.Input.Pre,
@@ -47,7 +47,7 @@ object InteractionManager : RequestHandler(
     TickEvent.Player.Post,
     onOpen = { activeRequest?.let { processRequest(it) } }
 ), PositionBlocking {
-    private var activeRequest: InteractionRequest? = null
+    private var activeRequest: InteractRequest? = null
     private var potentialInteractions = mutableListOf()
 
     private var interactionsThisTick = 0
@@ -70,7 +70,7 @@ object InteractionManager : RequestHandler(
         }
     }
 
-    override fun SafeContext.handleRequest(request: InteractionRequest) {
+    override fun SafeContext.handleRequest(request: InteractRequest) {
         if (activeRequest != null || BreakManager.activeThisTick || PlaceManager.activeThisTick) return
 
         activeRequest = request
@@ -78,7 +78,7 @@ object InteractionManager : RequestHandler(
         if (interactionsThisTick > 0) activeThisTick = true
     }
 
-    fun SafeContext.processRequest(request: InteractionRequest) {
+    fun SafeContext.processRequest(request: InteractRequest) {
         pendingActions.cleanUp()
         
         if (request.fresh) populateFrom(request)
@@ -88,23 +88,22 @@ object InteractionManager : RequestHandler(
         val iterator = potentialInteractions.iterator()
         while (iterator.hasNext()) {
             if (interactionsThisTick + 1 > maxInteractionsThisTick) break
-            val config = request.config
             val ctx = iterator.next()
 
             if (!ctx.requestDependencies(request)) return
 
-            if (config.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) {
-                InteractionInfo(ctx, request.pendingInteractionsList, config).startPending()
+            if (request.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) {
+                InteractionInfo(ctx, request.pendingInteractionsList, request).startPending()
             }
-            if (config.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
+            if (request.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
                 interaction.interactBlock(player, Hand.MAIN_HAND, ctx.result)
             } else {
                 interaction.sendSequencedPacket(world) { sequence ->
                     PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, ctx.result, sequence)
                 }
             }
-            if (request.config.swingHand) {
-                swingHand(request.config.interactSwingType, Hand.MAIN_HAND)
+            if (request.swingHand) {
+                swingHand(request.interactSwingType, Hand.MAIN_HAND)
             }
             request.onInteract?.invoke(ctx.blockPos)
             interactionsThisTick++
@@ -112,7 +111,7 @@ object InteractionManager : RequestHandler(
         }
     }
 
-    private fun populateFrom(request: InteractionRequest) {
+    private fun populateFrom(request: InteractRequest) {
         setPendingConfigs(request.build)
         potentialInteractions = request.contexts
             .filter { !isPosBlocked(it.blockPos) }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt
similarity index 58%
rename from common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
rename to common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt
index 4e093ef8f..e4ffecdc2 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,42 +15,36 @@
  * along with this program.  If not, see .
  */
 
-package com.lambda.config.groups
+package com.lambda.interaction.request.inventory
 
 import com.lambda.interaction.material.ContainerSelection
-import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer
 import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.container.MaterialContainer
 import com.lambda.interaction.request.RequestConfig
-import com.lambda.interaction.request.inventory.InventoryManager
-import com.lambda.interaction.request.inventory.InventoryRequest
 import net.minecraft.block.Block
 
-abstract class InventoryConfig : RequestConfig() {
-    abstract val disposables: Set
-    abstract val swapWithDisposables: Boolean
-    abstract val providerPriority: Priority
-    abstract val storePriority: Priority
+interface InventoryConfig : RequestConfig {
+    val disposables: Set
+    val swapWithDisposables: Boolean
+    val providerPriority: Priority
+    val storePriority: Priority
 
-    abstract val accessShulkerBoxes: Boolean
-    abstract val accessEnderChest: Boolean
-    abstract val accessChests: Boolean
-    abstract val accessStashes: Boolean
+    val accessShulkerBoxes: Boolean
+    val accessEnderChest: Boolean
+    val accessChests: Boolean
+    val accessStashes: Boolean
 
-    val containerSelection: ContainerSelection get() = selectContainer {
-        val allowedContainers = mutableSetOf().apply {
-            addAll(MaterialContainer.Rank.entries)
-            if (!accessShulkerBoxes) remove(MaterialContainer.Rank.SHULKER_BOX)
-            if (!accessEnderChest) remove(MaterialContainer.Rank.ENDER_CHEST)
-            if (!accessChests) remove(MaterialContainer.Rank.CHEST)
-            if (!accessStashes) remove(MaterialContainer.Rank.STASH)
+    val containerSelection: ContainerSelection
+        get() = ContainerSelection.Companion.selectContainer {
+            val allowedContainers = mutableSetOf().apply {
+                addAll(MaterialContainer.Rank.entries)
+                if (!accessShulkerBoxes) remove(MaterialContainer.Rank.SHULKER_BOX)
+                if (!accessEnderChest) remove(MaterialContainer.Rank.ENDER_CHEST)
+                if (!accessChests) remove(MaterialContainer.Rank.CHEST)
+                if (!accessStashes) remove(MaterialContainer.Rank.STASH)
+            }
+            ofAnyType(*allowedContainers.toTypedArray())
         }
-        ofAnyType(*allowedContainers.toTypedArray())
-    }
-
-    override fun requestInternal(request: InventoryRequest, queueIfClosed: Boolean) {
-        InventoryManager.request(request, queueIfClosed)
-    }
 
     enum class Priority {
         WithMinItems,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
index 39a83fd00..d86987734 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
@@ -17,12 +17,14 @@
 
 package com.lambda.interaction.request.inventory
 
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.interaction.request.Request
 
 class InventoryRequest(
     override val config: InventoryConfig
-) : Request() {
+) : Request(), InventoryConfig by config {
     override val done: Boolean
         get() = TODO("Not yet implemented")
+
+    override fun submit(queueIfClosed: Boolean) =
+        InventoryManager.request(this, queueIfClosed)
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt
index ca6746f11..45665877a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt
@@ -21,23 +21,19 @@ import com.lambda.config.groups.BuildConfig
 import com.lambda.event.Event
 import com.lambda.interaction.request.RequestConfig
 
-abstract class PlaceConfig : RequestConfig() {
-    abstract val rotateForPlace: Boolean
-    abstract val airPlace: AirPlaceMode
-    protected abstract val axisRotateSetting: Boolean
+interface PlaceConfig : RequestConfig {
+    val rotateForPlace: Boolean
+    val airPlace: AirPlaceMode
+    val axisRotateSetting: Boolean
     val axisRotate
         get() = rotateForPlace && airPlace.isEnabled() && axisRotateSetting
-    abstract val placeStageMask: Set
-    abstract val placeConfirmationMode: PlaceConfirmationMode
-    abstract val maxPendingPlacements: Int
-    abstract val placementsPerTick: Int
-    abstract val swing: Boolean
-    abstract val swingType: BuildConfig.SwingType
-    abstract val sounds: Boolean
-
-    override fun requestInternal(request: PlaceRequest, queueIfClosed: Boolean) {
-        PlaceManager.request(request, queueIfClosed)
-    }
+    val placeStageMask: Set
+    val placeConfirmationMode: PlaceConfirmationMode
+    val maxPendingPlacements: Int
+    val placementsPerTick: Int
+    val swing: Boolean
+    val swingType: BuildConfig.SwingType
+    val sounds: Boolean
 
     enum class AirPlaceMode {
         None,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index 3efa1c394..700399c51 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -32,6 +32,10 @@ import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.breaking.BreakManager
 import com.lambda.interaction.request.interacting.InteractionManager
 import com.lambda.interaction.request.placing.PlaceManager.activeRequest
+import com.lambda.interaction.request.placing.PlaceManager.maxPlacementsThisTick
+import com.lambda.interaction.request.placing.PlaceManager.placeBlock
+import com.lambda.interaction.request.placing.PlaceManager.populateFrom
+import com.lambda.interaction.request.placing.PlaceManager.potentialPlacements
 import com.lambda.interaction.request.placing.PlaceManager.processRequest
 import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingActions
 import com.lambda.interaction.request.placing.PlacedBlockHandler.setPendingConfigs
@@ -160,15 +164,13 @@ object PlaceManager : RequestHandler(
      * @see isPosBlocked
      */
     private fun populateFrom(request: PlaceRequest) {
-        val place = request.build.placing
-
         setPendingConfigs(request.build)
         potentialPlacements = request.contexts
             .filter { !isPosBlocked(it.blockPos) }
             .toMutableList()
 
-        val pendingLimit =  (place.maxPendingPlacements - pendingActions.size).coerceAtLeast(0)
-        maxPlacementsThisTick = (place.placementsPerTick.coerceAtMost(pendingLimit))
+        val pendingLimit =  (request.maxPendingPlacements - pendingActions.size).coerceAtLeast(0)
+        maxPlacementsThisTick = (request.placementsPerTick.coerceAtMost(pendingLimit))
     }
 
     /**
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
index 744b1f598..5f7605cdc 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
@@ -34,10 +34,13 @@ data class PlaceRequest(
     val hotbar: HotbarConfig,
     val pendingInteractions: MutableCollection,
     val onPlace: () -> Unit
-) : Request() {
+) : Request(), PlaceConfig by build.placing {
     override val config = build.placing
     override val done: Boolean
         get() = runSafe {
             contexts.all { it.expectedState.matches(blockState(it.blockPos)) }
         } == true
+
+    override fun submit(queueIfClosed: Boolean) =
+        PlaceManager.request(this, queueIfClosed)
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
index 86cc17743..a38dbed96 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
@@ -24,40 +24,36 @@ import com.lambda.interaction.request.RequestConfig
  *
  * @param priority The priority of this configuration.
  */
-abstract class RotationConfig : RequestConfig() {
+interface RotationConfig : RequestConfig {
     /**
      * - [RotationMode.Silent] Spoofing server-side rotation.
      * - [RotationMode.Sync] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim).
      * - [RotationMode.Lock] Locks the camera client-side.
      * - [RotationMode.None] No rotation.
      */
-    abstract val rotationMode: RotationMode
+    val rotationMode: RotationMode
 
     /**
      * The rotation speed (in degrees).
      */
-    abstract val turnSpeed: Double
+    val turnSpeed: Double
 
     /**
      * Ticks the rotation should not be changed.
      */
-    abstract val keepTicks: Int
+    val keepTicks: Int
 
     /**
      * Ticks to rotate back to the actual rotation.
      */
-    abstract val decayTicks: Int
+    val decayTicks: Int
 
     val rotate: Boolean get() = rotationMode != RotationMode.None
 
-    override fun requestInternal(request: RotationRequest, queueIfClosed: Boolean) {
-        RotationManager.request(request, queueIfClosed)
-    }
-
-    open class Instant(mode: RotationMode) : RotationConfig() {
-        override val turnSpeed get() = 360.0
-        override val keepTicks get() = 1
-        override val decayTicks get() = 1
+    open class Instant(mode: RotationMode) : RotationConfig {
         override val rotationMode = mode
+        override val keepTicks = 1
+        override val decayTicks = 1
+        override val turnSpeed = 360.0
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
index 7074ca608..aeb77d008 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
@@ -134,7 +134,7 @@ object RotationManager : RequestHandler(
         serverRotation = activeRotation/*.fixSensitivity(prevServerRotation)*/
 
         // Handle LOCK mode
-        if (activeRequest?.mode == RotationMode.Lock) {
+        if (activeRequest?.rotationMode == RotationMode.Lock) {
             mc.player?.yaw = activeRotation.yawF
             mc.player?.pitch = activeRotation.pitchF
         }
@@ -148,7 +148,7 @@ object RotationManager : RequestHandler(
             else player.rotation
 
             val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier
-            val turnSpeed = request.turnSpeed() * speedMultiplier
+            val turnSpeed = request.turnSpeed * speedMultiplier
 
             serverRotation.slerp(rotationTo, turnSpeed).wrap()
         } ?: player.rotation
@@ -166,7 +166,7 @@ object RotationManager : RequestHandler(
 
     @JvmStatic
     val lockRotation
-        get() = if (activeRequest?.mode == RotationMode.Lock) smoothRotation else null
+        get() = if (activeRequest?.rotationMode == RotationMode.Lock) smoothRotation else null
 
     @JvmStatic
     val headYaw
@@ -178,29 +178,29 @@ object RotationManager : RequestHandler(
 
     @JvmStatic
     val handYaw
-        get() = if (activeRequest?.mode == RotationMode.Lock) activeRotation.yawF else null
+        get() = if (activeRequest?.rotationMode == RotationMode.Lock) activeRotation.yawF else null
 
     @JvmStatic
     val handPitch
-        get() = if (activeRequest?.mode == RotationMode.Lock) activeRotation.pitchF else null
+        get() = if (activeRequest?.rotationMode == RotationMode.Lock) activeRotation.pitchF else null
 
     @JvmStatic
     val movementYaw: Float?
         get() {
-            if (activeRequest == null || activeRequest?.mode == RotationMode.Silent) return null
+            if (activeRequest == null || activeRequest?.rotationMode == RotationMode.Silent) return null
             return activeRotation.yaw.toFloat()
         }
 
     @JvmStatic
     val movementPitch: Float?
         get() {
-            if (activeRequest == null || activeRequest?.mode == RotationMode.Silent) return null
+            if (activeRequest == null || activeRequest?.rotationMode == RotationMode.Silent) return null
             return activeRotation.pitch.toFloat()
         }
 
     @JvmStatic
     fun getRotationForVector(deltaTime: Double): Vec2d? {
-        if (activeRequest == null || activeRequest?.mode == RotationMode.Silent) return null
+        if (activeRequest == null || activeRequest?.rotationMode == RotationMode.Silent) return null
 
         val rot = lerp(deltaTime, serverRotation, activeRotation)
         return Vec2d(rot.yaw, rot.pitch)
@@ -248,7 +248,7 @@ object RotationManager : RequestHandler(
             // Actual yaw used by the physics engine
             var actualYaw = activeRotation.yaw
 
-            if (activeRequest?.mode == RotationMode.Silent) {
+            if (activeRequest?.rotationMode == RotationMode.Silent) {
                 actualYaw = player.yaw.toDouble()
             }
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
index ddf95452a..9e9579843 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt
@@ -23,21 +23,18 @@ import com.lambda.threading.runSafe
 
 data class RotationRequest(
     val target: RotationTarget,
-    val mode: RotationMode,
     override val config: RotationConfig,
-    var keepTicks: Int = 3,
-    var decayTicks: Int = 0,
-    val turnSpeed: () -> Double = { 180.0 },
+    override val rotationMode: RotationMode = config.rotationMode,
+    override val turnSpeed: Double = config.turnSpeed,
+    override var keepTicks: Int = config.keepTicks,
+    override var decayTicks: Int = config.decayTicks,
     val speedMultiplier: Double = 1.0
-) : Request() {
+) : Request(), RotationConfig by config {
     var age = 0
 
-    constructor(
-        target: RotationTarget,
-        config: RotationConfig,
-        speedMultiplier: Double = 1.0
-    ) : this(target, config.rotationMode, config, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier)
-
     override val done: Boolean get() =
-        mode == RotationMode.None || runSafe { target.verify() } == true
-}
+        rotationMode == RotationMode.None || runSafe { target.verify() } == true
+
+    override fun submit(queueIfClosed: Boolean): RotationRequest =
+        RotationManager.request(this, queueIfClosed)
+}
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt
index 103f58e26..74ff1c42a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTarget.kt
@@ -52,6 +52,6 @@ data class RotationTarget(
      * @param config The rotation configuration.
      * @return [RotationRequest] containing this [RotationTarget].
      */
-    fun requestBy(config: RotationConfig) =
-        config.request(RotationRequest(this, config))
+    fun requestBy(config: RotationConfig, queueIfClosed: Boolean = true) =
+        RotationRequest(this, config).submit(queueIfClosed)
 }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
index 4990294bf..05e4af378 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
@@ -34,7 +34,7 @@ object SilentSwap : Module(
 
     init {
         listen {
-            if (!hotbar.request(HotbarRequest(0, hotbar)).done) {
+            if (!HotbarRequest(0, hotbar).submit().done) {
                 it.cancel()
                 return@listen
             }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
index eff263baa..27ea52482 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
@@ -158,8 +158,7 @@ object AntiAim : Module(
 
         listen(priority = Int.MIN_VALUE) {
             if (currentYaw == wrap(player.yaw) && currentPitch == player.pitch) return@listen
-            val rotationRequest = RotationRequest(lookAt(Rotation(currentYaw, currentPitch)), rotation)
-            rotation.request(rotationRequest, false)
+            RotationRequest(lookAt(Rotation(currentYaw, currentPitch)), rotation).submit(false)
         }
     }
 
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index c83a766a2..837a41ea7 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -187,7 +187,7 @@ object PacketMine : Module(
             onReBreak { reBreakPos = it }
             onItemDrop { _ -> itemDrops++ }
         }
-        breakConfig.request(request, true)
+        request.submit()
     }
 
     private fun SafeContext.breakContexts(positions: Collection) =
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt
index 27963be28..d5f2c596b 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.task.tasks
 
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.container.ContainerManager
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index b03c92695..389177959 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -21,7 +21,6 @@ import baritone.api.pathing.goals.GoalBlock
 import com.lambda.Lambda.LOG
 import com.lambda.config.groups.BuildConfig
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
@@ -46,7 +45,8 @@ import com.lambda.interaction.construction.verify.TargetState
 import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer
 import com.lambda.interaction.request.breaking.BreakRequest.Companion.breakRequest
 import com.lambda.interaction.request.hotbar.HotbarConfig
-import com.lambda.interaction.request.interacting.InteractionRequest
+import com.lambda.interaction.request.interacting.InteractRequest
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.placing.PlaceRequest
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
@@ -164,7 +164,7 @@ class BuildTask @Ta5kBuilder constructor(
                                     onItemDrop { onItemDrop(it) }
                                 }
                             }
-                            build.breaking.request(request)
+                            request.submit()
                             return@listen
                         }
                         is PlaceResult.Place -> {
@@ -173,11 +173,7 @@ class BuildTask @Ta5kBuilder constructor(
                                 .distinctBy { it.blockPos }
                                 .take(emptyPendingInteractionSlots)
 
-                            build.placing.request(
-                                PlaceRequest(
-                                    placeResults.map { it.context }, build, rotation, hotbar, pendingInteractions
-                                ) { placements++ }
-                            )
+                            PlaceRequest(placeResults.map { it.context }, build, rotation, hotbar, pendingInteractions) { placements++ }.submit()
                         }
                         is InteractResult.Interact -> {
                             val interactResults = resultsNotBlocked
@@ -186,17 +182,7 @@ class BuildTask @Ta5kBuilder constructor(
                                 .take(emptyPendingInteractionSlots)
                                 .map { it.context }
 
-                            build.interacting.request(
-                                InteractionRequest(
-                                    interactResults,
-                                    null,
-                                    pendingInteractions,
-                                    build.interacting,
-                                    build,
-                                    hotbar,
-                                    rotation
-                                )
-                            )
+                            InteractRequest(interactResults, null, pendingInteractions, build.interacting, build, hotbar, rotation).submit()
                         }
                     }
                 }
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index 05b1ba40e..9c82c30e1 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -19,7 +19,6 @@ package com.lambda.task.tasks
 
 import com.lambda.config.groups.BuildConfig
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
 import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint
@@ -27,6 +26,7 @@ import com.lambda.interaction.construction.result.BuildResult
 import com.lambda.interaction.construction.result.PlaceResult
 import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
 import com.lambda.interaction.construction.verify.TargetState
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
@@ -45,7 +45,7 @@ class PlaceContainer @Ta5kBuilder constructor(
     val build: BuildConfig = TaskFlowModule.build,
     val rotation: RotationConfig = TaskFlowModule.rotation,
     val interact: InteractionConfig = TaskFlowModule.interaction,
-    val inventory: InventoryConfig = TaskFlowModule.inventory,
+    val inventory: InventoryConfig = TaskFlowModule.inventory
 ) : Task() {
     private val startStack: ItemStack = stack.copy()
     override val name: String get() = "Placing container ${startStack.name.string}"

From 441d4b99ce9bf7149ea7cca777ff60c22fc1bdd5 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 23 Jul 2025 16:07:48 +0100
Subject: [PATCH 308/364] secondary request submit function for usage diversity

---
 .../interaction/construction/context/InteractionContext.kt   | 5 +++--
 .../lambda/interaction/construction/context/PlaceContext.kt  | 5 +++--
 .../main/kotlin/com/lambda/interaction/request/Request.kt    | 5 +++++
 .../kotlin/com/lambda/module/modules/debug/SilentSwap.kt     | 3 ++-
 .../main/kotlin/com/lambda/module/modules/player/AntiAim.kt  | 3 ++-
 .../kotlin/com/lambda/module/modules/player/PacketMine.kt    | 5 ++---
 common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt    | 5 ++---
 7 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
index b8102d009..e4691c23e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
@@ -20,6 +20,7 @@ package com.lambda.interaction.construction.context
 import com.lambda.context.SafeContext
 import com.lambda.graphics.renderer.esp.DirectionMask
 import com.lambda.graphics.renderer.esp.DirectionMask.exclude
+import com.lambda.interaction.request.Request.Companion.submit
 import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.hotbar.HotbarRequest
 import com.lambda.interaction.request.interacting.InteractRequest
@@ -66,8 +67,8 @@ class InteractionContext(
     }
 
     fun requestDependencies(request: InteractRequest): Boolean {
-        val hotbarRequest = HotbarRequest(hotbarIndex, request.hotbar).submit(false)
-        val validRotation = if (request.rotate) rotation.submit(false).done else true
+        val hotbarRequest = submit(HotbarRequest(hotbarIndex, request.hotbar), false)
+        val validRotation = if (request.rotate) submit(rotation, false).done else true
         return hotbarRequest.done && validRotation
     }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
index ba6b46c3f..474c360e8 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
@@ -21,6 +21,7 @@ import com.lambda.Lambda.mc
 import com.lambda.context.SafeContext
 import com.lambda.graphics.renderer.esp.DirectionMask
 import com.lambda.graphics.renderer.esp.DirectionMask.exclude
+import com.lambda.interaction.request.Request.Companion.submit
 import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.hotbar.HotbarRequest
 import com.lambda.interaction.request.placing.PlaceRequest
@@ -76,9 +77,9 @@ data class PlaceContext(
     }
 
     fun requestDependencies(request: PlaceRequest): Boolean {
-        val hotbarRequest = HotbarRequest(hotbarIndex, request.hotbar).submit(false)
+        val hotbarRequest = submit(HotbarRequest(hotbarIndex, request.hotbar), false)
         val validRotation = if (request.rotateForPlace) {
-            rotation.submit(false).done && currentDirIsValid
+            submit(rotation, false).done && currentDirIsValid
         } else true
         return hotbarRequest.done && validRotation
     }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
index 5482cdfda..d2c64d42e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt
@@ -24,4 +24,9 @@ abstract class Request {
     abstract val done: Boolean
 
     abstract fun submit(queueIfClosed: Boolean = true): Request
+
+    companion object {
+        fun submit(request: Request, queueIfClosed: Boolean = true) =
+            request.submit(queueIfClosed)
+    }
 }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
index 05e4af378..ff7bb76c7 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
@@ -20,6 +20,7 @@ package com.lambda.module.modules.debug
 import com.lambda.config.groups.HotbarSettings
 import com.lambda.event.events.PlayerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.request.Request.Companion.submit
 import com.lambda.interaction.request.hotbar.HotbarRequest
 import com.lambda.module.Module
 import com.lambda.module.tag.ModuleTag
@@ -34,7 +35,7 @@ object SilentSwap : Module(
 
     init {
         listen {
-            if (!HotbarRequest(0, hotbar).submit().done) {
+            if (!submit(HotbarRequest(0, hotbar)).done) {
                 it.cancel()
                 return@listen
             }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
index 27ea52482..98171c742 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
@@ -22,6 +22,7 @@ import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.request.Request.Companion.submit
 import com.lambda.interaction.request.rotating.Rotation
 import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo
 import com.lambda.interaction.request.rotating.Rotation.Companion.wrap
@@ -158,7 +159,7 @@ object AntiAim : Module(
 
         listen(priority = Int.MIN_VALUE) {
             if (currentYaw == wrap(player.yaw) && currentPitch == player.pitch) return@listen
-            RotationRequest(lookAt(Rotation(currentYaw, currentPitch)), rotation).submit(false)
+            submit(RotationRequest(lookAt(Rotation(currentYaw, currentPitch)), rotation), false)
         }
     }
 
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 837a41ea7..6bb271e3e 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -176,7 +176,7 @@ object PacketMine : Module(
         if (!reBreaking) {
             queuePositions.retainAllPositions(breakContexts)
         }
-        val request = breakRequest(
+        breakRequest(
             breakContexts, pendingInteractions, rotation, hotbar, interact, inventory, build,
         ) {
             onStart { queuePositions.removePos(it); addBreak(it) }
@@ -186,8 +186,7 @@ object PacketMine : Module(
             onReBreakStart { reBreakPos = it }
             onReBreak { reBreakPos = it }
             onItemDrop { _ -> itemDrops++ }
-        }
-        request.submit()
+        }.submit()
     }
 
     private fun SafeContext.breakContexts(positions: Collection) =
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index 389177959..89b695fa9 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -156,15 +156,14 @@ class BuildTask @Ta5kBuilder constructor(
                                 requestContexts.addAll(breakResults.map { it.context })
                             }
 
-                            val request = breakRequest(
+                            breakRequest(
                                 requestContexts, pendingInteractions, rotation, hotbar, interactionConfig, inventory, build,
                             ) {
                                 onStop { breaks++ }
                                 onItemDrop?.let { onItemDrop ->
                                     onItemDrop { onItemDrop(it) }
                                 }
-                            }
-                            request.submit()
+                            }.submit()
                             return@listen
                         }
                         is PlaceResult.Place -> {

From 1b70a1a2b09f5d6239320bb682965b1068bfe293 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 23 Jul 2025 20:24:58 +0100
Subject: [PATCH 309/364] remove priority typealias

---
 .../lambda/interaction/request/Priority.kt    | 20 -------------------
 .../request/breaking/BreakManager.kt          |  3 +--
 .../request/placing/PlaceManager.kt           |  3 +--
 .../request/rotating/RotationManager.kt       |  3 +--
 4 files changed, 3 insertions(+), 26 deletions(-)
 delete mode 100644 common/src/main/kotlin/com/lambda/interaction/request/Priority.kt

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Priority.kt b/common/src/main/kotlin/com/lambda/interaction/request/Priority.kt
deleted file mode 100644
index 26d6f21e7..000000000
--- a/common/src/main/kotlin/com/lambda/interaction/request/Priority.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2025 Lambda
- *
- * This program 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.
- *
- * This program 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 this program.  If not, see .
- */
-
-package com.lambda.interaction.request
-
-typealias Priority = Int
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 974d28832..ab3fc60ef 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -39,7 +39,6 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
 import com.lambda.interaction.construction.verify.TargetState
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
 import com.lambda.interaction.request.PositionBlocking
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode
 import com.lambda.interaction.request.breaking.BreakConfig.BreakMode
@@ -130,7 +129,7 @@ object BreakManager : RequestHandler(
 
     fun Any.onBreak(
         alwaysListen: Boolean = false,
-        priority: Priority = 0,
+        priority: Int = 0,
         block: SafeContext.() -> Unit
     ) = this.listen(priority, alwaysListen) {
         block()
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index 700399c51..89f45188c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -27,7 +27,6 @@ import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.construction.context.PlaceContext
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
 import com.lambda.interaction.request.PositionBlocking
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.breaking.BreakManager
 import com.lambda.interaction.request.interacting.InteractionManager
@@ -86,7 +85,7 @@ object PlaceManager : RequestHandler(
 
     fun Any.onPlace(
         alwaysListen: Boolean = false,
-        priority: Priority = 0,
+        priority: Int = 0,
         block: SafeContext.() -> Unit
     ) = this.listen(priority, alwaysListen) {
         block()
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
index aeb77d008..773a3d842 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
@@ -28,7 +28,6 @@ import com.lambda.event.events.TickEvent
 import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
-import com.lambda.interaction.request.Priority
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.rotating.Rotation.Companion.slerp
 import com.lambda.interaction.request.rotating.Rotation.Companion.wrap
@@ -64,7 +63,7 @@ object RotationManager : RequestHandler(
 
     fun Any.onRotate(
         alwaysListen: Boolean = false,
-        priority: Priority = 0,
+        priority: Int = 0,
         block: SafeContext.() -> Unit
     ) = this.listen(priority, alwaysListen) {
         block()

From 771469d1ab86914fb1ebc7701459f82c14139b72 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 23 Jul 2025 20:58:36 +0100
Subject: [PATCH 310/364] consistent confirmation checks for block updates in
 the managers

---
 .../context/InteractionContext.kt             |  2 +-
 .../request/breaking/BreakManager.kt          |  5 ++--
 .../request/breaking/BrokenBlockHandler.kt    |  6 +++--
 .../interacting/InteractedBlockHandler.kt     | 27 +++++++------------
 .../interaction/request/placing/PlaceInfo.kt  |  3 ++-
 .../request/placing/PlaceManager.kt           |  2 +-
 .../request/placing/PlaceRequest.kt           |  3 ++-
 .../request/placing/PlacedBlockHandler.kt     | 17 ++++++------
 8 files changed, 30 insertions(+), 35 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
index e4691c23e..33bcb966a 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt
@@ -36,7 +36,7 @@ class InteractionContext(
     override val result: BlockHitResult,
     override val rotation: RotationRequest,
     override var hotbarIndex: Int,
-    override val cachedState: BlockState,
+    override var cachedState: BlockState,
     override val expectedState: BlockState,
 ) : BuildContext() {
     private val baseColor = Color(35, 254, 79, 25)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index ab3fc60ef..78b52f14f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -67,7 +67,6 @@ import com.lambda.interaction.request.rotating.RotationRequest
 import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
-import com.lambda.util.BlockUtils.emptyState
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.isNotEmpty
@@ -192,8 +191,8 @@ object BreakManager : RequestHandler(
                     if (isNotBroken(currentState, event.newState)) {
                         // check to see if its just some small property changes, e.g. redstone ore changing the LIT property
                         if (!currentState.matches(event.newState, ProcessorRegistry.postProcessedProperties))
-                            this@BreakManager.warn("Break at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.cachedState.emptyState}")
-                        // update the checked state
+                            this@BreakManager.warn("Server updated breaking block at ${event.pos.toShortString()} with a new state: ${event.newState}")
+                        // update the cached state
                         info.context.cachedState = event.newState
                         return@listen
                     }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 71fc392aa..8ebdd7a11 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -59,7 +59,7 @@ object BrokenBlockHandler : PostActionHandler() {
             if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out")
             else if (!TaskFlowModule.ignoreItemDropWarnings) warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
 
-            if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
+            if (info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
                 world.setBlockState(info.context.blockPos, info.context.cachedState)
             }
         }
@@ -78,8 +78,10 @@ object BrokenBlockHandler : PostActionHandler() {
                 // return if the block's not broken
                 if (isNotBroken(currentState, event.newState)) {
                     // return if the state hasn't changed
-                    if (event.newState.matches(currentState, ProcessorRegistry.postProcessedProperties))
+                    if (event.newState.matches(currentState, ProcessorRegistry.postProcessedProperties)) {
+                        pending.context.cachedState = event.newState
                         return@listen
+                    }
 
                     if (!pending.isReBreaking) {
                         this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}")
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
index 5eb2b1a47..27ab65aba 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
@@ -27,9 +27,6 @@ import com.lambda.util.BlockUtils.matches
 import com.lambda.util.Communication.info
 import com.lambda.util.Communication.warn
 import com.lambda.util.collections.LimitedDecayQueue
-import net.minecraft.block.BlockState
-import net.minecraft.util.Hand
-import net.minecraft.util.math.BlockPos
 
 object InteractedBlockHandler : PostActionHandler() {
     override val pendingActions = LimitedDecayQueue(
@@ -46,24 +43,20 @@ object InteractedBlockHandler : PostActionHandler() {
         listen(priority = Int.MIN_VALUE) { event ->
             pendingActions
                 .firstOrNull { it.context.blockPos == event.pos }
-                ?.let { info ->
-                    info.stopPending()
+                ?.let { pending ->
+                    pending.stopPending()
 
-                    if (!matchesTargetState(event.pos, info.context.expectedState, event.newState))
+                    if (!pending.context.expectedState.matches(event.newState)) {
+                        this@InteractedBlockHandler.warn("Interacted block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.expectedState}")
                         return@listen
+                    }
 
-                    if (info.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract)
-                        with (info.context) {
-                            cachedState.onUse(world, player, Hand.MAIN_HAND, result)
-                        }
+                    //ToDo: reliable way to recreate the sounds played when interacting with any given block
+//                    if (pending.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract)
+//                        with (pending.context) {
+//                            cachedState.onUse(world, player, Hand.MAIN_HAND, result)
+//                        }
                 }
         }
     }
-
-    private fun matchesTargetState(pos: BlockPos, targetState: BlockState, newState: BlockState) =
-        if (targetState.matches(newState)) true
-        else {
-            this@InteractedBlockHandler.warn("Interaction at ${pos.toShortString()} was rejected with $newState instead of $targetState")
-            false
-        }
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt
index 9047ed645..dbb51800e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt
@@ -20,10 +20,11 @@ package com.lambda.interaction.request.placing
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.context.PlaceContext
 import com.lambda.interaction.request.ActionInfo
+import net.minecraft.util.math.BlockPos
 
 data class PlaceInfo(
     override val context: PlaceContext,
     override val pendingInteractionsList: MutableCollection,
-    val onPlace: () -> Unit,
+    val onPlace: ((BlockPos) -> Unit)?,
     val placeConfig: PlaceConfig
 ) : ActionInfo
\ No newline at end of file
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index 89f45188c..6aa521076 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -316,7 +316,7 @@ object PlaceManager : RequestHandler(
         if (!player.abilities.creativeMode) itemStack.decrement(1)
 
         if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) {
-            request.onPlace()
+            request.onPlace?.invoke(placeContext.blockPos)
         }
 
         return ActionResult.success(world.isClient)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
index 5f7605cdc..65e1efccd 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt
@@ -26,6 +26,7 @@ import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.BlockUtils.matches
+import net.minecraft.util.math.BlockPos
 
 data class PlaceRequest(
     val contexts: Collection,
@@ -33,7 +34,7 @@ data class PlaceRequest(
     val rotation: RotationConfig,
     val hotbar: HotbarConfig,
     val pendingInteractions: MutableCollection,
-    val onPlace: () -> Unit
+    val onPlace: ((BlockPos) -> Unit)? = null
 ) : Request(), PlaceConfig by build.placing {
     override val config = build.placing
     override val done: Boolean
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
index 4358d196c..f14ca3ad4 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
@@ -45,21 +45,20 @@ object PlacedBlockHandler : PostActionHandler() {
         listen(priority = Int.MIN_VALUE) { event ->
             pendingActions
                 .firstOrNull { it.context.blockPos == event.pos }
-                ?.let { info ->
-                    info.stopPending()
+                ?.let { pending ->
+                    pending.stopPending()
 
                     // return if the block wasn't placed properly
-                    if (!event.newState.matches(info.context.expectedState)) {
-                        this@PlacedBlockHandler.warn(
-                            "Place at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${info.context.expectedState}"
-                        )
+                    if (!pending.context.expectedState.matches(event.newState)) {
+                        this@PlacedBlockHandler.warn("Place at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.expectedState}")
+                        return@listen
                     }
 
-                    if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
-                        with (info.context) {
+                    if (pending.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
+                        with (pending.context) {
                             placeSound(expectedState.block.item as BlockItem, expectedState, blockPos)
                         }
-                    info.onPlace()
+                    pending.onPlace?.invoke(pending.context.blockPos)
                 }
         }
     }

From 52843b6374aa461ea2ef51c080c5c012f40b7d77 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 23 Jul 2025 21:34:48 +0100
Subject: [PATCH 311/364] equality check over elvis

---
 .../com/lambda/interaction/request/breaking/BreakManager.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 78b52f14f..398841561 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -642,7 +642,7 @@ object BreakManager : RequestHandler(
 
                     return primaryBreak?.let { primary ->
                         updateBreakProgress(primary)
-                    } ?: false
+                    } == true
                 }
                 is ReBreakResult.ReBroke -> {
                     info.type = ReBreak

From 2427e33024a65e4c28cc03424d43b7b08998ee41 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 23 Jul 2025 21:59:40 +0100
Subject: [PATCH 312/364] nest the BreakRequestBuilder annotation class

---
 .../com/lambda/interaction/request/breaking/BreakRequest.kt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index 7e0f4f2ca..b734a3fab 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -31,9 +31,6 @@ import com.lambda.util.BlockUtils.isEmpty
 import net.minecraft.entity.ItemEntity
 import net.minecraft.util.math.BlockPos
 
-@DslMarker
-annotation class BreakRequestBuilder
-
 data class BreakRequest(
     val contexts: Collection,
     val pendingInteractions: MutableCollection,
@@ -58,6 +55,9 @@ data class BreakRequest(
     override fun submit(queueIfClosed: Boolean) =
         BreakManager.request(this, queueIfClosed)
 
+    @DslMarker
+    annotation class BreakRequestBuilder
+
     @BreakRequestBuilder
     class RequestBuilder(
         contexts: Collection,

From 56a2abff6ea5069ea42bfac453befa324b68256b Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 26 Jul 2025 00:52:01 +0100
Subject: [PATCH 313/364] decrement item stack even if awaiting before client
 placement

---
 .../com/lambda/interaction/request/placing/PlaceManager.kt   | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index 6aa521076..eecfe0a7b 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -295,6 +295,9 @@ object PlaceManager : RequestHandler(
             }
         }
 
+        val itemStack = itemPlacementContext.stack
+        if (!player.abilities.creativeMode) itemStack.decrement(1)
+
         if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
             return ActionResult.success(world.isClient)
 
@@ -304,7 +307,6 @@ object PlaceManager : RequestHandler(
         if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL
 
         val blockPos = itemPlacementContext.blockPos
-        val itemStack = itemPlacementContext.stack
         var hitState = world.getBlockState(blockPos)
         if (hitState.isOf(blockState.block)) {
             hitState = item.placeFromNbt(blockPos, world, itemStack, hitState)
@@ -313,7 +315,6 @@ object PlaceManager : RequestHandler(
         }
 
         if (placeConfig.sounds) placeSound(item, hitState, blockPos)
-        if (!player.abilities.creativeMode) itemStack.decrement(1)
 
         if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) {
             request.onPlace?.invoke(placeContext.blockPos)

From e5d64c451a2bdf748d7635ef92a811496df8fd4d Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 26 Jul 2025 01:12:59 +0100
Subject: [PATCH 314/364] compile time fixes

---
 .../java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java  | 3 ++-
 .../main/java/com/lambda/mixin/entity/LivingEntityMixin.java   | 1 -
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
index 7d078fba8..e93825729 100644
--- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
+++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
@@ -25,9 +25,9 @@
 import com.lambda.interaction.PlayerPacketManager;
 import com.lambda.interaction.request.rotating.RotationManager;
 import com.lambda.module.modules.player.PortalGui;
+import com.lambda.module.modules.render.ViewModel;
 import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
 import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
-import com.lambda.module.modules.render.ViewModel;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
 import net.minecraft.client.input.Input;
@@ -35,6 +35,7 @@
 import net.minecraft.client.network.ClientPlayerEntity;
 import net.minecraft.entity.MovementType;
 import net.minecraft.entity.damage.DamageSource;
+import net.minecraft.util.Hand;
 import net.minecraft.util.math.Vec3d;
 import org.spongepowered.asm.mixin.Final;
 import org.spongepowered.asm.mixin.Mixin;
diff --git a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java
index 534383585..e233bb3ec 100644
--- a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java
+++ b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java
@@ -20,7 +20,6 @@
 import com.lambda.Lambda;
 import com.lambda.event.EventFlow;
 import com.lambda.event.events.MovementEvent;
-import com.lambda.interaction.request.rotation.RotationManager;
 import com.lambda.module.modules.render.ViewModel;
 import com.lambda.interaction.request.rotating.RotationManager;
 import net.minecraft.entity.LivingEntity;

From b02a45252ce776ea45da4122c20e07bd43a31df3 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 26 Jul 2025 18:02:39 +0100
Subject: [PATCH 315/364] undo small "fix" i made because it wasnt broken to
 begin with and i broke it with the "fix"

---
 .../lambda/interaction/request/breaking/BrokenBlockHandler.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 8ebdd7a11..92bd4d625 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -59,7 +59,7 @@ object BrokenBlockHandler : PostActionHandler() {
             if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out")
             else if (!TaskFlowModule.ignoreItemDropWarnings) warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
 
-            if (info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
+            if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
                 world.setBlockState(info.context.blockPos, info.context.cachedState)
             }
         }

From 3b445f2a8e0e22c5269a0d860c505fff6722f149 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 26 Jul 2025 21:49:10 +0100
Subject: [PATCH 316/364] improve block update checks in managers

---
 .../construction/context/PlaceContext.kt          |  2 +-
 .../interaction/request/breaking/BreakManager.kt  | 12 ++----------
 .../request/breaking/BrokenBlockHandler.kt        |  6 +++---
 .../request/interacting/InteractedBlockHandler.kt | 12 ++++++++++--
 .../request/interacting/InteractionManager.kt     |  2 +-
 .../request/placing/PlacedBlockHandler.kt         | 15 +++++++++++----
 6 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
index 474c360e8..aec3d09b4 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
@@ -38,7 +38,7 @@ data class PlaceContext(
     override val rotation: RotationRequest,
     override var hotbarIndex: Int,
     override val blockPos: BlockPos,
-    override val cachedState: BlockState,
+    override var cachedState: BlockState,
     override val expectedState: BlockState,
     val sneak: Boolean,
     val insideBlock: Boolean,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 398841561..fb9fdd83f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -33,7 +33,6 @@ import com.lambda.graphics.renderer.esp.builders.buildOutline
 import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
 import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint
 import com.lambda.interaction.construction.context.BreakContext
-import com.lambda.interaction.construction.processing.ProcessorRegistry
 import com.lambda.interaction.construction.result.BreakResult
 import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
 import com.lambda.interaction.construction.verify.TargetState
@@ -70,8 +69,6 @@ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.isNotEmpty
-import com.lambda.util.BlockUtils.matches
-import com.lambda.util.Communication.warn
 import com.lambda.util.item.ItemUtils.block
 import com.lambda.util.math.lerp
 import com.lambda.util.player.gamemode
@@ -189,9 +186,6 @@ object BreakManager : RequestHandler(
                     val currentState = info.context.cachedState
                     // if not broken
                     if (isNotBroken(currentState, event.newState)) {
-                        // check to see if its just some small property changes, e.g. redstone ore changing the LIT property
-                        if (!currentState.matches(event.newState, ProcessorRegistry.postProcessedProperties))
-                            this@BreakManager.warn("Server updated breaking block at ${event.pos.toShortString()} with a new state: ${event.newState}")
                         // update the cached state
                         info.context.cachedState = event.newState
                         return@listen
@@ -199,11 +193,9 @@ object BreakManager : RequestHandler(
                     destroyBlock(info)
                     info.request.onStop?.invoke(info.context.blockPos)
                     info.internalOnBreak()
-                    if (!info.callbacksCompleted) {
-                        info.startPending()
-                    } else {
+                    if (info.callbacksCompleted)
                         ReBreakManager.offerReBreak(info)
-                    }
+                    else info.startPending()
                     info.nullify()
                 }
         }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 92bd4d625..2b2f5cda4 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -83,11 +83,11 @@ object BrokenBlockHandler : PostActionHandler() {
                         return@listen
                     }
 
-                    if (!pending.isReBreaking) {
+                    if (pending.isReBreaking) {
+                        pending.context.cachedState = event.newState
+                    } else {
                         this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}")
                         pending.stopPending()
-                    } else {
-                        pending.context.cachedState = event.newState
                     }
                     return@listen
                 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
index 27ab65aba..9733db974 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt
@@ -21,6 +21,7 @@ import com.lambda.Lambda.mc
 import com.lambda.config.groups.InteractionConfig
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.construction.processing.ProcessorRegistry
 import com.lambda.interaction.request.PostActionHandler
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.matches
@@ -44,13 +45,20 @@ object InteractedBlockHandler : PostActionHandler() {
             pendingActions
                 .firstOrNull { it.context.blockPos == event.pos }
                 ?.let { pending ->
-                    pending.stopPending()
-
                     if (!pending.context.expectedState.matches(event.newState)) {
+                        if (pending.context.cachedState.matches(event.newState, ProcessorRegistry.postProcessedProperties)) {
+                            pending.context.cachedState = event.newState
+                            return@listen
+                        }
+
+                        pending.stopPending()
+
                         this@InteractedBlockHandler.warn("Interacted block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.expectedState}")
                         return@listen
                     }
 
+                    pending.stopPending()
+
                     //ToDo: reliable way to recreate the sounds played when interacting with any given block
 //                    if (pending.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.AwaitThenInteract)
 //                        with (pending.context) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index fd4a7a4e1..1cb205235 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -92,7 +92,7 @@ object InteractionManager : RequestHandler(
 
             if (!ctx.requestDependencies(request)) return
 
-            if (request.interactConfirmationMode == InteractionConfig.InteractConfirmationMode.None) {
+            if (request.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.None) {
                 InteractionInfo(ctx, request.pendingInteractionsList, request).startPending()
             }
             if (request.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.AwaitThenInteract) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
index f14ca3ad4..1756e4fc9 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
@@ -20,6 +20,7 @@ package com.lambda.interaction.request.placing
 import com.lambda.Lambda.mc
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.construction.processing.ProcessorRegistry
 import com.lambda.interaction.request.PostActionHandler
 import com.lambda.interaction.request.placing.PlaceManager.placeSound
 import com.lambda.module.modules.client.TaskFlowModule
@@ -46,14 +47,20 @@ object PlacedBlockHandler : PostActionHandler() {
             pendingActions
                 .firstOrNull { it.context.blockPos == event.pos }
                 ?.let { pending ->
-                    pending.stopPending()
-
-                    // return if the block wasn't placed properly
                     if (!pending.context.expectedState.matches(event.newState)) {
-                        this@PlacedBlockHandler.warn("Place at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.expectedState}")
+                        if (pending.context.cachedState.matches(event.newState, ProcessorRegistry.postProcessedProperties)) {
+                            pending.context.cachedState = event.newState
+                            return@listen
+                        }
+
+                        pending.stopPending()
+
+                        this@PlacedBlockHandler.warn("Placed block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.expectedState}")
                         return@listen
                     }
 
+                    pending.stopPending()
+
                     if (pending.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
                         with (pending.context) {
                             placeSound(expectedState.block.item as BlockItem, expectedState, blockPos)

From b5d57bb3f65f52d5700e3908894ffb9bd757471d Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 26 Jul 2025 22:38:10 +0100
Subject: [PATCH 317/364] override load function over init

---
 .../interaction/request/interacting/InteractionManager.kt   | 6 +++++-
 .../interaction/request/inventory/InventoryManager.kt       | 2 ++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index 1cb205235..45139149c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -56,7 +56,9 @@ object InteractionManager : RequestHandler(
     override val blockedPositions
         get() = pendingActions.map { it.context.blockPos }
 
-    init {
+    override fun load(): String {
+        super.load()
+
         listen(priority = Int.MIN_VALUE) {
             activeRequest = null
             interactionsThisTick = 0
@@ -68,6 +70,8 @@ object InteractionManager : RequestHandler(
                 it.input.sneaking = false
             }
         }
+
+        return "Loaded Interaction Manager"
     }
 
     override fun SafeContext.handleRequest(request: InteractRequest) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
index 732660585..21b04aa6d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
@@ -37,6 +37,8 @@ object InventoryManager : RequestHandler(
     var activeRequest: InventoryRequest? = null
 
     override fun load(): String {
+        super.load()
+
         return "Loaded Inventory Manager"
     }
 

From 2da4cefc73dcba5c1bb8764b05af26abbc92eea8 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 26 Jul 2025 23:11:29 +0100
Subject: [PATCH 318/364] make manager swing hand more consistent with the view
 model no swing delay setting

---
 common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt
index 2d6056e92..394295477 100644
--- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt
@@ -51,7 +51,10 @@ fun SafeContext.spawnFakePlayer(
 
 fun SafeContext.swingHand(swingType: BuildConfig.SwingType, hand: Hand) =
     when (swingType) {
-        BuildConfig.SwingType.Vanilla -> player.swingHand(hand)
+        BuildConfig.SwingType.Vanilla -> {
+            swingHandClient(hand)
+            connection.sendPacket(HandSwingC2SPacket(hand))
+        }
         BuildConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(hand))
         BuildConfig.SwingType.Client -> swingHandClient(hand)
     }

From 5e266e326b273d43c72e562e174170dc226a50d8 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 27 Jul 2025 18:19:42 +0100
Subject: [PATCH 319/364] update cancels on each stage close with mask checks

---
 .../interaction/request/breaking/BreakInfo.kt |  5 +-
 .../request/breaking/BreakManager.kt          | 94 +++++++++----------
 .../request/hotbar/HotbarManager.kt           |  2 +-
 .../lambda/module/modules/player/FastBreak.kt |  5 -
 4 files changed, 49 insertions(+), 57 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index eca4cfdb7..9c51269be 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -37,7 +37,7 @@ data class BreakInfo(
     override val pendingInteractionsList get() = request.pendingInteractions
 
     var updatedThisTick = true
-    var updatedProgressThisTick = false
+    var progressedThisTick = false
 
     var breaking = false
     var abandoned = false
@@ -79,11 +79,12 @@ data class BreakInfo(
         updatedThisTick = true
         this.context = context
         request?.let { this.request = it }
+        if (isRedundant) type = BreakType.Secondary
     }
 
     fun tickStats() {
         updatedThisTick = false
-        updatedProgressThisTick = false
+        progressedThisTick = false
     }
 
     fun resetCallbacks() {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index fb9fdd83f..be3a1b615 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -46,13 +46,14 @@ import com.lambda.interaction.request.breaking.BreakManager.breakInfos
 import com.lambda.interaction.request.breaking.BreakManager.breaks
 import com.lambda.interaction.request.breaking.BreakManager.canAccept
 import com.lambda.interaction.request.breaking.BreakManager.cancelBreak
+import com.lambda.interaction.request.breaking.BreakManager.checkForCancels
 import com.lambda.interaction.request.breaking.BreakManager.initNewBreak
 import com.lambda.interaction.request.breaking.BreakManager.instantBreaks
-import com.lambda.interaction.request.breaking.BreakManager.makeRedundant
 import com.lambda.interaction.request.breaking.BreakManager.maxBreaksThisTick
 import com.lambda.interaction.request.breaking.BreakManager.performInstantBreaks
 import com.lambda.interaction.request.breaking.BreakManager.processNewBreaks
 import com.lambda.interaction.request.breaking.BreakManager.processRequest
+import com.lambda.interaction.request.breaking.BreakManager.simulateAbandoned
 import com.lambda.interaction.request.breaking.BreakManager.updateBreakProgress
 import com.lambda.interaction.request.breaking.BreakType.Primary
 import com.lambda.interaction.request.breaking.BreakType.ReBreak
@@ -91,7 +92,8 @@ object BreakManager : RequestHandler(
     TickEvent.Input.Pre,
     TickEvent.Input.Post,
     TickEvent.Player.Post,
-    onOpen = { processRequest(activeRequest) }
+    onOpen = { simulateAbandoned(); processRequest(activeRequest) },
+    onClose = { checkForCancels() }
 ), PositionBlocking {
     private var primaryBreak: BreakInfo?
         get() = breakInfos[0]
@@ -134,41 +136,12 @@ object BreakManager : RequestHandler(
     override fun load(): String {
         super.load()
 
-        listen(priority = Int.MAX_VALUE) {
-            // Cancelled but double breaking so requires break manager to continue the simulation
-            breakInfos
-                .asSequence()
-                .filterNotNull()
-                .filter { it.abandoned && !it.isRedundant }
-                .forEach { info ->
-                    with (info.request) {
-                        info.context.blockPos
-                            .toStructure(TargetState.Empty)
-                            .toBlueprint()
-                            .simulate(player.eyePos, interact, rotation, inventory, build)
-                            .asSequence()
-                            .filterIsInstance()
-                            .sorted()
-                            .let { sim ->
-                                info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
-                            }
-                    }
-                }
-        }
-
         listen(priority = Int.MIN_VALUE) {
             if (breakCooldown > 0) {
                 breakCooldown--
             }
             breakInfos.forEach { info ->
-                info?.apply {
-                    if (isRedundant) updateBreakProgress(this)
-                    else if (!updatedThisTick) {
-                        this.cancelBreak()
-                        return@apply
-                    }
-                    tickStats()
-                }
+                info?.tickStats()
             }
             activeRequest = null
             breaks = mutableListOf()
@@ -305,7 +278,7 @@ object BreakManager : RequestHandler(
                     }
                     .asReversed()
                     .forEach { info ->
-                        if (info.updatedProgressThisTick) return@forEach
+                        if (info.progressedThisTick) return@forEach
                         val minKeepTicks = if (info.isSecondary) {
                             val breakDelta = info.context.cachedState.calcBreakDelta(
                                 player,
@@ -334,6 +307,39 @@ object BreakManager : RequestHandler(
         }
     }
 
+    private fun SafeContext.simulateAbandoned() {
+        // Cancelled but double breaking so requires break manager to continue the simulation
+        breakInfos
+            .asSequence()
+            .filterNotNull()
+            .filter { it.abandoned && !it.isRedundant }
+            .forEach { info ->
+                with (info.request) {
+                    info.context.blockPos
+                        .toStructure(TargetState.Empty)
+                        .toBlueprint()
+                        .simulate(player.eyePos, interact, rotation, inventory, build)
+                        .asSequence()
+                        .filterIsInstance()
+                        .sorted()
+                        .let { sim ->
+                            info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
+                        }
+                }
+            }
+    }
+
+    private fun SafeContext.checkForCancels() {
+        breakInfos
+            .filterNotNull()
+            .asSequence()
+            .filter { !it.updatedThisTick && tickStage in it.breakConfig.breakStageMask }
+            .forEach { info ->
+                if (info.isRedundant && !info.progressedThisTick) updateBreakProgress(info)
+                else info.cancelBreak()
+            }
+    }
+
     /**
      * Filters the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches
      * in positions. If a match is found, the [BreakInfo] is updated with the new context. Otherwise, the break is cancelled.
@@ -362,15 +368,13 @@ object BreakManager : RequestHandler(
                 newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos }?.let { ctx ->
                     if (!info.updatedThisTick || info.abandoned) {
                         info.updateInfo(ctx, request)
-                        if (info.isRedundant) {
-                            info.type = BreakType.Secondary
+                        if (info.isRedundant)
                             info.request.onStart?.invoke(info.context.blockPos)
-                        } else if (info.abandoned) {
+                        else if (info.abandoned) {
                             info.abandoned = false
                             info.request.onStart?.invoke(info.context.blockPos)
-                        } else {
+                        } else
                             info.request.onUpdate?.invoke(info.context.blockPos)
-                        }
                     }
                     newBreaks.remove(ctx)
                     return@forEach
@@ -552,8 +556,6 @@ object BreakManager : RequestHandler(
      *
      * If the user has [BreakConfig.unsafeCancels] enabled, the info is made redundant, and mostly ignored.
      * If not, the break continues.
-     *
-     * @see makeRedundant
      */
     private fun BreakInfo.cancelBreak() =
         runSafe {
@@ -564,7 +566,7 @@ object BreakManager : RequestHandler(
                 request.onCancel?.invoke(context.blockPos)
             } else if (isSecondary) {
                 if (breakConfig.unsafeCancels) {
-                    makeRedundant()
+                    type = BreakType.RedundantSecondary
                     setBreakingTextureStage(player, world, -1)
                     request.onCancel?.invoke(context.blockPos)
                 } else {
@@ -578,13 +580,6 @@ object BreakManager : RequestHandler(
      */
     private fun BreakInfo.nullify() = type.nullify()
 
-    /**
-     * Makes the [BreakInfo] redundant and triggers the [BreakInfo.internalOnCancel] callback
-     */
-    private fun BreakInfo.makeRedundant() {
-        type = BreakType.RedundantSecondary
-    }
-
     /**
      * Nullifies the [BreakInfo] reference in the [breakInfos] array based on the [BreakType]
      */
@@ -603,8 +598,9 @@ object BreakManager : RequestHandler(
      * @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress
      */
     private fun SafeContext.updateBreakProgress(info: BreakInfo): Boolean {
+        info.progressedThisTick = true
+
         val config = info.breakConfig
-        info.updatedProgressThisTick = true
         val ctx = info.context
         val hitResult = ctx.result
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index 37df7d29f..d39cb0936 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -82,7 +82,7 @@ object HotbarManager : RequestHandler(
 
         val sameButLonger = activeRequest?.let { active ->
             request.slot == active.slot && request.keepTicks >= active.keepTicks
-        } ?: false
+        } == true
 
         if (sameButLonger) activeRequest?.let { current ->
             request.swapPauseAge = current.swapPauseAge
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
index 932025d05..dbe31e1af 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
@@ -17,7 +17,6 @@
 
 package com.lambda.module.modules.player
 
-import com.lambda.context.SafeContext
 import com.lambda.event.events.PacketEvent
 import com.lambda.event.events.PlayerEvent
 import com.lambda.event.events.RenderEvent
@@ -28,7 +27,6 @@ import com.lambda.graphics.renderer.esp.builders.buildOutline
 import com.lambda.module.Module
 import com.lambda.module.tag.ModuleTag
 import com.lambda.util.math.lerp
-import com.lambda.util.math.transform
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action
 import net.minecraft.util.math.Box
@@ -61,9 +59,6 @@ object FastBreak : Module(
     private val endOutlineColour by setting("End Outline Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }
     private val outlineWidth by setting("Outline Width", 1f, 0f..3f, 0.1f, "the thickness of the outline") { page == Page.Render && render && renderSetting != RenderSetting.Fill }
 
-
-    private var boxSet = emptySet()
-
     init {
         listen {
             if (it.packet !is PlayerActionC2SPacket

From 7928d32e04bcb6cb4bd3c5d74cc91198011b9e83 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 27 Jul 2025 18:23:35 +0100
Subject: [PATCH 320/364] remove input pre break stage mask from defaults

---
 .../src/main/kotlin/com/lambda/config/groups/BreakSettings.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 039f0716e..139330be7 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -42,7 +42,7 @@ class BreakSettings(
     override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
 //    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
-    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
+    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
     override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() && page == Page.General }
     override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && page == Page.General && swing != SwingMode.None }
     override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { vis() && page == Page.General }

From 41f69efa776b51a685a41c492ca725b93798fea7 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 27 Jul 2025 18:29:10 +0100
Subject: [PATCH 321/364] tick stage check and abort packet when cancelling
 primary breaks

---
 .../lambda/interaction/request/breaking/BreakManager.kt    | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index be3a1b615..62b3c515f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -45,7 +45,6 @@ import com.lambda.interaction.request.breaking.BreakManager.activeRequest
 import com.lambda.interaction.request.breaking.BreakManager.breakInfos
 import com.lambda.interaction.request.breaking.BreakManager.breaks
 import com.lambda.interaction.request.breaking.BreakManager.canAccept
-import com.lambda.interaction.request.breaking.BreakManager.cancelBreak
 import com.lambda.interaction.request.breaking.BreakManager.checkForCancels
 import com.lambda.interaction.request.breaking.BreakManager.initNewBreak
 import com.lambda.interaction.request.breaking.BreakManager.instantBreaks
@@ -347,7 +346,6 @@ object BreakManager : RequestHandler(
      * value is set.
      *
      * @see canAccept
-     * @see cancelBreak
      */
     private fun SafeContext.populateFrom(request: BreakRequest) {
         // Sanitize the new breaks
@@ -361,7 +359,7 @@ object BreakManager : RequestHandler(
             }
             .toMutableList()
 
-        // Update the current break infos or cancel if abandoned
+        // Update the current break infos
         breakInfos
             .filterNotNull()
             .forEach { info ->
@@ -469,7 +467,7 @@ object BreakManager : RequestHandler(
         val breakInfo = BreakInfo(requestCtx, Primary, request)
         primaryBreak?.let { primaryInfo ->
             if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) {
-                if (!primaryInfo.updatedThisTick) {
+                if (!primaryInfo.updatedThisTick && tickStage in primaryInfo.breakConfig.breakStageMask) {
                     primaryInfo.cancelBreak()
                     return@let
                 } else return null
@@ -563,6 +561,7 @@ object BreakManager : RequestHandler(
             if (isPrimary) {
                 nullify()
                 setBreakingTextureStage(player, world, -1)
+                abortBreakPacket(world, interaction)
                 request.onCancel?.invoke(context.blockPos)
             } else if (isSecondary) {
                 if (breakConfig.unsafeCancels) {

From a0cca20be2dc37bb3f8de2c038a76a313640f20b Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 27 Jul 2025 20:21:56 +0100
Subject: [PATCH 322/364] use break manager for fast break

---
 .../com/lambda/config/groups/BreakSettings.kt |   2 +-
 .../construction/context/BreakContext.kt      |   5 +-
 .../construction/simulation/BuildSimulator.kt |   4 +-
 .../request/breaking/BreakConfig.kt           |   2 +-
 .../request/breaking/BreakManager.kt          |   8 +-
 .../request/breaking/BreakRequest.kt          |  21 +--
 .../lambda/module/modules/player/FastBreak.kt | 130 +++++-------------
 7 files changed, 52 insertions(+), 120 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 139330be7..3bc08ce53 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -36,9 +36,9 @@ class BreakSettings(
     override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
     override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed") { vis() && page == Page.General }
     override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
-    override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
     override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
     override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() && page == Page.General }
+    override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
     override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
 //    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
index 0af6dd153..b7248c859 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
@@ -25,7 +25,6 @@ import com.lambda.interaction.request.breaking.BreakRequest
 import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.hotbar.HotbarRequest
 import com.lambda.interaction.request.rotating.RotationRequest
-import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.emptyState
 import net.minecraft.block.BlockState
 import net.minecraft.block.FallingBlock
@@ -38,9 +37,9 @@ data class BreakContext(
     override val result: BlockHitResult,
     override val rotation: RotationRequest,
     override var hotbarIndex: Int,
-    override var cachedState: BlockState,
     var instantBreak: Boolean,
-    val sortMode: BreakConfig.SortMode = TaskFlowModule.build.breaking.sorter
+    override var cachedState: BlockState,
+    val sortMode: BreakConfig.SortMode
 ) : BuildContext() {
     private val baseColor = Color(222, 0, 0, 25)
     private val sideColor = Color(222, 0, 0, 100)
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index a89365f50..7d1140e6d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -697,8 +697,8 @@ object BuildSimulator {
                     blockHit,
                     rotationRequest,
                     player.inventory.selectedSlot,
-                    state,
                     instantBreakable(state, pos, breaking.breakThreshold),
+                    state,
                     breaking.sorter
                 )
                 acc.add(BreakResult.Break(pos, breakContext))
@@ -746,7 +746,7 @@ object BuildSimulator {
         val rotationRequest = RotationRequest(target, rotation)
         val instant = instantBreakable(state, pos, breaking.breakThreshold)
 
-        val breakContext = BreakContext(blockHit, rotationRequest, player.inventory.selectedSlot, state, instant)
+        val breakContext = BreakContext(blockHit, rotationRequest, player.inventory.selectedSlot, instant, state, breaking.sorter)
 
         if (gamemode.isCreative) {
             acc.add(BreakResult.Break(pos, breakContext))
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index 390212d20..371c3045e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -27,9 +27,9 @@ interface BreakConfig : RequestConfig {
     val breakMode: BreakMode
     val sorter: SortMode
     val reBreak: Boolean
-    val unsafeCancels: Boolean
     val breakThreshold: Float
     val doubleBreak: Boolean
+    val unsafeCancels: Boolean
     val fudgeFactor: Int
     //ToDo: Needs a more advanced player simulation implementation to predict the next ticks onGround / submerged status
 //    abstract val desyncFix: Boolean
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 62b3c515f..f2daeefec 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -351,7 +351,7 @@ object BreakManager : RequestHandler(
         // Sanitize the new breaks
         val newBreaks = request.contexts
             .distinctBy { it.blockPos }
-            .filter { ctx -> canAccept(ctx, request.build.breaking) }
+            .filter { ctx -> canAccept(ctx, request.config) }
             .let { acceptable ->
                 acceptable.firstOrNull()?.let { first ->
                     acceptable.filter { it.hotbarIndex == first.hotbarIndex }
@@ -387,7 +387,7 @@ object BreakManager : RequestHandler(
             .filter { !it.instantBreak }
             .toMutableList()
 
-        val breakConfig = request.build.breaking
+        val breakConfig = request.config
         val pendingLimit = (breakConfig.maxPendingBreaks - pendingBreakCount).coerceAtLeast(0)
         maxBreaksThisTick = breakConfig.breaksPerTick.coerceAtMost(pendingLimit)
     }
@@ -425,8 +425,8 @@ object BreakManager : RequestHandler(
             val ctx = iterator.next()
 
             if (!ctx.requestDependencies(request)) return false
-            rotationRequest = if (request.build.breaking.rotateForBreak) ctx.rotation.submit(false) else null
-            if (!rotated || tickStage !in request.build.breaking.breakStageMask) return false
+            rotationRequest = if (request.config.rotateForBreak) ctx.rotation.submit(false) else null
+            if (!rotated || tickStage !in request.config.breakStageMask) return false
 
             val breakInfo = initNewBreak(ctx, request) ?: return false
             updateBreakProgress(breakInfo)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index b734a3fab..9030b041e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -25,6 +25,7 @@ import com.lambda.interaction.request.Request
 import com.lambda.interaction.request.hotbar.HotbarConfig
 import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.rotating.RotationConfig
+import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.BlockUtils.isEmpty
@@ -34,11 +35,11 @@ import net.minecraft.util.math.BlockPos
 data class BreakRequest(
     val contexts: Collection,
     val pendingInteractions: MutableCollection,
-    val build: BuildConfig,
-    val hotbar: HotbarConfig,
-    val rotation: RotationConfig,
-    val inventory: InventoryConfig,
-    val interact: InteractionConfig
+    val build: BuildConfig = TaskFlowModule.build,
+    val hotbar: HotbarConfig = TaskFlowModule.hotbar,
+    val rotation: RotationConfig = TaskFlowModule.rotation,
+    val inventory: InventoryConfig = TaskFlowModule.inventory,
+    val interact: InteractionConfig = TaskFlowModule.interaction
 ) : Request() {
     override val config = build.breaking
     var onStart: ((BlockPos) -> Unit)? = null
@@ -114,11 +115,11 @@ data class BreakRequest(
         fun breakRequest(
             contexts: Collection,
             pendingInteractions: MutableCollection,
-            rotation: RotationConfig,
-            hotbar: HotbarConfig,
-            interact: InteractionConfig,
-            inventory: InventoryConfig,
-            build: BuildConfig,
+            rotation: RotationConfig = TaskFlowModule.rotation,
+            hotbar: HotbarConfig = TaskFlowModule.hotbar,
+            interact: InteractionConfig = TaskFlowModule.interaction,
+            inventory: InventoryConfig = TaskFlowModule.inventory,
+            build: BuildConfig = TaskFlowModule.build,
             builder: RequestBuilder.() -> Unit
         ) = RequestBuilder(contexts, pendingInteractions, rotation, hotbar, interact, inventory, build).apply(builder).build()
     }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
index dbe31e1af..d5abcefd9 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
@@ -17,20 +17,22 @@
 
 package com.lambda.module.modules.player
 
-import com.lambda.event.events.PacketEvent
+import com.lambda.config.groups.BuildSettings
 import com.lambda.event.events.PlayerEvent
-import com.lambda.event.events.RenderEvent
-import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.graphics.renderer.esp.builders.buildFilled
-import com.lambda.graphics.renderer.esp.builders.buildOutline
+import com.lambda.interaction.construction.context.BreakContext
+import com.lambda.interaction.construction.context.BuildContext
+import com.lambda.interaction.request.breaking.BreakRequest
+import com.lambda.interaction.request.rotating.Rotation.Companion.rotation
+import com.lambda.interaction.request.rotating.RotationRequest
+import com.lambda.interaction.request.rotating.visibilty.lookAt
 import com.lambda.module.Module
+import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.module.tag.ModuleTag
-import com.lambda.util.math.lerp
-import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
-import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action
-import net.minecraft.util.math.Box
-import java.awt.Color
+import com.lambda.util.BlockUtils.blockState
+import net.minecraft.util.Hand
+import net.minecraft.util.hit.BlockHitResult
+import java.util.concurrent.ConcurrentLinkedQueue
 
 object FastBreak : Module(
     name = "FastBreak",
@@ -39,100 +41,30 @@ object FastBreak : Module(
         ModuleTag.PLAYER, ModuleTag.WORLD
     )
 ) {
-    private val page by setting("Page", Page.Mining)
+    private val buildConfig = BuildSettings(this)
 
-    private val breakDelay by setting("Break Delay", 5, 0..5, 1, "The tick delay between breaking blocks", unit = " ticks") { page == Page.Mining }
-    private val breakThreshold by setting("Break Threshold", 0.7f, 0.2f..1.0f, 0.1f, "The progress at which the block will break.") { page == Page.Mining }
-
-    private val render by setting("Render", true, "Render block breaking progress")
-    private val renderMode by setting("Render Mode", RenderMode.Out, "The animation style of the renders") { page == Page.Render }
-    private val renderSetting by setting("Render Setting", RenderSetting.Both, "The different ways to draw the renders") { page == Page.Render && render }
-
-    private val fillColourMode by setting("Fill Mode", ColourMode.Dynamic) { page == Page.Render && renderSetting != RenderSetting.Outline }
-    private val staticFillColour by setting("Static Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static fill of the box") { page == Page.Render && render && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Static }
-    private val startFillColour by setting("Start Fill Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start fill of the box") { page == Page.Render && render && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic }
-    private val endFillColour by setting("End Fill Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end fill of the box") { page == Page.Render && render && renderSetting != RenderSetting.Outline && fillColourMode == ColourMode.Dynamic }
-
-    private val outlineColourMode by setting("Outline Mode", ColourMode.Dynamic) { page == Page.Render && renderSetting != RenderSetting.Fill }
-    private val staticOutlineColour by setting("Static Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the static outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Static }
-    private val startOutlineColour by setting("Start Outline Colour", Color(1f, 0f, 0f, 0.3f), "The colour used to render the start outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }
-    private val endOutlineColour by setting("End Outline Colour", Color(0f, 1f, 0f, 0.3f), "The colour used to render the end outline of the box") { page == Page.Render && render && renderSetting != RenderSetting.Fill && outlineColourMode == ColourMode.Dynamic }
-    private val outlineWidth by setting("Outline Width", 1f, 0f..3f, 0.1f, "the thickness of the outline") { page == Page.Render && render && renderSetting != RenderSetting.Fill }
+    private val pendingInteractions = ConcurrentLinkedQueue()
 
     init {
-        listen {
-            if (it.packet !is PlayerActionC2SPacket
-                || it.packet.action != Action.STOP_DESTROY_BLOCK
-            ) return@listen
-
-            connection.sendPacket(
-                PlayerActionC2SPacket(
-                    Action.ABORT_DESTROY_BLOCK,
-                    // For the exploit to work, the position must be outside the player range, so any
-                    // position farther than 6 blocks will work.
-                    // This is only required for grim 2 and potentially grim 3 in the future if they update it
-                    it.packet.pos.up(2024 - 4 - 18),
-                    it.packet.direction
-                )
+        listen { it.cancel() }
+        listen { event ->
+            event.cancel()
+            player.swingHand(Hand.MAIN_HAND)
+
+            val hitResult = mc.crosshairTarget as? BlockHitResult ?: return@listen
+            val pos = event.pos
+            val state = blockState(pos)
+
+            val breakContext = BreakContext(
+                hitResult,
+                RotationRequest(lookAt(player.rotation), TaskFlowModule.rotation),
+                player.inventory.selectedSlot,
+                state.calcBlockBreakingDelta(player, world, pos) >= buildConfig.breaking.breakThreshold,
+                state,
+                buildConfig.breaking.sorter
             )
-        }
-
-        listen {
-            interaction.blockBreakingCooldown = interaction.blockBreakingCooldown.coerceAtMost(breakDelay)
-        }
 
-        listen {
-            it.progress += world.getBlockState(it.pos)
-                .calcBlockBreakingDelta(player, world, it.pos) * (1 - breakThreshold)
+            BreakRequest(setOf(breakContext), pendingInteractions, buildConfig).submit()
         }
-
-        listen { event ->
-            if (!render || !interaction.isBreakingBlock) return@listen
-
-            val pos = interaction.currentBreakingPos
-            val breakDelta = world.getBlockState(pos).calcBlockBreakingDelta(player, world, pos)
-
-            world.getBlockState(pos).getOutlineShape(world, pos).boundingBoxes.forEach {
-                val previousFactor = interaction.currentBreakingProgress - breakDelta
-                val nextFactor = interaction.currentBreakingProgress
-                val factor = lerp(mc.tickDelta, previousFactor, nextFactor).toDouble()
-
-                val fillColour = fillColourMode.block(factor, if (fillColourMode == ColourMode.Static) staticFillColour else startFillColour, endFillColour)
-                val outlineColor = outlineColourMode.block(factor, if (fillColourMode == ColourMode.Static) staticOutlineColour else startOutlineColour, endOutlineColour)
-                val box = renderMode.block(factor, it)
-                    .offset(pos)
-
-                when (renderSetting) {
-                    RenderSetting.Both -> {
-                        event.renderer.buildFilled(box, fillColour)
-                        event.renderer.buildOutline(box, outlineColor)
-                    }
-
-                    RenderSetting.Fill -> event.renderer.buildFilled(box, fillColour)
-                    RenderSetting.Outline -> event.renderer.buildOutline(box, outlineColor)
-                }
-            }
-        }
-    }
-
-    enum class Page {
-        Mining, Render
-    }
-
-    enum class RenderSetting {
-        Both, Fill, Outline
-    }
-
-    enum class ColourMode(val block: (Double, Color, Color) -> Color) {
-        Static({ _, start, _ -> start }),
-        Dynamic({ factor, start, end -> lerp(factor, start, end) })
-    }
-
-    val Box.ofCenter: Box get() = Box(center, center)
-
-    enum class RenderMode(val block: (Double, Box) -> Box) {
-        Out({ factor, box -> lerp(factor, box.ofCenter, box)}),
-        In({ factor, box -> lerp(factor, box, box.ofCenter)}),
-        Static({ _, box -> box });
     }
 }

From 314409f92949da8897dd55fd495b4373a755eb43 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 01:23:36 +0100
Subject: [PATCH 323/364] show interpolated renders between current and
 predicted next tick progress next tick to be more accurate and future-proof
 for per frame rendering

---
 .../request/breaking/BreakManager.kt          | 25 +++++++++++--------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index f2daeefec..e085f4a4e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -69,6 +69,7 @@ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.isNotEmpty
+import com.lambda.util.extension.partialTicks
 import com.lambda.util.item.ItemUtils.block
 import com.lambda.util.math.lerp
 import com.lambda.util.player.gamemode
@@ -198,21 +199,25 @@ object BreakManager : RequestHandler(
                         info.context.blockPos,
                         info.breakConfig,
                         if (!info.isRedundant) player.inventory.getStack(info.context.hotbarIndex) else null
-                    )
+                    ).toDouble()
+                    val currentDelta = info.breakingTicks * breakDelta
+
                     val threshold = if (info.isPrimary) info.breakConfig.breakThreshold else 1f
-                    val progress = (info.breakingTicks * breakDelta).toDouble() / (threshold + (breakDelta * config.fudgeFactor))
-                    val state = info.context.cachedState
-                    val boxes = state.getOutlineShape(world, info.context.blockPos).boundingBoxes.map {
-                        it.offset(info.context.blockPos)
-                    }
+                    val adjustedThreshold = threshold + (breakDelta * config.fudgeFactor)
+
+                    val currentProgress = currentDelta / adjustedThreshold
+                    val nextTicksProgress = (currentDelta + breakDelta) / adjustedThreshold
+                    val interpolatedProgress = lerp(mc.partialTicks, currentProgress, nextTicksProgress)
 
-                    val fillColor = if (config.dynamicFillColor) lerp(progress, config.startFillColor, config.endFillColor)
+                    val fillColor = if (config.dynamicFillColor) lerp(interpolatedProgress, config.startFillColor, config.endFillColor)
                     else config.staticFillColor
-                    val outlineColor = if (config.dynamicOutlineColor) lerp(progress, config.startOutlineColor, config.endOutlineColor)
+                    val outlineColor = if (config.dynamicOutlineColor) lerp(interpolatedProgress, config.startOutlineColor, config.endOutlineColor)
                     else config.staticOutlineColor
 
-                    boxes.forEach boxes@ { box ->
-                        val interpolated = interpolateBox(box, progress, info.breakConfig)
+                    info.context.cachedState.getOutlineShape(world, info.context.blockPos).boundingBoxes.map {
+                        it.offset(info.context.blockPos)
+                    }.forEach boxes@ { box ->
+                        val interpolated = interpolateBox(box, interpolatedProgress, info.breakConfig)
                         if (config.fill) event.renderer.buildFilled(interpolated, fillColor)
                         if (config.outline) event.renderer.buildOutline(interpolated, outlineColor)
                     }

From 05afa476a2ce2153842ab9afc151f29a9c42053a Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 02:03:17 +0100
Subject: [PATCH 324/364] check entity before setting pitch lol

---
 .../java/com/lambda/mixin/render/LivingEntityRendererMixin.java | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
index 5bbf06391..135196df2 100644
--- a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
+++ b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
@@ -17,6 +17,7 @@
 
 package com.lambda.mixin.render;
 
+import com.lambda.Lambda;
 import com.lambda.interaction.request.rotating.RotationManager;
 import net.minecraft.client.render.entity.LivingEntityRenderer;
 import net.minecraft.entity.LivingEntity;
@@ -39,6 +40,7 @@ public class LivingEntityRendererMixin {
      */
     @Redirect(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerp(FFF)F", ordinal = 0), require = 0)
     private float injectRotationPitch(float g, float f, float s) {
+        if ((Object) this != Lambda.getMc().player) return MathHelper.lerp(g, f, s);
         Float headPitch = RotationManager.getHeadPitch();
         if (headPitch != null) {
             return MathHelper.lerp(g, RotationManager.INSTANCE.getPrevServerRotation().getPitchF(), headPitch);

From 0d572019ba5a3bf6556e1cf95867c8f3ec00a549 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 13:18:43 +0100
Subject: [PATCH 325/364] use sequences in the breakContexts function to limit
 collection creation and population spam

---
 .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 6bb271e3e..64957fe9e 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -191,12 +191,15 @@ object PacketMine : Module(
 
     private fun SafeContext.breakContexts(positions: Collection) =
         positions
+            .asSequence()
             .filterNotNull()
             .associateWith { TargetState.State(blockState(it).fluidState.blockState) }
             .toBlueprint()
             .simulate(player.eyePos, interact, rotation, inventory, build)
+            .asSequence()
             .filterIsInstance()
             .map { it.context }
+            .toCollection(mutableListOf())
 
     private fun addBreak(pos: BlockPos) {
         if (breakConfig.doubleBreak && breakPositions[0] != null) {

From bacff64d1fa75c65e2d1bef1b99cabcde5d07672 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 14:58:20 +0100
Subject: [PATCH 326/364] break settings re-order

---
 .../com/lambda/config/groups/BreakSettings.kt | 33 +++++++++++++++----
 .../request/breaking/BreakConfig.kt           | 23 +++++++++----
 2 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 3bc08ce53..e784528ad 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -33,44 +33,63 @@ class BreakSettings(
     vis: () -> Boolean = { true }
 ) : BreakConfig {
     val page by c.setting("Break Page", Page.General, visibility = vis)
+
+    // General
     override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
     override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed") { vis() && page == Page.General }
-    override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
     override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
+    override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
+
+    // Double break
     override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once") { vis() && page == Page.General }
     override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
+
+    // Fixes / Delays
     override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
 //    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
+
+    // Timing
     override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
+
+    // Swing
     override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() && page == Page.General }
     override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && page == Page.General && swing != SwingMode.None }
+
+    // Rotate
     override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { vis() && page == Page.General }
-    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.General }
+
+    // Pending / Post
     override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() && page == Page.General }
-    override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() && page == Page.General }
     override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.General }
-    override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block") { vis() && page == Page.General }
+    override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks") { vis() && page == Page.General }
+
+    // Block
     override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill") { vis() && page == Page.General }
     override val avoidSupporting by c.setting("Avoid Supporting", true, "Avoids breaking the block supporting the player") { vis() && page == Page.General }
     override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.General }
+    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.General }
+
+    // Tool
+    override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block") { vis() && page == Page.General }
     override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.General }
     override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks") { vis() && page == Page.General }
     override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && page == Page.General && forceFortunePickaxe }
 
+    // Cosmetics
     override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() && page == Page.Cosmetic }
     override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() && page == Page.Cosmetic }
     override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() && page == Page.Cosmetic }
-
+    // Modes
     override val renders by c.setting("Renders", true, "Enables the render settings for breaking progress") { vis() && page == Page.Cosmetic }
     override val animation by c.setting("Animation", AnimationMode.Out, "The style of animation used for the box") { vis() && page == Page.Cosmetic && renders }
-
+    // Fill
     override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && page == Page.Cosmetic && renders }
     override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && page == Page.Cosmetic && renders && fill }
     override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill") { vis() && page == Page.Cosmetic && renders && !dynamicFillColor && fill }
     override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill at the start of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
     override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60).brighter(), "The color of the fill at the end of breaking") { vis() && page == Page.Cosmetic && renders && dynamicFillColor && fill }
-
+    // Outline
     override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && page == Page.Cosmetic && renders }
     override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && page == Page.Cosmetic && renders && outline }
     override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && page == Page.Cosmetic && renders && outline }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index 371c3045e..f1ba67c27 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -26,32 +26,41 @@ import java.awt.Color
 interface BreakConfig : RequestConfig {
     val breakMode: BreakMode
     val sorter: SortMode
-    val reBreak: Boolean
     val breakThreshold: Float
+    val reBreak: Boolean
+
     val doubleBreak: Boolean
     val unsafeCancels: Boolean
+
     val fudgeFactor: Int
     //ToDo: Needs a more advanced player simulation implementation to predict the next ticks onGround / submerged status
 //    abstract val desyncFix: Boolean
     val breakDelay: Int
+
     val breakStageMask: Set
+
     val swing: SwingMode
     val swingType: BuildConfig.SwingType
-    val sounds: Boolean
-    val particles: Boolean
-    val breakingTexture: Boolean
+
     val rotateForBreak: Boolean
+
     val breakConfirmation: BreakConfirmationMode
-    val maxPendingBreaks: Int
     val breaksPerTick: Int
-    val suitableToolsOnly: Boolean
+    val maxPendingBreaks: Int
+
     val avoidLiquids: Boolean
     val avoidSupporting: Boolean
     val breakWeakBlocks: Boolean
+    val ignoredBlocks: Set
+
+    val suitableToolsOnly: Boolean
     val forceSilkTouch: Boolean
     val forceFortunePickaxe: Boolean
     val minFortuneLevel: Int
-    val ignoredBlocks: Set
+
+    val sounds: Boolean
+    val particles: Boolean
+    val breakingTexture: Boolean
 
     val renders: Boolean
     val fill: Boolean

From f4716598edfb42154e1c559b99ee9ad04fd7b0db Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 16:25:50 +0100
Subject: [PATCH 327/364] ignore break attempts at already secondary breaking
 blocks

---
 .../main/kotlin/com/lambda/module/modules/player/PacketMine.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 64957fe9e..88ced321e 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -115,7 +115,7 @@ object PacketMine : Module(
                 }
             }
             positions.removeIf { breakPos ->
-                queue && queuePositions.any { it == breakPos }
+                (queue && queuePositions.any { it == breakPos }) || breakPos == breakPositions[1]
             }
             if (positions.isEmpty()) return@listen
             val activeBreaking = if (queue) {

From 157e4661c1dc67f7cbda3ff3fde2a78803431112 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 16:59:13 +0100
Subject: [PATCH 328/364] add closest option for queue and render the order
 colours using the sorted version

---
 .../module/modules/player/PacketMine.kt       | 20 +++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 88ced321e..7f9d064be 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -84,11 +84,18 @@ object PacketMine : Module(
 
     private val breakPositions = arrayOfNulls(2)
     private val queuePositions = ArrayList>()
-    private val queueSorted
+    private val SafeContext.queueSorted
         get() = when (queueOrder) {
             QueueOrder.Standard -> queuePositions
             QueueOrder.Reversed -> queuePositions.asReversed()
-        }.flatten()
+            QueueOrder.Closest -> queuePositions.sortedBy {
+                it.firstOrNull()
+                    ?.toCenterPos()
+                    ?.let { center ->
+                        center distSq player.pos
+                    } ?: Double.MAX_VALUE
+            }
+        }
 
     private var reBreakPos: BlockPos? = null
 
@@ -120,7 +127,7 @@ object PacketMine : Module(
             if (positions.isEmpty()) return@listen
             val activeBreaking = if (queue) {
                 queuePositions.add(positions)
-                breakPositions.toList() + queueSorted
+                breakPositions.toList() + queueSorted.flatten()
             } else {
                 queuePositions.clear()
                 queuePositions.add(positions)
@@ -135,7 +142,7 @@ object PacketMine : Module(
 
         listen {
             if (!attackedThisTick) {
-                requestBreakManager((breakPositions + queueSorted).toList())
+                requestBreakManager((breakPositions + queueSorted.flatten()).toList())
                 if (!breakConfig.reBreak || (reBreakMode != ReBreakMode.Auto && reBreakMode != ReBreakMode.AutoConstant)) return@listen
                 val reBreak = reBreakPos ?: return@listen
                 requestBreakManager(listOf(reBreak), true)
@@ -144,7 +151,7 @@ object PacketMine : Module(
 
         listen { event ->
             if (!renderQueue) return@listen
-            queuePositions.forEachIndexed { index, positions ->
+            queueSorted.forEachIndexed { index, positions ->
                 positions.forEach { pos ->
                     val color = if (dynamicColor) lerp(index / queuePositions.size.toDouble(), startColor, endColor)
                     else staticColor
@@ -260,7 +267,8 @@ object PacketMine : Module(
 
     enum class QueueOrder {
         Standard,
-        Reversed
+        Reversed,
+        Closest
     }
 
     private enum class RenderMode {

From 5bc4afd90d1ce1c3df2d9392f54d1270f60ecff2 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 20:13:00 +0100
Subject: [PATCH 329/364] use main hand stack if nothing else is faster rather
 than first hotbar slot

---
 .../construction/simulation/BuildSimulator.kt | 27 ++++++++++++-------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 7d1140e6d..9dca0505e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -781,17 +781,24 @@ object BuildSimulator {
             return acc
         }
 
-        val matchingStacks = swapCandidates.associateWith { it.matchingStacks(stackSelection) }
-        val (_, toolPair) = matchingStacks.mapValues { (_, stacks) ->
-	        stacks.associateWith { state.calcItemBlockBreakingDelta(player, world, pos, it) }
-                .maxByOrNull { it.value }
-                ?.toPair()
-        }.entries.maxByOrNull { it.value?.second ?: 0f }?.toPair() ?: return acc
-
-        if (toolPair == null) return acc
+        val swapStack = swapCandidates.map { it.matchingStacks(stackSelection) }
+            .asSequence()
+            .flatten()
+            .let { containerStacks ->
+                var bestStack = player.mainHandStack
+                var bestBreakDelta = state.calcItemBlockBreakingDelta(player, world, pos, bestStack)
+                containerStacks.forEach { stack ->
+                    val breakDelta = state.calcItemBlockBreakingDelta(player, world, pos, stack)
+                    if (breakDelta > bestBreakDelta) {
+                        bestBreakDelta = breakDelta
+                        bestStack = stack
+                    }
+                }
+                bestStack
+        }
 
-        breakContext.hotbarIndex = player.hotbar.indexOf(toolPair.first)
-        breakContext.instantBreak = instantBreakable(state, pos, toolPair.first, breaking.breakThreshold)
+        breakContext.hotbarIndex = player.hotbar.indexOf(swapStack)
+        breakContext.instantBreak = instantBreakable(state, pos, swapStack, breaking.breakThreshold)
 	    acc.add(BreakResult.Break(pos, breakContext))
         return acc
     }

From a5dc330cc71427d54ce43f4852221115ebb0262c Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 23:25:06 +0100
Subject: [PATCH 330/364] fix impossible failure and use the accurate wrong
 stack result

---
 .../interaction/construction/simulation/BuildSimulator.kt  | 2 +-
 .../main/kotlin/com/lambda/task/tasks/PlaceContainer.kt    | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 9dca0505e..c3e94ac5b 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -496,7 +496,7 @@ object BuildSimulator {
                         return@forEach
                     }
                     false
-                } ?: true
+                } != false
 
                 run rotate@ {
                     if (!place.axisRotate) {
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index 9c82c30e1..cb9dd7a5e 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -63,7 +63,7 @@ class PlaceContainer @Ta5kBuilder constructor(
         val succeeds = results.filterIsInstance().filter {
             canBeOpened(startStack, it.blockPos, it.context.result.side)
         }
-        val wrongStacks = results.filterIsInstance().filter {
+        val wrongStacks = results.filterIsInstance().filter {
             canBeOpened(startStack, it.blockPos, it.context.result.side)
         }
         (succeeds + wrongStacks).minOrNull()?.let { result ->
@@ -79,9 +79,10 @@ class PlaceContainer @Ta5kBuilder constructor(
             }.finally {
                 success(result.blockPos)
             }.execute(this@PlaceContainer)
-        } ?: {
-            failure("No valid placement found")
+            return
         }
+
+        failure("No valid placement found")
     }
 
     private fun SafeContext.canBeOpened(

From a9663e84e226ec754bffdd396660c3f3258fafb3 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Wed, 30 Jul 2025 23:36:13 +0100
Subject: [PATCH 331/364] cleanup on tick pre in the post action handlers

---
 .../com/lambda/interaction/request/PostActionHandler.kt     | 6 ++++++
 .../com/lambda/interaction/request/breaking/BreakManager.kt | 2 --
 .../interaction/request/interacting/InteractionManager.kt   | 2 --
 .../com/lambda/interaction/request/placing/PlaceManager.kt  | 2 --
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt
index fa31f4d1e..154474219 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt
@@ -19,6 +19,8 @@ package com.lambda.interaction.request
 
 import com.lambda.config.groups.BuildConfig
 import com.lambda.event.events.ConnectionEvent
+import com.lambda.event.events.TickEvent
+import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.interaction.request.breaking.BrokenBlockHandler
 import com.lambda.util.collections.LimitedDecayQueue
@@ -27,6 +29,10 @@ abstract class PostActionHandler {
     abstract val pendingActions: LimitedDecayQueue
 
     init {
+        listen(priority = Int.MAX_VALUE) {
+            pendingActions.cleanUp()
+        }
+
         listenUnsafe(priority = Int.MIN_VALUE) {
             pendingActions.clear()
         }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index e085f4a4e..e003e8544 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -256,8 +256,6 @@ object BreakManager : RequestHandler(
      * @see updateBreakProgress
      */
     private fun SafeContext.processRequest(breakRequest: BreakRequest?) {
-        pendingActions.cleanUp()
-
         repeat(2) {
             breakRequest?.let { request ->
                 if (request.fresh) populateFrom(request)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index 45139149c..ad7706706 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -83,8 +83,6 @@ object InteractionManager : RequestHandler(
     }
 
     fun SafeContext.processRequest(request: InteractRequest) {
-        pendingActions.cleanUp()
-        
         if (request.fresh) populateFrom(request)
 
         if (player.isSneaking) return
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index eecfe0a7b..bad7b13da 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -135,8 +135,6 @@ object PlaceManager : RequestHandler(
      * @see placeBlock
      */
     fun SafeContext.processRequest(request: PlaceRequest) {
-        pendingActions.cleanUp()
-
         if (request.fresh) populateFrom(request)
 
         val iterator = potentialPlacements.iterator()

From 7f78b68f54aa4898f0e62d6a0b36b90f95853f0b Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Thu, 31 Jul 2025 00:09:02 +0100
Subject: [PATCH 332/364] only place shulkers on our y level until we can check
 where we can path to reliably

---
 .../src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index cb9dd7a5e..8327a0790 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -60,11 +60,12 @@ class PlaceContainer @Ta5kBuilder constructor(
                     .simulate(player.eyePos)
             }
 
+        // ToDo: Check based on if we can move the player close enough rather than y level once the custom pathfinder is merged
         val succeeds = results.filterIsInstance().filter {
-            canBeOpened(startStack, it.blockPos, it.context.result.side)
+            canBeOpened(startStack, it.blockPos, it.context.result.side) && it.blockPos.y == player.blockPos.y
         }
         val wrongStacks = results.filterIsInstance().filter {
-            canBeOpened(startStack, it.blockPos, it.context.result.side)
+            canBeOpened(startStack, it.blockPos, it.context.result.side) && it.blockPos.y == player.blockPos.y
         }
         (succeeds + wrongStacks).minOrNull()?.let { result ->
             build(

From c28e38bc2c881d786e8f964f92b842eb47f77245 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Thu, 31 Jul 2025 15:04:19 +0100
Subject: [PATCH 333/364] use ticking blueprints for placing containers

---
 .../blueprint/PropagatingBlueprint.kt         | 13 ++--
 .../blueprint/TickingBlueprint.kt             | 13 ++--
 .../src/main/kotlin/com/lambda/task/Task.kt   |  4 +-
 .../kotlin/com/lambda/task/tasks/BuildTask.kt | 21 +++---
 .../com/lambda/task/tasks/OpenContainer.kt    |  9 +--
 .../com/lambda/task/tasks/PlaceContainer.kt   | 69 +++++++++++--------
 6 files changed, 72 insertions(+), 57 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt
index 854600855..9d5f4ced3 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt
@@ -23,13 +23,14 @@ import com.lambda.util.extension.Structure
 import net.minecraft.util.math.Vec3i
 
 data class PropagatingBlueprint(
-    val onFinish: SafeContext.(Structure) -> Structure = { it },
+    val onFinish: SafeContext.(Structure) -> Structure? = { it },
 ) : Blueprint() {
-    fun next() {
+    fun next() =
         runSafe {
-            structure = onFinish(structure)
+            onFinish(structure)?.also { new ->
+                structure = new
+            }
         }
-    }
 
     override var structure: Structure = emptyMap()
         private set(value) {
@@ -40,14 +41,14 @@ data class PropagatingBlueprint(
     override fun toString() = "Propagating Blueprint at ${center?.toShortString()}"
 
     companion object {
-        fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure = {
+        fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure? = {
             it.map { (pos, state) ->
                 pos.add(offset) to state
             }.toMap()
         }
 
         fun propagatingBlueprint(
-            onFinish: SafeContext.(Structure) -> Structure,
+            onFinish: SafeContext.(Structure) -> Structure?,
         ) = PropagatingBlueprint(onFinish)
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt
index 67b8a4863..a4815d8b6 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt
@@ -23,13 +23,14 @@ import com.lambda.util.extension.Structure
 import net.minecraft.util.math.Vec3i
 
 data class TickingBlueprint(
-    val onTick: SafeContext.(Structure) -> Structure = { it },
+    val onTick: SafeContext.(Structure) -> Structure? = { it },
 ) : Blueprint() {
-    fun tick() {
+    fun tick() =
         runSafe {
-            structure = onTick(structure)
+            onTick(structure)?.also { new ->
+                structure = new
+            }
         }
-    }
 
     override var structure: Structure = emptyMap()
         private set(value) {
@@ -40,14 +41,14 @@ data class TickingBlueprint(
     override fun toString() = "Dynamic Blueprint at ${center?.toShortString()}"
 
     companion object {
-        fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure = {
+        fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure? = {
             it.map { (pos, state) ->
                 pos.add(offset) to state
             }.toMap()
         }
 
         fun tickingBlueprint(
-            onTick: SafeContext.(Structure) -> Structure,
+            onTick: SafeContext.(Structure) -> Structure?,
         ) = TickingBlueprint(onTick)
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt
index b7fe9c73c..dbb55e737 100644
--- a/common/src/main/kotlin/com/lambda/task/Task.kt
+++ b/common/src/main/kotlin/com/lambda/task/Task.kt
@@ -185,9 +185,7 @@ abstract class Task : Nameable, Muteable {
     }
 
     @Ta5kBuilder
-    fun failure(message: String) {
-        failure(IllegalStateException(message))
-    }
+    fun failure(message: String) = failure(IllegalStateException(message))
 
     @Ta5kBuilder
     fun failure(
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index 89b695fa9..0bd1032b6 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -70,7 +70,7 @@ class BuildTask @Ta5kBuilder constructor(
     private val interactionConfig: InteractionConfig = TaskFlowModule.interaction,
     private val inventory: InventoryConfig = TaskFlowModule.inventory,
     private val hotbar: HotbarConfig = TaskFlowModule.hotbar,
-) : Task() {
+) : Task() {
     override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s"
 
     private val pendingInteractions = ConcurrentLinkedQueue()
@@ -89,7 +89,7 @@ class BuildTask @Ta5kBuilder constructor(
         } else null
 
     override fun SafeContext.onStart() {
-        (blueprint as? PropagatingBlueprint)?.next()
+        iteratePropagating()
     }
 
     init {
@@ -115,12 +115,9 @@ class BuildTask @Ta5kBuilder constructor(
                 is BuildResult.Unbreakable,
                 is BuildResult.Restricted,
                 is BuildResult.NoPermission -> {
-                    if (blueprint is PropagatingBlueprint) {
-                        blueprint.next()
-                        return@listen
-                    }
+                    if (iteratePropagating()) return@listen
 
-                    if (finishOnDone) success()
+                    if (finishOnDone) success(blueprint.structure)
                 }
 
                 is BuildResult.NotVisible,
@@ -195,7 +192,9 @@ class BuildTask @Ta5kBuilder constructor(
         }
 
         listen {
-            (blueprint as? TickingBlueprint)?.tick()
+            if (blueprint is TickingBlueprint) {
+                blueprint.tick() ?: failure("Failed to tick the ticking blueprint")
+            }
 
             if (finishOnDone && blueprint.structure.isEmpty()) {
                 failure("Structure is empty")
@@ -234,6 +233,12 @@ class BuildTask @Ta5kBuilder constructor(
                 return true
             } ?: false
 
+    fun iteratePropagating() =
+        if (blueprint is PropagatingBlueprint) {
+            blueprint.next() ?: failure("Failed to propagate the next blueprint")
+            true
+        } else false
+
     companion object {
         @Ta5kBuilder
         fun build(
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
index d7d9faeda..0d5da0633 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
@@ -21,6 +21,7 @@ import com.lambda.config.groups.InteractionConfig
 import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.request.interacting.InteractConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.interaction.request.rotating.visibilty.lookAtBlock
 import com.lambda.module.modules.client.TaskFlowModule
@@ -34,9 +35,9 @@ import net.minecraft.util.math.Direction
 class OpenContainer @Ta5kBuilder constructor(
     private val blockPos: BlockPos,
     private val waitForSlotLoad: Boolean = true,
-    private val rotate: Boolean = true,
     private val rotation: RotationConfig = TaskFlowModule.rotation,
-    private val interact: InteractionConfig = TaskFlowModule.interaction,
+    private val interact: InteractConfig = TaskFlowModule.build.interacting,
+    private val interactionConfig: InteractionConfig = TaskFlowModule.interaction,
     private val sides: Set = Direction.entries.toSet(),
 ) : Task() {
     override val name get() = "${containerState.description(inScope)} at ${blockPos.toShortString()}"
@@ -83,8 +84,8 @@ class OpenContainer @Ta5kBuilder constructor(
         listen {
             if (containerState != State.SCOPING) return@listen
 
-            val target = lookAtBlock(blockPos, sides, config = interact)
-            if (rotate && !target.requestBy(rotation).done) return@listen
+            val target = lookAtBlock(blockPos, sides, config = interactionConfig)
+            if (interact.rotate && !target.requestBy(rotation).done) return@listen
 
             val hitResult = target.hit?.hitIfValid()?.blockResult ?: return@listen
             interaction.interactBlock(player, Hand.MAIN_HAND, hitResult)
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index 8327a0790..0c2968401 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -22,6 +22,7 @@ import com.lambda.config.groups.InteractionConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
 import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint
+import com.lambda.interaction.construction.blueprint.TickingBlueprint.Companion.tickingBlueprint
 import com.lambda.interaction.construction.result.BuildResult
 import com.lambda.interaction.construction.result.PlaceResult
 import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
@@ -32,6 +33,7 @@ import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
 import com.lambda.task.tasks.BuildTask.Companion.build
 import com.lambda.util.BlockUtils.blockPos
+import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.item.ItemUtils.shulkerBoxes
 import net.minecraft.block.ChestBlock
 import net.minecraft.entity.mob.ShulkerEntity
@@ -51,39 +53,46 @@ class PlaceContainer @Ta5kBuilder constructor(
     override val name: String get() = "Placing container ${startStack.name.string}"
 
     override fun SafeContext.onStart() {
-        val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4)
-            .map { it.blockPos }
-            .flatMap {
-                it.blockPos
-                    .toStructure(TargetState.Stack(startStack))
-                    .toBlueprint()
-                    .simulate(player.eyePos)
+        tickingBlueprint { current ->
+            if (current.isNotEmpty() &&
+                current.all { it.value.matches(blockState(it.key), it.key, world) })
+            {
+                return@tickingBlueprint current
             }
 
-        // ToDo: Check based on if we can move the player close enough rather than y level once the custom pathfinder is merged
-        val succeeds = results.filterIsInstance().filter {
-            canBeOpened(startStack, it.blockPos, it.context.result.side) && it.blockPos.y == player.blockPos.y
-        }
-        val wrongStacks = results.filterIsInstance().filter {
-            canBeOpened(startStack, it.blockPos, it.context.result.side) && it.blockPos.y == player.blockPos.y
-        }
-        (succeeds + wrongStacks).minOrNull()?.let { result ->
-            build(
-                build = build,
-                rotation = rotation,
-                interact = interact,
-                inventory = inventory,
-            ) {
-                result.blockPos
-                    .toStructure(TargetState.Stack(startStack))
-                    .toBlueprint()
-            }.finally {
-                success(result.blockPos)
-            }.execute(this@PlaceContainer)
-            return
-        }
+            val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4)
+                .map { it.blockPos }
+                .flatMap {
+                    it.blockPos
+                        .toStructure(TargetState.Stack(startStack))
+                        .toBlueprint()
+                        .simulate(player.eyePos)
+                }
 
-        failure("No valid placement found")
+            // ToDo: Check based on if we can move the player close enough rather than y level once the custom pathfinder is merged
+            val succeeds = results.filterIsInstance().filter {
+                canBeOpened(startStack, it.blockPos, it.context.result.side) && it.blockPos.y == player.blockPos.y
+            }
+            val wrongStacks = results.filterIsInstance().filter {
+                canBeOpened(startStack, it.blockPos, it.context.result.side) && it.blockPos.y == player.blockPos.y
+            }
+            (succeeds + wrongStacks).minOrNull()
+                ?.blockPos
+                ?.toStructure(TargetState.Stack(startStack))
+        }.build(
+            true,
+            false,
+            build = build,
+            rotation = rotation,
+            interact = interact,
+            inventory = inventory
+        ).finally {
+            val pos = it.keys.firstOrNull() ?: run {
+                failure("The returned structure was empty")
+                return@finally
+            }
+            success(pos)
+        }.execute(this@PlaceContainer)
     }
 
     private fun SafeContext.canBeOpened(

From ae4546794cbfdee3110d2c4e8ea102802716730f Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Thu, 31 Jul 2025 20:56:24 +0100
Subject: [PATCH 334/364] pending interaction checks in PlaceContainer

---
 .../kotlin/com/lambda/module/modules/player/PacketMine.kt     | 1 -
 .../src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt   | 4 +++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index 7f9d064be..b882d8052 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -192,7 +192,6 @@ object PacketMine : Module(
             onCancel { removeBreak(it, true) }
             onReBreakStart { reBreakPos = it }
             onReBreak { reBreakPos = it }
-            onItemDrop { _ -> itemDrops++ }
         }.submit()
     }
 
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index 0c2968401..8692397fb 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -27,6 +27,7 @@ import com.lambda.interaction.construction.result.BuildResult
 import com.lambda.interaction.construction.result.PlaceResult
 import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
 import com.lambda.interaction.construction.verify.TargetState
+import com.lambda.interaction.request.ManagerUtils
 import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
@@ -55,7 +56,8 @@ class PlaceContainer @Ta5kBuilder constructor(
     override fun SafeContext.onStart() {
         tickingBlueprint { current ->
             if (current.isNotEmpty() &&
-                current.all { it.value.matches(blockState(it.key), it.key, world) })
+                (current.all { it.value.matches(blockState(it.key), it.key, world) } ||
+                ManagerUtils.positionBlockingManagers.any { it.blockedPositions.isNotEmpty() }))
             {
                 return@tickingBlueprint current
             }

From 674db53a87a6b8577bd301c029accab2ad431cba Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 1 Aug 2025 14:37:51 +0100
Subject: [PATCH 335/364] remove input pre in default hotbar stage mask setting

---
 .../src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
index a65855985..73038317b 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
@@ -29,5 +29,5 @@ class HotbarSettings(
     override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", visibility = vis)
     override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() }
     override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", visibility = vis)
-    override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Pre, TickEvent.Input.Post), "The sub-tick timing at which hotbar actions are performed", visibility = vis)
+    override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Post), "The sub-tick timing at which hotbar actions are performed", visibility = vis)
 }

From e46372579fefa5e19db9538937dac811f402a817 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 1 Aug 2025 20:07:00 +0100
Subject: [PATCH 336/364] selection and speed checks rather than slot
 comparison for new breaks

---
 .../construction/context/BreakContext.kt      |  2 +
 .../construction/simulation/BuildSimulator.kt | 29 +++++++++++--
 .../request/breaking/BreakManager.kt          | 43 +++++++++++++------
 .../request/breaking/BrokenBlockHandler.kt    |  2 +-
 .../lambda/module/modules/player/FastBreak.kt |  2 +
 5 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
index b7248c859..2540e695c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
@@ -20,6 +20,7 @@ package com.lambda.interaction.construction.context
 import com.lambda.context.SafeContext
 import com.lambda.graphics.renderer.esp.DirectionMask
 import com.lambda.graphics.renderer.esp.DirectionMask.exclude
+import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.request.breaking.BreakConfig
 import com.lambda.interaction.request.breaking.BreakRequest
 import com.lambda.interaction.request.hotbar.HotbarManager
@@ -37,6 +38,7 @@ data class BreakContext(
     override val result: BlockHitResult,
     override val rotation: RotationRequest,
     override var hotbarIndex: Int,
+    var itemSelection: StackSelection,
     var instantBreak: Boolean,
     override var cachedState: BlockState,
     val sortMode: BreakConfig.SortMode
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index c3e94ac5b..85e3ab006 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -38,6 +38,7 @@ import com.lambda.interaction.material.StackSelection.Companion.selectStack
 import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial
 import com.lambda.interaction.material.container.MaterialContainer
 import com.lambda.interaction.request.breaking.BreakConfig
+import com.lambda.interaction.request.breaking.BreakManager
 import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.placing.PlaceConfig
 import com.lambda.interaction.request.rotating.Rotation.Companion.rotation
@@ -687,6 +688,7 @@ object BuildSimulator {
             hit.blockResult?.blockPos == pos
         }
 
+        // ToDo: Move this to a location where more of the context parameters can be properly set
         /* the player is buried inside the block */
         if (boxes.any { it.contains(eye) }) {
             currentCast?.blockResult?.let { blockHit ->
@@ -697,6 +699,7 @@ object BuildSimulator {
                     blockHit,
                     rotationRequest,
                     player.inventory.selectedSlot,
+                    StackSelection.EVERYTHING.select(),
                     instantBreakable(state, pos, breaking.breakThreshold),
                     state,
                     breaking.sorter
@@ -746,7 +749,15 @@ object BuildSimulator {
         val rotationRequest = RotationRequest(target, rotation)
         val instant = instantBreakable(state, pos, breaking.breakThreshold)
 
-        val breakContext = BreakContext(blockHit, rotationRequest, player.inventory.selectedSlot, instant, state, breaking.sorter)
+        val breakContext = BreakContext(
+            blockHit,
+            rotationRequest,
+            player.inventory.selectedSlot,
+            StackSelection.EVERYTHING.select(),
+            instant,
+            state,
+            breaking.sorter
+        )
 
         if (gamemode.isCreative) {
             acc.add(BreakResult.Break(pos, breakContext))
@@ -784,6 +795,15 @@ object BuildSimulator {
         val swapStack = swapCandidates.map { it.matchingStacks(stackSelection) }
             .asSequence()
             .flatten()
+            .filter { newStack ->
+                BreakManager.currentStackSelection.filterStack(newStack) &&
+                        BreakManager.currentContext?.run {
+                            val currentStack = player.inventory.getStack(hotbarIndex)
+                            val currentSpeed = cachedState.calcItemBlockBreakingDelta(player, world, blockPos, currentStack)
+                            val newSpeed = cachedState.calcItemBlockBreakingDelta(player, world, blockPos, newStack)
+                            newSpeed >= currentSpeed
+                        } != false
+            }
             .let { containerStacks ->
                 var bestStack = player.mainHandStack
                 var bestBreakDelta = state.calcItemBlockBreakingDelta(player, world, pos, bestStack)
@@ -797,8 +817,11 @@ object BuildSimulator {
                 bestStack
         }
 
-        breakContext.hotbarIndex = player.hotbar.indexOf(swapStack)
-        breakContext.instantBreak = instantBreakable(state, pos, swapStack, breaking.breakThreshold)
+        breakContext.apply {
+            hotbarIndex = player.hotbar.indexOf(swapStack)
+            itemSelection = stackSelection
+            instantBreak = instantBreakable(state, pos, swapStack, breaking.breakThreshold)
+        }
 	    acc.add(BreakResult.Break(pos, breakContext))
         return acc
     }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index e003e8544..06b736543 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -36,6 +36,8 @@ import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.result.BreakResult
 import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
 import com.lambda.interaction.construction.verify.TargetState
+import com.lambda.interaction.material.StackSelection
+import com.lambda.interaction.material.StackSelection.Companion.select
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
 import com.lambda.interaction.request.PositionBlocking
 import com.lambda.interaction.request.RequestHandler
@@ -102,6 +104,14 @@ object BreakManager : RequestHandler(
         get() = breakInfos[1]
         set(value) { breakInfos[1] = value }
     private val breakInfos = arrayOfNulls(2)
+    val currentStackSelection
+        get() = breakInfos
+            .lastOrNull {
+                it?.breakConfig?.doubleBreak == true || it?.isSecondary == true
+            }?.context?.itemSelection
+            ?: StackSelection.EVERYTHING.select()
+    val currentContext
+        get() = breakInfos.lastOrNull()?.context
 
     private val pendingBreakCount get() = breakInfos.count { it != null } + pendingActions.size
     override val blockedPositions
@@ -294,7 +304,7 @@ object BreakManager : RequestHandler(
                         } else 0
                         if (!info.context.requestDependencies(info.request, minKeepTicks)) return@run
                         if (tickStage !in info.breakConfig.breakStageMask) return@forEach
-                        if ((!rotated && info.isPrimary)) return@run
+                        if (!rotated && info.isPrimary) return@run
 
                         updateBreakProgress(info)
                     }
@@ -323,6 +333,7 @@ object BreakManager : RequestHandler(
                         .simulate(player.eyePos, interact, rotation, inventory, build)
                         .asSequence()
                         .filterIsInstance()
+                        .filter { canAccept(it.context) }
                         .sorted()
                         .let { sim ->
                             info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
@@ -354,7 +365,7 @@ object BreakManager : RequestHandler(
         // Sanitize the new breaks
         val newBreaks = request.contexts
             .distinctBy { it.blockPos }
-            .filter { ctx -> canAccept(ctx, request.config) }
+            .filter { ctx -> canAccept(ctx) }
             .let { acceptable ->
                 acceptable.firstOrNull()?.let { first ->
                     acceptable.filter { it.hotbarIndex == first.hotbarIndex }
@@ -398,19 +409,23 @@ object BreakManager : RequestHandler(
     /**
      * @return if the break context can be accepted.
      */
-    private fun SafeContext.canAccept(ctx: BreakContext, breakConfig: BreakConfig): Boolean {
-        if (breakInfos.none { it?.context?.blockPos == ctx.blockPos } && isPosBlocked(ctx.blockPos)) return false
+    private fun SafeContext.canAccept(newCtx: BreakContext): Boolean {
+        if (breakInfos.none { it?.context?.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false
 
-        if (breakConfig.doubleBreak) {
-            breakInfos
-                .firstOrNull { it != null && !it.isRedundant }
-                ?.let { info ->
-                    if (ctx.hotbarIndex != info.context.hotbarIndex) return false
-                }
-        }
+        breakInfos
+            .lastOrNull { it != null && !it.isRedundant && it.breakConfig.doubleBreak }
+            ?.let { info ->
+                val currentCtx = info.context
+                val currentStack = player.inventory.getStack(currentCtx.hotbarIndex)
+                val currentSpeed = currentCtx.cachedState.calcItemBlockBreakingDelta(player, world, currentCtx.blockPos, currentStack)
+                val newStack = player.inventory.getStack(newCtx.hotbarIndex)
+                val newSpeed = currentCtx.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, newStack)
+                if (!currentCtx.itemSelection.filterStack(newStack) || newSpeed < currentSpeed)
+                    return false
+            }
 
-        val blockState = blockState(ctx.blockPos)
-        val hardness = ctx.cachedState.getHardness(world, ctx.blockPos)
+        val blockState = blockState(newCtx.blockPos)
+        val hardness = newCtx.cachedState.getHardness(world, newCtx.blockPos)
 
         return blockState.isNotEmpty && hardness != 600f && hardness != -1f
     }
@@ -469,7 +484,7 @@ object BreakManager : RequestHandler(
 
         val breakInfo = BreakInfo(requestCtx, Primary, request)
         primaryBreak?.let { primaryInfo ->
-            if (!breakInfo.breakConfig.doubleBreak || secondaryBreak != null) {
+            if (!primaryInfo.breakConfig.doubleBreak || secondaryBreak != null) {
                 if (!primaryInfo.updatedThisTick && tickStage in primaryInfo.breakConfig.breakStageMask) {
                     primaryInfo.cancelBreak()
                     return@let
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 2b2f5cda4..f4ee54241 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -56,7 +56,7 @@ object BrokenBlockHandler : PostActionHandler() {
             val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z))
             if (!loaded) return@let
 
-            if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out")
+            if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out with cached state ${info.context.cachedState}")
             else if (!TaskFlowModule.ignoreItemDropWarnings) warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
 
             if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
index d5abcefd9..16e0c4e5f 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
@@ -22,6 +22,7 @@ import com.lambda.event.events.PlayerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.context.BuildContext
+import com.lambda.interaction.material.StackSelection.Companion.select
 import com.lambda.interaction.request.breaking.BreakRequest
 import com.lambda.interaction.request.rotating.Rotation.Companion.rotation
 import com.lambda.interaction.request.rotating.RotationRequest
@@ -59,6 +60,7 @@ object FastBreak : Module(
                 hitResult,
                 RotationRequest(lookAt(player.rotation), TaskFlowModule.rotation),
                 player.inventory.selectedSlot,
+                player.mainHandStack.select(),
                 state.calcBlockBreakingDelta(player, world, pos) >= buildConfig.breaking.breakThreshold,
                 state,
                 buildConfig.breaking.sorter

From 2f28e0a87a078f58f0228f1dd87343d4d57fca8f Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 2 Aug 2025 01:26:25 +0100
Subject: [PATCH 337/364] generally prefer primary breaks item stack for usage,
 but secondary for min keep ticks

---
 .../interaction/request/breaking/BreakInfo.kt | 10 +++----
 .../request/breaking/BreakManager.kt          | 28 +++++++++++--------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 9c51269be..95dbe5647 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -128,11 +128,11 @@ data class BreakInfo(
         }
 }
 
-enum class BreakType(val index: Int) {
-    Primary(0),
-    Secondary(1),
-    RedundantSecondary(2),
-    ReBreak(2);
+enum class BreakType() {
+    Primary,
+    Secondary,
+    RedundantSecondary,
+    ReBreak;
 
     fun getBreakThreshold(breakConfig: BreakConfig) =
         when (this) {
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 06b736543..f3cd5fe16 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -288,21 +288,25 @@ object BreakManager : RequestHandler(
                                 if (instantBreaks.isEmpty()) rotation.submit(false) else rotation
                             }
                     }
+                    .also {
+                        it.firstOrNull()?.let { info ->
+                            val minKeepTicks = secondaryBreak?.let { secondary ->
+                                val breakDelta = secondary.context.cachedState.calcBreakDelta(
+                                    player,
+                                    world,
+                                    secondary.context.blockPos,
+                                    secondary.breakConfig,
+                                    player.inventory.getStack(secondary.context.hotbarIndex)
+                                )
+                                val breakAmount = breakDelta * (secondary.breakingTicks + 1)
+                                if (breakAmount >= 1.0f) 1 else 0
+                            } ?: 0
+                            if (!info.context.requestDependencies(info.request, minKeepTicks)) return@run
+                        }
+                    }
                     .asReversed()
                     .forEach { info ->
                         if (info.progressedThisTick) return@forEach
-                        val minKeepTicks = if (info.isSecondary) {
-                            val breakDelta = info.context.cachedState.calcBreakDelta(
-                                player,
-                                world,
-                                info.context.blockPos,
-                                info.breakConfig,
-                                player.inventory.getStack(info.context.hotbarIndex)
-                            )
-                            val breakAmount = breakDelta * (info.breakingTicks + 1)
-                            if (breakAmount >= 1.0f) 1 else 0
-                        } else 0
-                        if (!info.context.requestDependencies(info.request, minKeepTicks)) return@run
                         if (tickStage !in info.breakConfig.breakStageMask) return@forEach
                         if (!rotated && info.isPrimary) return@run
 

From 7ac89255f5a042c2701d46c4d9fd648a899dd7c2 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 2 Aug 2025 12:29:10 +0100
Subject: [PATCH 338/364] more details in break info warnings and allow any
 block as long as it matches the current breaking stack selection

---
 .../construction/simulation/BuildSimulator.kt | 10 +---
 .../interaction/request/breaking/BreakInfo.kt |  1 +
 .../request/breaking/BreakManager.kt          | 50 +++++++++++--------
 .../request/breaking/BrokenBlockHandler.kt    |  4 +-
 4 files changed, 32 insertions(+), 33 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 85e3ab006..c545b1f66 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -795,15 +795,7 @@ object BuildSimulator {
         val swapStack = swapCandidates.map { it.matchingStacks(stackSelection) }
             .asSequence()
             .flatten()
-            .filter { newStack ->
-                BreakManager.currentStackSelection.filterStack(newStack) &&
-                        BreakManager.currentContext?.run {
-                            val currentStack = player.inventory.getStack(hotbarIndex)
-                            val currentSpeed = cachedState.calcItemBlockBreakingDelta(player, world, blockPos, currentStack)
-                            val newSpeed = cachedState.calcItemBlockBreakingDelta(player, world, blockPos, newStack)
-                            newSpeed >= currentSpeed
-                        } != false
-            }
+            .filter { BreakManager.currentStackSelection.filterStack(it) }
             .let { containerStacks ->
                 var bestStack = player.mainHandStack
                 var bestBreakDelta = state.calcItemBlockBreakingDelta(player, world, pos, bestStack)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 95dbe5647..b3a6281e3 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -38,6 +38,7 @@ data class BreakInfo(
 
     var updatedThisTick = true
     var progressedThisTick = false
+    var serverBreakTicks = 0
 
     var breaking = false
     var abandoned = false
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index f3cd5fe16..e28e0b5a0 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -110,8 +110,6 @@ object BreakManager : RequestHandler(
                 it?.breakConfig?.doubleBreak == true || it?.isSecondary == true
             }?.context?.itemSelection
             ?: StackSelection.EVERYTHING.select()
-    val currentContext
-        get() = breakInfos.lastOrNull()?.context
 
     private val pendingBreakCount get() = breakInfos.count { it != null } + pendingActions.size
     override val blockedPositions
@@ -198,6 +196,12 @@ object BreakManager : RequestHandler(
         }
 
         listen { event ->
+            val activeStack = breakInfos
+                .filterNotNull()
+                .firstOrNull()?.let { info ->
+                    player.inventory.getStack(info.context.hotbarIndex)
+                } ?: return@listen
+
             breakInfos
                 .filterNotNull()
                 .forEach { info ->
@@ -208,7 +212,7 @@ object BreakManager : RequestHandler(
                         world,
                         info.context.blockPos,
                         info.breakConfig,
-                        if (!info.isRedundant) player.inventory.getStack(info.context.hotbarIndex) else null
+                        if (!info.isRedundant) activeStack else null
                     ).toDouble()
                     val currentDelta = info.breakingTicks * breakDelta
 
@@ -290,7 +294,7 @@ object BreakManager : RequestHandler(
                     }
                     .also {
                         it.firstOrNull()?.let { info ->
-                            val minKeepTicks = secondaryBreak?.let { secondary ->
+                            secondaryBreak?.let { secondary ->
                                 val breakDelta = secondary.context.cachedState.calcBreakDelta(
                                     player,
                                     world,
@@ -299,9 +303,17 @@ object BreakManager : RequestHandler(
                                     player.inventory.getStack(secondary.context.hotbarIndex)
                                 )
                                 val breakAmount = breakDelta * (secondary.breakingTicks + 1)
-                                if (breakAmount >= 1.0f) 1 else 0
-                            } ?: 0
-                            if (!info.context.requestDependencies(info.request, minKeepTicks)) return@run
+                                val minKeepTicks = if (breakAmount >= 1.0f) 1 else 0
+                                if (!info.context.requestDependencies(info.request, minKeepTicks)) {
+                                    secondary.serverBreakTicks = 0
+                                    return@run
+                                }
+                                if (minKeepTicks > 0) {
+                                    secondary.serverBreakTicks++
+                                }
+                                return@also
+                            }
+                            if (!info.context.requestDependencies(info.request, 0)) return@run
                         }
                     }
                     .asReversed()
@@ -369,19 +381,13 @@ object BreakManager : RequestHandler(
         // Sanitize the new breaks
         val newBreaks = request.contexts
             .distinctBy { it.blockPos }
-            .filter { ctx -> canAccept(ctx) }
-            .let { acceptable ->
-                acceptable.firstOrNull()?.let { first ->
-                    acceptable.filter { it.hotbarIndex == first.hotbarIndex }
-                } ?: acceptable
-            }
             .toMutableList()
 
         // Update the current break infos
         breakInfos
             .filterNotNull()
             .forEach { info ->
-                newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos }?.let { ctx ->
+                newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos && canAccept(ctx) }?.let { ctx ->
                     if (!info.updatedThisTick || info.abandoned) {
                         info.updateInfo(ctx, request)
                         if (info.isRedundant)
@@ -418,14 +424,9 @@ object BreakManager : RequestHandler(
 
         breakInfos
             .lastOrNull { it != null && !it.isRedundant && it.breakConfig.doubleBreak }
-            ?.let { info ->
-                val currentCtx = info.context
-                val currentStack = player.inventory.getStack(currentCtx.hotbarIndex)
-                val currentSpeed = currentCtx.cachedState.calcItemBlockBreakingDelta(player, world, currentCtx.blockPos, currentStack)
+            ?.let { current ->
                 val newStack = player.inventory.getStack(newCtx.hotbarIndex)
-                val newSpeed = currentCtx.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, newStack)
-                if (!currentCtx.itemSelection.filterStack(newStack) || newSpeed < currentSpeed)
-                    return false
+                if (!current.context.itemSelection.filterStack(newStack)) return false
             }
 
         val blockState = blockState(newCtx.blockPos)
@@ -446,6 +447,8 @@ object BreakManager : RequestHandler(
 
             val ctx = iterator.next()
 
+            if (!canAccept(ctx)) continue
+
             if (!ctx.requestDependencies(request)) return false
             rotationRequest = if (request.config.rotateForBreak) ctx.rotation.submit(false) else null
             if (!rotated || tickStage !in request.config.breakStageMask) return false
@@ -469,6 +472,9 @@ object BreakManager : RequestHandler(
         val iterator = breaks.iterator()
         while (iterator.hasNext()) {
             val ctx = iterator.next()
+
+            if (!canAccept(ctx)) continue
+
             initNewBreak(ctx, request) ?: return false
             iterator.remove()
         }
@@ -723,7 +729,7 @@ object BreakManager : RequestHandler(
         }
 
         val swing = config.swing
-        if (overBreakThreshold) {
+        if (overBreakThreshold && (!info.isSecondary || info.serverBreakTicks >= info.breakConfig.fudgeFactor + 1)) {
             if (info.isPrimary) {
                 onBlockBreak(info)
                 info.stopBreakPacket(world, interaction)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index f4ee54241..87aee2605 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -56,8 +56,8 @@ object BrokenBlockHandler : PostActionHandler() {
             val loaded = world.isChunkLoaded(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z))
             if (!loaded) return@let
 
-            if (!info.broken) warn("${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out with cached state ${info.context.cachedState}")
-            else if (!TaskFlowModule.ignoreItemDropWarnings) warn("${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
+            if (!info.broken) warn("${info.type} ${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out with cached state ${info.context.cachedState}")
+            else if (!TaskFlowModule.ignoreItemDropWarnings) warn("${info.type} ${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out")
 
             if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) {
                 world.setBlockState(info.context.blockPos, info.context.cachedState)

From 3515c2922901be7f7ceb5d4d06b21565259653f4 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 2 Aug 2025 13:44:01 +0100
Subject: [PATCH 339/364] redundancy improvements

---
 .../request/breaking/BreakManager.kt          | 72 +++++++++----------
 .../request/breaking/BrokenBlockHandler.kt    |  1 -
 2 files changed, 33 insertions(+), 40 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index e28e0b5a0..a779fe3da 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -107,7 +107,7 @@ object BreakManager : RequestHandler(
     val currentStackSelection
         get() = breakInfos
             .lastOrNull {
-                it?.breakConfig?.doubleBreak == true || it?.isSecondary == true
+                it?.isRedundant == false && (it.breakConfig.doubleBreak || it.isSecondary)
             }?.context?.itemSelection
             ?: StackSelection.EVERYTHING.select()
 
@@ -172,6 +172,10 @@ object BreakManager : RequestHandler(
                         return@listen
                     }
                     destroyBlock(info)
+                    if (info.isRedundant) {
+                        info.nullify()
+                        return@listen
+                    }
                     info.request.onStop?.invoke(info.context.blockPos)
                     info.internalOnBreak()
                     if (info.callbacksCompleted)
@@ -185,6 +189,7 @@ object BreakManager : RequestHandler(
         listen(priority = Int.MIN_VALUE) {
             if (it.entity !is ItemEntity) return@listen
 
+            // ToDo: Proper item drop prediction system
             ReBreakManager.reBreak?.let { reBreak ->
                 if (matchesBlockItem(reBreak, it.entity)) return@listen
             }
@@ -364,7 +369,15 @@ object BreakManager : RequestHandler(
             .asSequence()
             .filter { !it.updatedThisTick && tickStage in it.breakConfig.breakStageMask }
             .forEach { info ->
-                if (info.isRedundant && !info.progressedThisTick) updateBreakProgress(info)
+                if (info.isRedundant && !info.progressedThisTick) {
+                    val cachedState = info.context.cachedState
+                    if (cachedState.isEmpty || cachedState.isAir) {
+                        info.nullify()
+                        return@forEach
+                    }
+                    info.progressedThisTick = true
+                    info.breakingTicks++
+                }
                 else info.cancelBreak()
             }
     }
@@ -422,12 +435,8 @@ object BreakManager : RequestHandler(
     private fun SafeContext.canAccept(newCtx: BreakContext): Boolean {
         if (breakInfos.none { it?.context?.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false
 
-        breakInfos
-            .lastOrNull { it != null && !it.isRedundant && it.breakConfig.doubleBreak }
-            ?.let { current ->
-                val newStack = player.inventory.getStack(newCtx.hotbarIndex)
-                if (!current.context.itemSelection.filterStack(newStack)) return false
-            }
+        if (!currentStackSelection.filterStack(player.inventory.getStack(newCtx.hotbarIndex)))
+            return false
 
         val blockState = blockState(newCtx.blockPos)
         val hardness = newCtx.cachedState.getHardness(world, newCtx.blockPos)
@@ -536,29 +545,25 @@ object BreakManager : RequestHandler(
      */
     private fun SafeContext.onBlockBreak(info: BreakInfo) {
         info.request.onStop?.invoke(info.context.blockPos)
-        if (info.isRedundant) {
-            info.startPending()
-        } else {
-            when (info.breakConfig.breakConfirmation) {
-                BreakConfirmationMode.None -> {
-                    destroyBlock(info)
-                    info.internalOnBreak()
-                    if (!info.callbacksCompleted) {
-                        info.startPending()
-                    } else {
-                        ReBreakManager.offerReBreak(info)
-                    }
-                }
-                BreakConfirmationMode.BreakThenAwait -> {
-                    destroyBlock(info)
-                    info.startPending()
-                }
-                BreakConfirmationMode.AwaitThenBreak -> {
+        when (info.breakConfig.breakConfirmation) {
+            BreakConfirmationMode.None -> {
+                destroyBlock(info)
+                info.internalOnBreak()
+                if (!info.callbacksCompleted) {
                     info.startPending()
+                } else {
+                    ReBreakManager.offerReBreak(info)
                 }
             }
-            breaksThisTick++
+            BreakConfirmationMode.BreakThenAwait -> {
+                destroyBlock(info)
+                info.startPending()
+            }
+            BreakConfirmationMode.AwaitThenBreak -> {
+                info.startPending()
+            }
         }
+        breaksThisTick++
         info.nullify()
     }
 
@@ -632,10 +637,6 @@ object BreakManager : RequestHandler(
         val hitResult = ctx.result
 
         if (gamemode.isCreative && world.worldBorder.contains(ctx.blockPos) && info.breaking) {
-            if (info.isRedundant) {
-                onBlockBreak(info)
-                return true
-            }
             breakCooldown = config.breakDelay
             lastPosStarted = ctx.blockPos
             onBlockBreak(info)
@@ -682,7 +683,7 @@ object BreakManager : RequestHandler(
         val blockState = blockState(ctx.blockPos)
         if (blockState.isEmpty || blockState.isAir) {
             info.nullify()
-            if (!info.isRedundant) info.request.onCancel?.invoke(info.context.blockPos)
+            info.request.onCancel?.invoke(info.context.blockPos)
             return false
         }
 
@@ -696,13 +697,6 @@ object BreakManager : RequestHandler(
 
         val overBreakThreshold = progress >= info.getBreakThreshold()
 
-        if (info.isRedundant) {
-            if (overBreakThreshold) {
-                onBlockBreak(info)
-            }
-            return true
-        }
-
         if (config.sounds) {
             if (info.soundsCooldown % 4.0f == 0.0f) {
                 val blockSoundGroup = blockState.soundGroup
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 87aee2605..230ad09f1 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -93,7 +93,6 @@ object BrokenBlockHandler : PostActionHandler() {
                 }
 
                 if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak
-                    || pending.isRedundant
                     || (pending.isReBreaking && !pending.breakConfig.reBreak)
                     ) {
                     destroyBlock(pending)

From 4b6f41028054f515f5f77dc592563a65b36899b6 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 2 Aug 2025 14:52:02 +0100
Subject: [PATCH 340/364] subject currently held item to the same scrutiny as
 other items

---
 .../interaction/construction/simulation/BuildSimulator.kt | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index c545b1f66..9facab3df 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -797,11 +797,13 @@ object BuildSimulator {
             .flatten()
             .filter { BreakManager.currentStackSelection.filterStack(it) }
             .let { containerStacks ->
-                var bestStack = player.mainHandStack
-                var bestBreakDelta = state.calcItemBlockBreakingDelta(player, world, pos, bestStack)
+                var bestStack = ItemStack.EMPTY
+                var bestBreakDelta = -1f
                 containerStacks.forEach { stack ->
                     val breakDelta = state.calcItemBlockBreakingDelta(player, world, pos, stack)
-                    if (breakDelta > bestBreakDelta) {
+                    if (breakDelta > bestBreakDelta ||
+                        (stack == player.mainHandStack && breakDelta >= bestBreakDelta))
+                    {
                         bestBreakDelta = breakDelta
                         bestStack = stack
                     }

From 37786be02724f749ae802dbabf08723e02ef6429 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 3 Aug 2025 23:52:12 +0100
Subject: [PATCH 341/364] swap mode setting

---
 .../com/lambda/config/groups/BreakSettings.kt    |  5 ++++-
 .../construction/context/BreakContext.kt         |  2 +-
 .../construction/simulation/BuildSimulator.kt    |  2 +-
 .../interaction/request/breaking/BreakConfig.kt  | 12 ++++++++++++
 .../interaction/request/breaking/BreakInfo.kt    | 16 ++++++++++++++++
 .../interaction/request/breaking/BreakManager.kt | 13 +++++++------
 6 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index e784528ad..1463b7ddb 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -37,7 +37,6 @@ class BreakSettings(
     // General
     override val breakMode by c.setting("Break Mode", BreakMode.Packet) { vis() && page == Page.General }
     override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed") { vis() && page == Page.General }
-    override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
     override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once") { vis() && page == Page.General }
 
     // Double break
@@ -45,6 +44,7 @@ class BreakSettings(
     override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes") { vis() && page == Page.General }
 
     // Fixes / Delays
+    override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
     override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
 //    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
@@ -52,6 +52,9 @@ class BreakSettings(
     // Timing
     override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
 
+    // Swap
+    override val swapMode by c.setting("Swap Mode", BreakConfig.SwapMode.End, "Decides when to swap to the best suited tool when breaking a block") { vis() && page == Page.General }
+
     // Swing
     override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() && page == Page.General }
     override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && page == Page.General && swing != SwingMode.None }
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
index 2540e695c..60a713b2c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt
@@ -77,7 +77,7 @@ data class BreakContext(
         withState(cachedState, blockPos, sideColor, result.side)
     }
 
-    fun requestDependencies(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean =
+    fun requestSwap(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean =
         HotbarRequest(
             hotbarIndex,
             breakRequest.hotbar,
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 9facab3df..d6fe25cc9 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -814,7 +814,7 @@ object BuildSimulator {
         breakContext.apply {
             hotbarIndex = player.hotbar.indexOf(swapStack)
             itemSelection = stackSelection
-            instantBreak = instantBreakable(state, pos, swapStack, breaking.breakThreshold)
+            instantBreak = instantBreakable(state, pos, if (breaking.swapMode.isEnabled()) swapStack else player.mainHandStack, breaking.breakThreshold)
         }
 	    acc.add(BreakResult.Break(pos, breakContext))
         return acc
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
index f1ba67c27..3b56f3cc4 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt
@@ -39,6 +39,8 @@ interface BreakConfig : RequestConfig {
 
     val breakStageMask: Set
 
+    val swapMode: SwapMode
+
     val swing: SwingMode
     val swingType: BuildConfig.SwingType
 
@@ -90,6 +92,16 @@ interface BreakConfig : RequestConfig {
         Random
     }
 
+    enum class SwapMode {
+        None,
+        Start,
+        End,
+        StartAndEnd,
+        Constant;
+
+        fun isEnabled() = this != None
+    }
+
     enum class SwingMode {
         Constant,
         StartAndEnd,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index b3a6281e3..e94d36349 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -27,6 +27,7 @@ import net.minecraft.entity.ItemEntity
 import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action
+import net.minecraft.world.WorldView
 
 data class BreakInfo(
     override var context: BreakContext,
@@ -93,6 +94,21 @@ data class BreakInfo(
         item = null
     }
 
+    fun shouldSwap(player: ClientPlayerEntity, world: WorldView): Boolean {
+        val item = player.inventory.getStack(context.hotbarIndex)
+        val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
+        val breakProgress = breakDelta * ((breakingTicks + 1) - breakConfig.fudgeFactor).let {
+            if (isSecondary) it + 1 else it
+        }
+        return when (breakConfig.swapMode) {
+            BreakConfig.SwapMode.None -> false
+            BreakConfig.SwapMode.Start -> !breaking
+            BreakConfig.SwapMode.End -> breakProgress >= getBreakThreshold()
+            BreakConfig.SwapMode.StartAndEnd -> !breaking || breakProgress >= getBreakThreshold()
+            BreakConfig.SwapMode.Constant -> true
+        }
+    }
+
     fun setBreakingTextureStage(
         player: ClientPlayerEntity,
         world: ClientWorld,
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index a779fe3da..227167898 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -217,7 +217,7 @@ object BreakManager : RequestHandler(
                         world,
                         info.context.blockPos,
                         info.breakConfig,
-                        if (!info.isRedundant) activeStack else null
+                        if (!info.isRedundant && info.breakConfig.swapMode.isEnabled()) activeStack else null
                     ).toDouble()
                     val currentDelta = info.breakingTicks * breakDelta
 
@@ -298,6 +298,8 @@ object BreakManager : RequestHandler(
                             }
                     }
                     .also {
+                        if (breakInfos.none { it?.shouldSwap(player, world) == true }) return@also
+
                         it.firstOrNull()?.let { info ->
                             secondaryBreak?.let { secondary ->
                                 val breakDelta = secondary.context.cachedState.calcBreakDelta(
@@ -307,9 +309,9 @@ object BreakManager : RequestHandler(
                                     secondary.breakConfig,
                                     player.inventory.getStack(secondary.context.hotbarIndex)
                                 )
-                                val breakAmount = breakDelta * (secondary.breakingTicks + 1)
+                                val breakAmount = breakDelta * ((secondary.breakingTicks - secondary.breakConfig.fudgeFactor) + 1)
                                 val minKeepTicks = if (breakAmount >= 1.0f) 1 else 0
-                                if (!info.context.requestDependencies(info.request, minKeepTicks)) {
+                                if (!info.context.requestSwap(info.request, minKeepTicks)) {
                                     secondary.serverBreakTicks = 0
                                     return@run
                                 }
@@ -318,7 +320,7 @@ object BreakManager : RequestHandler(
                                 }
                                 return@also
                             }
-                            if (!info.context.requestDependencies(info.request, 0)) return@run
+                            if (!info.context.requestSwap(info.request, 0)) return@run
                         }
                     }
                     .asReversed()
@@ -458,13 +460,12 @@ object BreakManager : RequestHandler(
 
             if (!canAccept(ctx)) continue
 
-            if (!ctx.requestDependencies(request)) return false
+            if (request.build.breaking.swapMode.isEnabled() && !ctx.requestSwap(request)) return false
             rotationRequest = if (request.config.rotateForBreak) ctx.rotation.submit(false) else null
             if (!rotated || tickStage !in request.config.breakStageMask) return false
 
             val breakInfo = initNewBreak(ctx, request) ?: return false
             updateBreakProgress(breakInfo)
-            breaksThisTick++
             iterator.remove()
         }
         return true

From d6601c409003265982022be5ea2f4b8ee3ebb011 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 4 Aug 2025 01:20:02 +0100
Subject: [PATCH 342/364] rebreak adjustments

---
 .../interaction/request/breaking/BreakManager.kt |  2 +-
 .../request/breaking/ReBreakManager.kt           | 16 +++++++++++-----
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 227167898..27b446c61 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -94,7 +94,7 @@ object BreakManager : RequestHandler(
     TickEvent.Input.Pre,
     TickEvent.Input.Post,
     TickEvent.Player.Post,
-    onOpen = { simulateAbandoned(); processRequest(activeRequest) },
+    onOpen = { processRequest(activeRequest); simulateAbandoned() },
     onClose = { checkForCancels() }
 ), PositionBlocking {
     private var primaryBreak: BreakInfo?
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
index 7d5e4ea55..8fc1f500e 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
@@ -32,13 +32,19 @@ object ReBreakManager {
     var reBreak: BreakInfo? = null
 
     init {
-        listen(priority = Int.MIN_VALUE) {
-            reBreak?.apply {
-                breakingTicks++
-                tickStats()
+        listen(priority = Int.MIN_VALUE) {
+            reBreak?.run {
+                if (!progressedThisTick) {
+                    breakingTicks++
+                    progressedThisTick = true
+                }
             }
         }
 
+        listen(priority = Int.MIN_VALUE) {
+            reBreak?.tickStats()
+        }
+
         listenUnsafe(priority = Int.MIN_VALUE) {
             reBreak = null
         }
@@ -72,7 +78,7 @@ object ReBreakManager {
             val context = info.context
 
             val breakProgress = context.cachedState.calcBlockBreakingDelta(player, world, context.blockPos)
-            return@runSafe if (info.breakingTicks * breakProgress >= info.breakConfig.breakThreshold) {
+            return@runSafe if ((info.breakingTicks - info.breakConfig.fudgeFactor) * breakProgress >= info.breakConfig.breakThreshold) {
                 if (context.cachedState.isEmpty) {
                     return@runSafe ReBreakResult.Ignored
                 }

From 232c21755e5aa064c02109788b2e42adedfcacec Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 4 Aug 2025 02:32:43 +0100
Subject: [PATCH 343/364] move re break checks to the processRequest function
 and decouple checks for if a break info can be re broken and the act of re
 breaking

---
 .../interaction/request/breaking/BreakInfo.kt | 11 ++++-
 .../request/breaking/BreakManager.kt          | 42 +++++++++--------
 .../request/breaking/ReBreakManager.kt        | 47 ++++++++++---------
 3 files changed, 58 insertions(+), 42 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index e94d36349..9350fb7f2 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -19,6 +19,7 @@ package com.lambda.interaction.request.breaking
 
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.request.ActionInfo
+import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.client.network.ClientPlayerInteractionManager
@@ -41,6 +42,12 @@ data class BreakInfo(
     var progressedThisTick = false
     var serverBreakTicks = 0
 
+    var couldReBreak = lazy {
+        runSafe {
+            ReBreakManager.couldReBreak(this@BreakInfo, player, world)
+        } == true
+    }
+
     var breaking = false
     var abandoned = false
     var breakingTicks = 0
@@ -100,7 +107,9 @@ data class BreakInfo(
         val breakProgress = breakDelta * ((breakingTicks + 1) - breakConfig.fudgeFactor).let {
             if (isSecondary) it + 1 else it
         }
-        return when (breakConfig.swapMode) {
+        return if (couldReBreak.value)
+            breakConfig.swapMode.isEnabled()
+        else when (breakConfig.swapMode) {
             BreakConfig.SwapMode.None -> false
             BreakConfig.SwapMode.Start -> !breaking
             BreakConfig.SwapMode.End -> breakProgress >= getBreakThreshold()
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 27b446c61..7a6fad0ae 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -329,6 +329,28 @@ object BreakManager : RequestHandler(
                         if (tickStage !in info.breakConfig.breakStageMask) return@forEach
                         if (!rotated && info.isPrimary) return@run
 
+                        if (info.couldReBreak.value) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
+                            is ReBreakResult.StillBreaking -> {
+                                primaryBreak = reBreakResult.breakInfo.apply {
+                                    type = Primary
+                                    ReBreakManager.clearReBreak()
+                                    request.onStart?.invoke(info.context.blockPos)
+                                }
+
+                                primaryBreak?.let { primary ->
+                                    updateBreakProgress(primary)
+                                }
+                                return@forEach
+                            }
+                            is ReBreakResult.ReBroke -> {
+                                info.type = ReBreak
+                                info.nullify()
+                                info.request.onReBreak?.invoke(info.context.blockPos)
+                                return@forEach
+                            }
+                            else -> {}
+                        }
+
                         updateBreakProgress(info)
                     }
             }
@@ -649,26 +671,6 @@ object BreakManager : RequestHandler(
         }
 
         if (!info.breaking) {
-            when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
-                is ReBreakResult.StillBreaking -> {
-                    primaryBreak = reBreakResult.breakInfo.apply {
-                        type = Primary
-                        ReBreakManager.clearReBreak()
-                        request.onStart?.invoke(ctx.blockPos)
-                    }
-
-                    return primaryBreak?.let { primary ->
-                        updateBreakProgress(primary)
-                    } == true
-                }
-                is ReBreakResult.ReBroke -> {
-                    info.type = ReBreak
-                    info.nullify()
-                    info.request.onReBreak?.invoke(info.context.blockPos)
-                    return true
-                }
-                else -> {}
-            }
             if (!startBreaking(info)) {
                 info.nullify()
                 info.request.onCancel?.invoke(info.context.blockPos)
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
index 8fc1f500e..663eb8199 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
@@ -24,9 +24,11 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
 import com.lambda.threading.runSafe
-import com.lambda.util.BlockUtils.isEmpty
+import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import com.lambda.util.player.swingHand
+import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.util.Hand
+import net.minecraft.world.BlockView
 
 object ReBreakManager {
     var reBreak: BreakInfo? = null
@@ -65,35 +67,38 @@ object ReBreakManager {
         reBreak = null
     }
 
+    fun couldReBreak(info: BreakInfo, player: ClientPlayerEntity, world: BlockView) =
+        reBreak?.let { reBreak ->
+            val stack = if (info.breakConfig.swapMode.isEnabled())
+                player.inventory.getStack(info.context.hotbarIndex)
+            else player.mainHandStack
+            val breakDelta = info.context.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, stack)
+            reBreak.breakConfig.reBreak &&
+                    info.context.blockPos == reBreak.context.blockPos &&
+                    !reBreak.updatedThisTick &&
+                    ((reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold)
+        } == true
+
     fun handleUpdate(ctx: BreakContext, breakRequest: BreakRequest) =
         runSafe {
-            val info = reBreak ?: return@runSafe ReBreakResult.Ignored
-
-            if (info.context.blockPos != ctx.blockPos || !info.breakConfig.reBreak) {
-                return@runSafe ReBreakResult.Ignored
-            }
-            if (info.updatedThisTick) return@runSafe ReBreakResult.ReBroke
-            info.updateInfo(ctx, breakRequest)
+            val reBreak = this@ReBreakManager.reBreak ?: return@runSafe ReBreakResult.Ignored
 
-            val context = info.context
+            reBreak.updateInfo(ctx, breakRequest)
 
-            val breakProgress = context.cachedState.calcBlockBreakingDelta(player, world, context.blockPos)
-            return@runSafe if ((info.breakingTicks - info.breakConfig.fudgeFactor) * breakProgress >= info.breakConfig.breakThreshold) {
-                if (context.cachedState.isEmpty) {
-                    return@runSafe ReBreakResult.Ignored
-                }
-                if (info.breakConfig.breakConfirmation != BreakConfig.BreakConfirmationMode.AwaitThenBreak) {
-                    destroyBlock(info)
+            val context = reBreak.context
+            val breakDelta = context.cachedState.calcBlockBreakingDelta(player, world, context.blockPos)
+            return@runSafe if ((reBreak.breakingTicks - reBreak.breakConfig.fudgeFactor) * breakDelta >= reBreak.breakConfig.breakThreshold) {
+                if (reBreak.breakConfig.breakConfirmation != BreakConfig.BreakConfirmationMode.AwaitThenBreak) {
+                    destroyBlock(reBreak)
                 }
-                info.stopBreakPacket(world, interaction)
-                val swing = info.breakConfig.swing
-                if (swing.isEnabled() && swing != BreakConfig.SwingMode.Start) {
-                    swingHand(info.breakConfig.swingType, Hand.MAIN_HAND)
+                reBreak.stopBreakPacket(world, interaction)
+                if (reBreak.breakConfig.swing.isEnabled()) {
+                    swingHand(reBreak.breakConfig.swingType, Hand.MAIN_HAND)
                 }
                 BreakManager.breaksThisTick++
                 ReBreakResult.ReBroke
             } else {
-                ReBreakResult.StillBreaking(info)
+                ReBreakResult.StillBreaking(reBreak)
             }
         }
 }
\ No newline at end of file

From 98561c22ca61c586e511a9a3428ac013fed6ef5d Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 4 Aug 2025 02:45:49 +0100
Subject: [PATCH 344/364] use updatableLazy

---
 .../com/lambda/interaction/request/breaking/BreakInfo.kt     | 5 +++--
 .../com/lambda/interaction/request/breaking/BreakManager.kt  | 5 ++++-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 9350fb7f2..f0b1dad4c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -21,6 +21,7 @@ import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.request.ActionInfo
 import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
+import com.lambda.util.collections.updatableLazy
 import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.client.network.ClientPlayerInteractionManager
 import net.minecraft.client.world.ClientWorld
@@ -42,7 +43,7 @@ data class BreakInfo(
     var progressedThisTick = false
     var serverBreakTicks = 0
 
-    var couldReBreak = lazy {
+    var couldReBreak = updatableLazy {
         runSafe {
             ReBreakManager.couldReBreak(this@BreakInfo, player, world)
         } == true
@@ -107,7 +108,7 @@ data class BreakInfo(
         val breakProgress = breakDelta * ((breakingTicks + 1) - breakConfig.fudgeFactor).let {
             if (isSecondary) it + 1 else it
         }
-        return if (couldReBreak.value)
+        return if (couldReBreak.value == true)
             breakConfig.swapMode.isEnabled()
         else when (breakConfig.swapMode) {
             BreakConfig.SwapMode.None -> false
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 7a6fad0ae..1ae577243 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -297,6 +297,9 @@ object BreakManager : RequestHandler(
                                 if (instantBreaks.isEmpty()) rotation.submit(false) else rotation
                             }
                     }
+                    .also  {
+                        it.forEach { it.couldReBreak.update() }
+                    }
                     .also {
                         if (breakInfos.none { it?.shouldSwap(player, world) == true }) return@also
 
@@ -329,7 +332,7 @@ object BreakManager : RequestHandler(
                         if (tickStage !in info.breakConfig.breakStageMask) return@forEach
                         if (!rotated && info.isPrimary) return@run
 
-                        if (info.couldReBreak.value) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
+                        if (info.couldReBreak.value == true) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
                             is ReBreakResult.StillBreaking -> {
                                 primaryBreak = reBreakResult.breakInfo.apply {
                                     type = Primary

From d8de058c6605463856ce36da9d4869c650ec9631 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 4 Aug 2025 11:57:05 +0100
Subject: [PATCH 345/364] fluid check to prevent placing when not necessary

---
 .../interaction/construction/simulation/BuildSimulator.kt     | 4 +++-
 common/src/main/kotlin/com/lambda/util/BlockUtils.kt          | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index d6fe25cc9..f59bc22a2 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -58,6 +58,7 @@ import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
+import com.lambda.util.BlockUtils.hasFluid
 import com.lambda.util.BlockUtils.instantBreakable
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotEmpty
@@ -345,7 +346,8 @@ object BuildSimulator {
         val statePromoting = currentState.block is SlabBlock && targetState.matches(currentState, pos, world, preProcessing.ignore)
         // If the target state is air then the only possible blocks it could place are to remove liquids so we use the Solid TargetState
         val nextTargetState = if (targetState is TargetState.Air) {
-            TargetState.Solid
+            if (currentState.hasFluid) TargetState.Solid
+            else return acc
         } else if (targetState.isEmpty()) {
             // Otherwise if the target state is empty, there's no situation where placement would be required so we can return
             return acc
diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
index 3272911bd..662613245 100644
--- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
@@ -313,6 +313,7 @@ object BlockUtils {
 
     val BlockState.isEmpty get() = matches(emptyState)
     val BlockState.isNotEmpty get() = !isEmpty
+    val BlockState.hasFluid get() = !fluidState.isEmpty
     val BlockState.emptyState: BlockState get() = fluidState.blockState
     fun isBroken(oldState: BlockState, newState: BlockState) = oldState.isNotEmpty && oldState.emptyState.matches(newState)
     fun isNotBroken(oldState: BlockState, newState: BlockState) = !isBroken(oldState, newState)

From 49d6e9f4702ad06e3522f55130cff5a2dd532af5 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 4 Aug 2025 14:01:38 +0100
Subject: [PATCH 346/364] use main hand stack for render progress when swap
 mode is set to start as the tool won't be present again to break the block

---
 .../com/lambda/interaction/request/breaking/BreakInfo.kt      | 4 +++-
 .../com/lambda/interaction/request/breaking/BreakManager.kt   | 3 ++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index f0b1dad4c..c0ea46392 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -128,7 +128,9 @@ data class BreakInfo(
     }
 
     private fun getBreakTextureProgress(player: PlayerEntity, world: ClientWorld): Int {
-        val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, player.mainHandStack)
+        val swapMode = breakConfig.swapMode
+        val item = if (swapMode.isEnabled() && swapMode != BreakConfig.SwapMode.Start) player.inventory.getStack(context.hotbarIndex) else player.mainHandStack
+        val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
         val progress = (breakDelta * breakingTicks) / (getBreakThreshold() + (breakDelta * breakConfig.fudgeFactor))
         return if (progress > 0.0f) (progress * 10.0f).toInt().coerceAtMost(10) else -1
     }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 1ae577243..b11fbb19c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -212,12 +212,13 @@ object BreakManager : RequestHandler(
                 .forEach { info ->
                     val config = info.breakConfig
                     if (!config.renders) return@listen
+                    val swapMode = info.breakConfig.swapMode
                     val breakDelta = info.context.cachedState.calcBreakDelta(
                         player,
                         world,
                         info.context.blockPos,
                         info.breakConfig,
-                        if (!info.isRedundant && info.breakConfig.swapMode.isEnabled()) activeStack else null
+                        if (!info.isRedundant && swapMode.isEnabled() && swapMode != BreakConfig.SwapMode.Start) activeStack else null
                     ).toDouble()
                     val currentDelta = info.breakingTicks * breakDelta
 

From 445f360d4c53fabdcbd8d69e5edf91a3f88da4d5 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Mon, 4 Aug 2025 15:05:36 +0100
Subject: [PATCH 347/364] move rebreak logic into startBreaking function to
 account for instant breakable blocks too

---
 .../request/breaking/BreakManager.kt          | 80 +++++++++----------
 1 file changed, 40 insertions(+), 40 deletions(-)

diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index b11fbb19c..e67bbb6a5 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -324,7 +324,7 @@ object BreakManager : RequestHandler(
                                 }
                                 return@also
                             }
-                            if (!info.context.requestSwap(info.request, 0)) return@run
+                            if (!info.context.requestSwap(info.request)) return@run
                         }
                     }
                     .asReversed()
@@ -333,28 +333,6 @@ object BreakManager : RequestHandler(
                         if (tickStage !in info.breakConfig.breakStageMask) return@forEach
                         if (!rotated && info.isPrimary) return@run
 
-                        if (info.couldReBreak.value == true) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
-                            is ReBreakResult.StillBreaking -> {
-                                primaryBreak = reBreakResult.breakInfo.apply {
-                                    type = Primary
-                                    ReBreakManager.clearReBreak()
-                                    request.onStart?.invoke(info.context.blockPos)
-                                }
-
-                                primaryBreak?.let { primary ->
-                                    updateBreakProgress(primary)
-                                }
-                                return@forEach
-                            }
-                            is ReBreakResult.ReBroke -> {
-                                info.type = ReBreak
-                                info.nullify()
-                                info.request.onReBreak?.invoke(info.context.blockPos)
-                                return@forEach
-                            }
-                            else -> {}
-                        }
-
                         updateBreakProgress(info)
                     }
             }
@@ -657,31 +635,31 @@ object BreakManager : RequestHandler(
      * @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress
      */
     private fun SafeContext.updateBreakProgress(info: BreakInfo): Boolean {
-        info.progressedThisTick = true
-
         val config = info.breakConfig
         val ctx = info.context
-        val hitResult = ctx.result
-
-        if (gamemode.isCreative && world.worldBorder.contains(ctx.blockPos) && info.breaking) {
-            breakCooldown = config.breakDelay
-            lastPosStarted = ctx.blockPos
-            onBlockBreak(info)
-            info.startBreakPacket(world, interaction)
-            if (config.swing.isEnabled()) {
-                swingHand(config.swingType, Hand.MAIN_HAND)
-            }
-            return true
-        }
 
         if (!info.breaking) {
             if (!startBreaking(info)) {
                 info.nullify()
-                info.request.onCancel?.invoke(info.context.blockPos)
+                info.request.onCancel?.invoke(ctx.blockPos)
                 return false
             }
             val swing = config.swing
-            if (swing.isEnabled() && swing != BreakConfig.SwingMode.End) {
+            if (swing.isEnabled() && (swing != BreakConfig.SwingMode.End || info.isReBreaking)) {
+                swingHand(config.swingType, Hand.MAIN_HAND)
+            }
+            return true
+        }
+
+        info.progressedThisTick = true
+        val hitResult = ctx.result
+
+        if (gamemode.isCreative && world.worldBorder.contains(ctx.blockPos)) {
+            breakCooldown = config.breakDelay
+            lastPosStarted = ctx.blockPos
+            onBlockBreak(info)
+            info.startBreakPacket(world, interaction)
+            if (config.swing.isEnabled()) {
                 swingHand(config.swingType, Hand.MAIN_HAND)
             }
             return true
@@ -690,7 +668,7 @@ object BreakManager : RequestHandler(
         val blockState = blockState(ctx.blockPos)
         if (blockState.isEmpty || blockState.isAir) {
             info.nullify()
-            info.request.onCancel?.invoke(info.context.blockPos)
+            info.request.onCancel?.invoke(ctx.blockPos)
             return false
         }
 
@@ -756,6 +734,28 @@ object BreakManager : RequestHandler(
     private fun SafeContext.startBreaking(info: BreakInfo): Boolean {
         val ctx = info.context
 
+        if (info.couldReBreak.value == true) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
+            is ReBreakResult.StillBreaking -> {
+                primaryBreak = reBreakResult.breakInfo.apply {
+                    type = Primary
+                    ReBreakManager.clearReBreak()
+                    request.onStart?.invoke(ctx.blockPos)
+                }
+
+                primaryBreak?.let { primary ->
+                    updateBreakProgress(primary)
+                }
+                return true
+            }
+            is ReBreakResult.ReBroke -> {
+                info.type = ReBreak
+                info.nullify()
+                info.request.onReBreak?.invoke(ctx.blockPos)
+                return true
+            }
+            else -> {}
+        }
+
         if (player.isBlockBreakingRestricted(world, ctx.blockPos, gamemode)) return false
         if (!world.worldBorder.contains(ctx.blockPos)) return false
 

From b55cadea99abe8bfe0218d549c431595eea185de Mon Sep 17 00:00:00 2001
From: Edouard127 <46357922+Edouard127@users.noreply.github.com>
Date: Tue, 5 Aug 2025 18:44:48 -0400
Subject: [PATCH 348/364] My changes from a long time ago

---
 build.gradle.kts                              |  5 +-
 .../mixin/entity/ClientPlayerEntityMixin.java | 23 +++---
 .../mixin/entity/PlayerInventoryMixin.java    | 58 ---------------
 .../mixin/render/HeldItemRendererMixin.java   |  4 +-
 .../lambda/mixin/render/InGameHudMixin.java   |  6 +-
 .../render/LivingEntityRendererMixin.java     |  2 +-
 .../lambda/config/groups/InventorySettings.kt |  2 +-
 .../construction/blueprint/Blueprint.kt       |  5 +-
 .../construction/context/PlaceContext.kt      | 24 +------
 .../construction/simulation/BuildSimulator.kt | 37 +++++-----
 .../construction/verify/TargetState.kt        |  2 +-
 .../interaction/material/StackSelection.kt    | 34 +++++----
 .../material/container/ContainerManager.kt    | 19 +----
 .../material/container/MaterialContainer.kt   |  2 +-
 .../request/breaking/BreakManager.kt          |  2 +-
 .../request/breaking/BrokenBlockHandler.kt    |  5 +-
 .../request/hotbar/HotbarManager.kt           |  1 -
 .../request/interacting/InteractionManager.kt |  3 +-
 .../request/placing/PlaceManager.kt           | 40 +++++------
 .../request/placing/PlacedBlockHandler.kt     | 10 +--
 .../request/rotating/RotationManager.kt       | 10 +--
 .../request/rotating/RotationMode.kt          |  6 --
 .../module/modules/combat/AutoDisconnect.kt   |  1 +
 .../lambda/module/modules/combat/KillAura.kt  | 15 ++--
 .../lambda/module/modules/debug/SilentSwap.kt |  2 +-
 .../lambda/module/modules/player/FastBreak.kt |  2 +-
 .../lambda/module/modules/player/Freecam.kt   |  1 -
 .../com/lambda/module/modules/player/Nuker.kt |  5 +-
 .../lambda/module/modules/player/Replay.kt    | 12 ++--
 .../lambda/module/modules/player/Scaffold.kt  |  2 +-
 .../kotlin/com/lambda/task/tasks/BuildTask.kt | 20 +++---
 .../com/lambda/task/tasks/OpenContainer.kt    |  1 -
 .../com/lambda/task/tasks/PlaceContainer.kt   |  9 +--
 .../main/kotlin/com/lambda/util/BlockUtils.kt | 72 +++----------------
 .../com/lambda/util/EnchantmentUtils.kt       | 65 +++++++++++++++++
 .../com/lambda/util/item/ItemStackUtils.kt    | 61 ++++++++++------
 .../kotlin/com/lambda/util/item/ItemUtils.kt  | 14 +---
 .../com/lambda/util/player/PlayerUtils.kt     |  4 +-
 .../main/resources/lambda.mixins.common.json  |  1 -
 39 files changed, 247 insertions(+), 340 deletions(-)
 delete mode 100644 common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
 create mode 100644 common/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt

diff --git a/build.gradle.kts b/build.gradle.kts
index 5cca99cfb..360a15d7f 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -54,10 +54,7 @@ subprojects {
 
     dependencies {
         "minecraft"("com.mojang:minecraft:$minecraftVersion")
-        "mappings"(loom.layered {
-            mappings("net.fabricmc:yarn:$minecraftVersion+$yarnMappings:v2")
-            mappings("dev.architectury:yarn-mappings-patch-neoforge:1.21+build.4")
-        })
+        "mappings"("net.fabricmc:yarn:$minecraftVersion+$yarnMappings:v2")
     }
 
     publishing {
diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
index d43ed5902..8944168ae 100644
--- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
+++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,18 +23,18 @@
 import com.lambda.event.events.PlayerEvent;
 import com.lambda.event.events.TickEvent;
 import com.lambda.interaction.PlayerPacketManager;
-import com.lambda.interaction.request.rotation.RotationManager;
+import com.lambda.interaction.request.rotating.RotationManager;
 import com.lambda.module.modules.player.PortalGui;
+import com.lambda.module.modules.render.ViewModel;
 import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
 import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
 import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.screen.DeathScreen;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.HandledScreen;
 import net.minecraft.client.input.Input;
+import net.minecraft.client.network.AbstractClientPlayerEntity;
 import net.minecraft.client.network.ClientPlayerEntity;
 import net.minecraft.entity.MovementType;
-import net.minecraft.entity.damage.DamageSource;
+import net.minecraft.util.Hand;
 import net.minecraft.util.math.Vec3d;
 import org.spongepowered.asm.mixin.Final;
 import org.spongepowered.asm.mixin.Mixin;
@@ -118,7 +118,7 @@ void redirectSneaking(CallbackInfoReturnable cir) {
         if (self != Lambda.getMc().player) return;
 
         if (self.input == null) return;
-        cir.setReturnValue(EventFlow.post(new MovementEvent.Sneak(self.input.sneaking)).getSneak());
+        cir.setReturnValue(EventFlow.post(new MovementEvent.Sneak(self.input.playerInput.sneak())).getSneak());
     }
 
     /**
@@ -148,6 +148,11 @@ float fixHeldItemPitch(ClientPlayerEntity instance) {
         return Objects.requireNonNullElse(RotationManager.getHandPitch(), instance.getPitch());
     }
 
+    @Inject(method = "swingHand", at = @At("HEAD"), cancellable = true)
+    void onSwing(Hand hand, CallbackInfo ci) {
+        if (EventFlow.post(new PlayerEvent.SwingHand(hand)).isCanceled()) ci.cancel();
+    }
+
     @Redirect(method = "swingHand", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;swingHand(Lnet/minecraft/util/Hand;)V"))
     private void adjustSwing(AbstractClientPlayerEntity instance, Hand hand) {
         ViewModel viewModel = ViewModel.INSTANCE;
@@ -160,9 +165,9 @@ private void adjustSwing(AbstractClientPlayerEntity instance, Hand hand) {
         viewModel.adjustSwing(hand, instance);
     }
 
-    @Inject(method = "damage", at = @At("HEAD"), cancellable = true)
-    public void damage(DamageSource source, float amount, CallbackInfoReturnable cir) {
-        if (EventFlow.post(new PlayerEvent.Damage(source, amount)).isCanceled()) cir.setReturnValue(false);
+    @Inject(method = "updateHealth", at = @At("HEAD"))
+    public void damage(float health, CallbackInfo ci) {
+        EventFlow.post(new PlayerEvent.Damage(health));
     }
 
     /**
diff --git a/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java b/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
deleted file mode 100644
index ad8f9f9df..000000000
--- a/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2025 Lambda
- *
- * This program 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.
- *
- * This program 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 this program.  If not, see .
- */
-
-package com.lambda.mixin.entity;
-
-import net.minecraft.block.BlockState;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerInteractionManager;
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.entity.player.PlayerInventory;
-import net.minecraft.item.ItemStack;
-import net.minecraft.util.collection.DefaultedList;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-import static net.minecraft.entity.player.PlayerInventory.isValidHotbarIndex;
-
-@Mixin(PlayerInventory.class)
-public class PlayerInventoryMixin {
-    @Shadow @Final public PlayerEntity player;
-
-    @Inject(method = "getMainStacks", at = @At(value = "HEAD"), cancellable = true)
-    public void handleSpoofedMainHandStack(CallbackInfoReturnable cir) {
-        MinecraftClient mc = MinecraftClient.getInstance();
-        ClientPlayerInteractionManager interaction = mc.interactionManager;
-
-        if (player != mc.player || interaction == null) return;
-
-        int actualSlot = interaction.lastSelectedSlot;
-
-        cir.setReturnValue(
-                isValidHotbarIndex(actualSlot) ? main.get(actualSlot) : ItemStack.EMPTY
-        );
-    }
-
-    @Inject(method = "getBlockBreakingSpeed", at = @At(value = "HEAD"), cancellable = true)
-    public void handleSpoofedBlockBreakingSpeed(BlockState block, CallbackInfoReturnable cir) {
-        cir.setReturnValue(player.getMainHandStack().getMiningSpeedMultiplier(block));
-    }
-}
diff --git a/common/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java
index 905a74025..8021a2387 100644
--- a/common/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java
+++ b/common/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java
@@ -34,7 +34,7 @@ private void onRenderArmHoldingItem(AbstractClientPlayerEntity player, float tic
         ViewModel.INSTANCE.transform(itemStack, hand, matrices);
     }
 
-    @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V"))
+    @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V"))
     private void onRenderFirstPersonItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
         if (!ViewModel.INSTANCE.isEnabled()) return;
 
@@ -85,4 +85,4 @@ private float modifySwing(float swingProgress) {
 
         return swingProgress;
     }
-}
\ No newline at end of file
+}
diff --git a/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java b/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java
index ac7437d7e..4d5c457e1 100644
--- a/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java
+++ b/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -41,8 +41,8 @@ private void onRender(DrawContext context, RenderTickCounter tickCounter, Callba
         RenderMain.render2D();
     }
 
-    @Redirect(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerInventory;getMainHandStack()Lnet/minecraft/item/ItemStack;"))
+    @Redirect(method = "tick()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerInventory;getSelectedStack()Lnet/minecraft/item/ItemStack;"))
     private ItemStack onTick(PlayerInventory inventory) {
-        return isValidHotbarIndex(inventory.selectedSlot) ? inventory.main.get(inventory.selectedSlot) : ItemStack.EMPTY;
+        return isValidHotbarIndex(inventory.getSelectedSlot()) ? inventory.getMainStacks().get(inventory.getSelectedSlot()) : ItemStack.EMPTY;
     }
 }
diff --git a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
index 59300e2f2..aa1c16147 100644
--- a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
+++ b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
@@ -47,6 +47,6 @@ public class LivingEntityRendererMixin {
      */
     @Redirect(method = "updateRenderState(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/client/render/entity/state/LivingEntityRenderState;F)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getLerpedPitch(F)F", ordinal = 0), require = 0)
     private float injectRotationPitch(LivingEntity instance, float v) {
-        return Objects.requireNonNullElse(RotationManager.getRenderPitch(), v);
+        return Objects.requireNonNullElse(RotationManager.getHeadPitch(), v);
     }
 }
diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
index 64f6d2721..03eda4d8b 100644
--- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
+++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt
index b072d0023..baed3fbef 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt
@@ -18,7 +18,6 @@
 package com.lambda.interaction.construction.blueprint
 
 import com.lambda.interaction.construction.verify.TargetState
-import com.lambda.util.BlockUtils.blockPos
 import com.lambda.util.collections.updatableLazy
 import com.lambda.util.extension.Structure
 import com.lambda.util.math.roundedBlockPos
@@ -53,20 +52,18 @@ abstract class Blueprint {
 
     fun isOutOfBounds(vec3d: Vec3d): Boolean = bounds.value?.contains(vec3d.roundedBlockPos) == false
 
-    val center get() = bounds.value?.center?.blockPos
+    val center get() = bounds.value?.center
 
     companion object {
         fun emptyStructure(): Structure = emptyMap()
 
         fun Box.toStructure(targetState: TargetState): Structure =
             BlockPos.stream(this)
-                .map { it.blockPos }
                 .toList()
                 .associateWith { targetState }
 
         fun BlockBox.toStructure(targetState: TargetState): Structure =
             BlockPos.stream(this)
-                .map { it.blockPos }
                 .toList()
                 .associateWith { targetState }
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
index a95330dfc..c7f6fa1d2 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,8 +29,6 @@ import com.lambda.interaction.request.rotating.RotationRequest
 import com.lambda.util.BlockUtils
 import com.lambda.util.BlockUtils.blockState
 import net.minecraft.block.BlockState
-import net.minecraft.util.ActionResult
-import net.minecraft.util.Hand
 import net.minecraft.util.hit.BlockHitResult
 import net.minecraft.util.math.BlockPos
 import java.awt.Color
@@ -49,26 +47,6 @@ data class PlaceContext(
     private val baseColor = Color(35, 188, 254, 25)
     private val sideColor = Color(35, 188, 254, 100)
 
-    override fun interact(swingHand: Boolean) {
-        runSafe {
-            val actionResult = interaction.interactBlock(
-                player, hand, result
-            )
-
-            if (actionResult is ActionResult.Success) {
-                if (actionResult.swingSource() == ActionResult.SwingSource.CLIENT && swingHand) {
-                    player.swingHand(hand)
-                }
-
-                if (!player.getStackInHand(hand).isEmpty && player.isCreative) {
-                    mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand)
-                }
-            } else {
-                warn("Internal interaction failed with $actionResult")
-            }
-        }
-    }
-
     override fun compareTo(other: BuildContext) =
         when (other) {
             is PlaceContext -> compareBy {
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 0249a2bd5..970d3801d 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -62,9 +62,9 @@ import com.lambda.util.BlockUtils.hasFluid
 import com.lambda.util.BlockUtils.instantBreakable
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotEmpty
-import com.lambda.util.BlockUtils.vecOf
 import com.lambda.util.Communication.warn
 import com.lambda.util.math.distSq
+import com.lambda.util.math.vec3d
 import com.lambda.util.player.SlotUtils.hotbar
 import com.lambda.util.player.copyPlayer
 import com.lambda.util.player.gamemode
@@ -85,7 +85,6 @@ import net.minecraft.item.Item
 import net.minecraft.item.ItemPlacementContext
 import net.minecraft.item.ItemStack
 import net.minecraft.item.ItemUsageContext
-import net.minecraft.registry.RegistryKeys
 import net.minecraft.state.property.Properties
 import net.minecraft.util.Hand
 import net.minecraft.util.hit.BlockHitResult
@@ -256,7 +255,7 @@ object BuildSimulator {
                     acc.add(BuildResult.OutOfReach(pos, eye, misses))
                 } else {
                     //ToDo: Must clean up surface scan usage / renders. Added temporary direction until changes are made
-                    acc.add(BuildResult.NotVisible(pos, pos, Direction.UP, eye.distanceTo(pos.vecOf(Direction.UP))))
+                    acc.add(BuildResult.NotVisible(pos, pos, Direction.UP, eye.distanceTo(pos.offset(Direction.UP).vec3d)))
                 }
                 return@interactBlock
             }
@@ -421,7 +420,7 @@ object BuildSimulator {
                     return@forEach
                 }
 
-                acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.vecOf(hitSide))))
+                acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.offset(hitSide).vec3d)))
                 return@forEach
             }
 
@@ -444,10 +443,7 @@ object BuildSimulator {
                 val cachePos = CachedBlockPosition(
                     usageContext.world, usageContext.blockPos, false
                 )
-                val canBePlacedOn = optimalStack.canPlaceOn(
-                    usageContext.world.registryManager.get(RegistryKeys.BLOCK),
-                    cachePos,
-                )
+                val canBePlacedOn = optimalStack.canPlaceOn(cachePos)
                 if (!player.abilities.allowModifyWorld && !canBePlacedOn) {
                     acc.add(PlaceResult.IllegalUsage(pos))
                     return@forEach
@@ -767,22 +763,21 @@ object BuildSimulator {
         }
 
         val stackSelection = selectStack(
-            block = {
-                run {
-                    if (breaking.suitableToolsOnly) isSuitableForBreaking(state)
-                    else StackSelection.EVERYTHING
-                } and if (breaking.forceSilkTouch) {
-                    hasEnchantment(Enchantments.SILK_TOUCH)
-                } else if (breaking.forceFortunePickaxe) {
-                    hasEnchantment(Enchantments.FORTUNE, breaking.minFortuneLevel)
-                } else StackSelection.EVERYTHING
-            },
             sorter = compareByDescending {
-                it.canDestroy(world.registryManager.get(RegistryKeys.BLOCK), CachedBlockPosition(world, pos, false))
+                it.canBreak(CachedBlockPosition(world, pos, false))
             }.thenByDescending {
                 state.calcItemBlockBreakingDelta(player, world, pos, it)
             }
-        )
+        ) {
+            run {
+                if (breaking.suitableToolsOnly) isSuitableForBreaking(state)
+                else StackSelection.EVERYTHING
+            } and if (breaking.forceSilkTouch) {
+                hasEnchantment(Enchantments.AQUA_AFFINITY)
+            } else if (breaking.forceFortunePickaxe) {
+                hasEnchantment(Enchantments.FORTUNE, breaking.minFortuneLevel)
+            } else StackSelection.EVERYTHING
+        }
 
         val silentSwapSelection = selectContainer {
             ofAnyType(MaterialContainer.Rank.HOTBAR)
diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
index b888c11c8..fc85de199 100644
--- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt
index ccaf910df..0124a236c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt
@@ -17,13 +17,17 @@
 
 package com.lambda.interaction.material
 
-import com.lambda.util.BlockUtils.item
+import com.lambda.util.EnchantmentUtils.getEnchantment
 import com.lambda.util.item.ItemStackUtils.shulkerBoxContents
 import net.minecraft.block.Block
 import net.minecraft.block.BlockState
 import net.minecraft.enchantment.Enchantment
+import net.minecraft.enchantment.Enchantments
 import net.minecraft.item.Item
 import net.minecraft.item.ItemStack
+import net.minecraft.registry.RegistryKey
+import net.minecraft.registry.tag.ItemTags
+import net.minecraft.registry.tag.TagKey
 import net.minecraft.screen.slot.Slot
 import kotlin.reflect.KClass
 
@@ -127,6 +131,11 @@ class StackSelection {
     @StackSelectionDsl
     fun isSuitableForBreaking(blockState: BlockState): (ItemStack) -> Boolean = { it.isSuitableFor(blockState) }
 
+    @StackSelectionDsl
+    fun isTag(tag: TagKey): (ItemStack) -> Boolean {
+        return { it.isIn(tag) }
+    }
+
     /**
      * [isItem] returns a predicate that matches a specific [Item] instance.
      * @param T The instance of [Item] to be matched.
@@ -145,8 +154,8 @@ class StackSelection {
      */
     @StackSelectionDsl
     fun isBlock(block: Block): (ItemStack) -> Boolean {
-        item = block.item
-        return { it.item == block.item }
+        item = block.asItem()
+        return { it.item == block.asItem() }
     }
 
     /**
@@ -178,15 +187,12 @@ class StackSelection {
      * @return A predicate that matches the [Enchantment] and `level`.
      */
     @StackSelectionDsl
-    fun hasEnchantment(enchantment: Enchantment, level: Int = -1): (ItemStack) -> Boolean = {
-        true
-
-        // TODO: Figure out what the fuck the new registry system is lmao
-        /*if (level < 0) {
-            EnchantmentHelper.getLevel(enchantment, it) > 0
+    fun hasEnchantment(enchantment: RegistryKey, level: Int = -1): (ItemStack) -> Boolean = {
+        if (level < 0) {
+            it.getEnchantment(enchantment) > 0
         } else {
-            EnchantmentHelper.getLevel(enchantment, it) == level
-        }*/
+            it.getEnchantment(enchantment) == level
+        }
     }
 
     /**
@@ -264,7 +270,7 @@ class StackSelection {
         fun selectStack(
             count: Int = DEFAULT_AMOUNT,
             inShulkerBox: Boolean = false,
-            block: StackSelection.() -> (ItemStack) -> Boolean
+            block: StackSelection.() -> (ItemStack) -> Boolean,
         ) = StackSelection().apply {
             selector = block()
             this.count = count
@@ -275,8 +281,8 @@ class StackSelection {
         fun selectStack(
             count: Int = DEFAULT_AMOUNT,
             inShulkerBox: Boolean = false,
+            sorter: Comparator? = null,
             block: StackSelection.() -> (ItemStack) -> Boolean,
-            sorter: Comparator? = null
         ) = StackSelection().apply {
             selector = block()
             comparator = sorter
@@ -284,4 +290,4 @@ class StackSelection {
             this.inShulkerBox = inShulkerBox
         }
     }
-}
\ No newline at end of file
+}
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
index f99d69729..dfab02536 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,7 +29,6 @@ import com.lambda.interaction.material.container.containers.ChestContainer
 import com.lambda.interaction.material.container.containers.EnderChestContainer
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.blockEntity
-import com.lambda.util.BlockUtils.item
 import com.lambda.util.Communication.info
 import com.lambda.util.extension.containerStacks
 import com.lambda.util.reflections.getInstances
@@ -124,22 +123,8 @@ object ContainerManager : Loadable {
             .filter { it.spaceAvailable(selection) >= selection.count }
             .filter { inventory.containerSelection.matches(it) }
 
-    fun findBestAvailableTool(
-        blockState: BlockState,
-        availableTools: Set = ItemUtils.tools,
-        inventory: InventoryConfig = TaskFlowModule.inventory,
-    ) = availableTools.map {
-        it to it.getMiningSpeed(it.defaultStack, blockState)
-    }.filter { (item, speed) ->
-        speed > 1.0
-                && item.defaultStack.isSuitableFor(blockState)
-                && containerWithMaterial(item.select(), inventory).isNotEmpty()
-    }.maxByOrNull {
-        it.second
-    }?.first
-
     fun findDisposable(inventory: InventoryConfig = TaskFlowModule.inventory) = container().find { container ->
-        inventory.disposables.any { container.materialAvailable(it.item.select()) > 0 }
+        inventory.disposables.any { container.materialAvailable(it.asItem().select()) >= 0 }
     }
 
     class NoContainerFound(selection: StackSelection) : Exception("No container found matching $selection")
diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
index e0acddbe1..debd82071 100644
--- a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index e67bbb6a5..3b3ef952c 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -814,7 +814,7 @@ object BreakManager : RequestHandler(
         config: BreakConfig,
         item: ItemStack? = null
     ) = runSafe {
-        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.mainHandStack)
+        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.selectedStack)
         //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
 //        if (config.desyncFix) {
 //            val nextTickPrediction = buildPlayerPrediction().next()
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index 230ad09f1..dfa02fef6 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -143,9 +143,8 @@ object BrokenBlockHandler : PostActionHandler() {
         val ctx = info.context
 
         if (player.isBlockBreakingRestricted(world, ctx.blockPos, gamemode)) return false
+        if (!player.mainHandStack.canMine(ctx.cachedState, world, ctx.blockPos, player)) return false
 
-        if (!player.mainHandStack.item.canMine(ctx.cachedState, world, ctx.blockPos, player))
-            return false
         val block = ctx.cachedState.block
         if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false
         if (ctx.cachedState.isEmpty) return false
@@ -159,4 +158,4 @@ object BrokenBlockHandler : PostActionHandler() {
 
         return setState
     }
-}
\ No newline at end of file
+}
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index d39cb0936..953377901 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -26,7 +26,6 @@ import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap
-import com.lambda.mixin.entity.PlayerInventoryMixin
 import com.lambda.mixin.render.InGameHudMixin
 import com.lambda.threading.runSafe
 
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index ad7706706..0b5a418c3 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -35,6 +35,7 @@ import com.lambda.interaction.request.interacting.InteractedBlockHandler.startPe
 import com.lambda.interaction.request.interacting.InteractionManager.activeRequest
 import com.lambda.interaction.request.interacting.InteractionManager.processRequest
 import com.lambda.interaction.request.placing.PlaceManager
+import com.lambda.util.player.MovementUtils.sneaking
 import com.lambda.util.player.swingHand
 import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
 import net.minecraft.util.Hand
@@ -124,4 +125,4 @@ object InteractionManager : RequestHandler(
     }
 
     override fun preEvent() = UpdateManagerEvent.Interact.post()
-}
\ No newline at end of file
+}
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index bad7b13da..aac1c4dec 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -41,6 +41,7 @@ import com.lambda.interaction.request.placing.PlacedBlockHandler.setPendingConfi
 import com.lambda.interaction.request.placing.PlacedBlockHandler.startPending
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.Communication.warn
+import com.lambda.util.player.MovementUtils.sneaking
 import com.lambda.util.player.gamemode
 import com.lambda.util.player.isItemOnCooldown
 import com.lambda.util.player.swingHand
@@ -53,7 +54,6 @@ import net.minecraft.item.ItemStack
 import net.minecraft.item.ItemUsageContext
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
 import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
-import net.minecraft.registry.RegistryKeys
 import net.minecraft.sound.SoundCategory
 import net.minecraft.util.ActionResult
 import net.minecraft.util.Hand
@@ -203,24 +203,24 @@ object PlaceManager : RequestHandler(
                 return ActionResult.FAIL
             }
 
-            val actionResult = blockState.onUse(world, player, hand, hitResult)
+            val actionResult = blockState.onUse(world, player, hitResult)
             if (actionResult.isAccepted) {
                 return actionResult
             }
         }
 
-        val itemStack = player.getStackInHand(hand)
+        val stack = player.mainHandStack
 
-        if (!itemStack.isEmpty && !isItemOnCooldown(itemStack.item)) {
+        if (!stack.isEmpty && !isItemOnCooldown(stack)) {
             val itemUsageContext = ItemUsageContext(player, hand, hitResult)
             return if (gamemode.isCreative) {
-                val i = itemStack.count
-                useOnBlock(placeContext, request, hand, hitResult, placeConfig, itemStack, itemUsageContext)
+                val i = stack.count
+                useOnBlock(placeContext, request, hand, hitResult, placeConfig, stack, itemUsageContext)
                     .also {
-                        itemStack.count = i
+                        stack.count = i
                     }
             } else
-                useOnBlock(placeContext, request, hand, hitResult, placeConfig, itemStack, itemUsageContext)
+                useOnBlock(placeContext, request, hand, hitResult, placeConfig, stack, itemUsageContext)
         }
         return ActionResult.PASS
     }
@@ -242,7 +242,7 @@ object PlaceManager : RequestHandler(
         val cachedBlockPosition = CachedBlockPosition(world, context.blockPos, false)
 
         val cantModifyWorld = !player.abilities.allowModifyWorld
-        val cantPlaceOn = !itemStack.canPlaceOn(context.world.registryManager.get(RegistryKeys.BLOCK), cachedBlockPosition)
+        val cantPlaceOn = !itemStack.canPlaceOn(cachedBlockPosition)
         if (cantModifyWorld && cantPlaceOn) return ActionResult.PASS
 
         val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS
@@ -288,7 +288,7 @@ object PlaceManager : RequestHandler(
         if (placeConfig.swing) {
             swingHand(placeConfig.swingType, hand)
 
-            if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) {
+            if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || player.isInCreativeMode)) {
                 mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand)
             }
         }
@@ -297,7 +297,7 @@ object PlaceManager : RequestHandler(
         if (!player.abilities.creativeMode) itemStack.decrement(1)
 
         if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
-            return ActionResult.success(world.isClient)
+            return ActionResult.SUCCESS
 
         // TODO: Implement restriction checks (e.g., world height) to prevent unnecessary server requests when the
         //  "AwaitThenPlace" confirmation setting is enabled, as the block state setting methods that validate these
@@ -305,20 +305,20 @@ object PlaceManager : RequestHandler(
         if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL
 
         val blockPos = itemPlacementContext.blockPos
-        var hitState = world.getBlockState(blockPos)
-        if (hitState.isOf(blockState.block)) {
-            hitState = item.placeFromNbt(blockPos, world, itemStack, hitState)
-            item.postPlacement(blockPos, world, player, itemStack, hitState)
-            hitState.block.onPlaced(world, blockPos, hitState, player, itemStack)
+        var state = world.getBlockState(blockPos)
+        if (state.isOf(blockState.block)) {
+            state = item.placeFromNbt(blockPos, world, itemStack, state)
+            item.postPlacement(blockPos, world, player, itemStack, state)
+            state.block.onPlaced(world, blockPos, state, player, itemStack)
         }
 
-        if (placeConfig.sounds) placeSound(item, hitState, blockPos)
+        if (placeConfig.sounds) placeSound(state, blockPos)
 
         if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None) {
             request.onPlace?.invoke(placeContext.blockPos)
         }
 
-        return ActionResult.success(world.isClient)
+        return ActionResult.SUCCESS
     }
 
     /**
@@ -332,12 +332,12 @@ object PlaceManager : RequestHandler(
     /**
      * Plays the block placement sound at a given position.
      */
-    fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) {
+    fun SafeContext.placeSound(state: BlockState, pos: BlockPos) {
         val blockSoundGroup = state.soundGroup
         world.playSound(
             player,
             pos,
-            item.getPlaceSound(state),
+            state.soundGroup.placeSound,
             SoundCategory.BLOCKS,
             (blockSoundGroup.getVolume() + 1.0f) / 2.0f,
             blockSoundGroup.getPitch() * 0.8f
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
index 1756e4fc9..ed3884876 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt
@@ -24,11 +24,13 @@ import com.lambda.interaction.construction.processing.ProcessorRegistry
 import com.lambda.interaction.request.PostActionHandler
 import com.lambda.interaction.request.placing.PlaceManager.placeSound
 import com.lambda.module.modules.client.TaskFlowModule
-import com.lambda.util.BlockUtils.item
+import com.lambda.util.BlockUtils.matches
 import com.lambda.util.BlockUtils.matches
 import com.lambda.util.Communication.info
 import com.lambda.util.Communication.warn
 import com.lambda.util.collections.LimitedDecayQueue
+import net.minecraft.block.BlockState
+import net.minecraft.util.math.BlockPos
 import net.minecraft.item.BlockItem
 
 object PlacedBlockHandler : PostActionHandler() {
@@ -62,11 +64,9 @@ object PlacedBlockHandler : PostActionHandler() {
                     pending.stopPending()
 
                     if (pending.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
-                        with (pending.context) {
-                            placeSound(expectedState.block.item as BlockItem, expectedState, blockPos)
-                        }
+                        with(pending.context) { placeSound(expectedState, blockPos) }
                     pending.onPlace?.invoke(pending.context.blockPos)
                 }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
index f376ba9d3..cb25dd411 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt
@@ -17,7 +17,7 @@
 
 package com.lambda.interaction.request.rotating
 
-import com.lambda.Lambda
+import com.lambda.Lambda.mc
 import com.lambda.context.SafeContext
 import com.lambda.core.Loadable
 import com.lambda.event.EventFlow.post
@@ -25,7 +25,6 @@ import com.lambda.event.events.*
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.interaction.request.RequestHandler
-import com.lambda.interaction.request.rotation.Rotation.Companion.fixSensitivity
 import com.lambda.interaction.request.rotating.Rotation.Companion.slerp
 import com.lambda.interaction.request.rotating.Rotation.Companion.wrap
 import com.lambda.interaction.request.rotating.visibilty.lookAt
@@ -39,6 +38,7 @@ import com.lambda.util.math.Vec2d
 import com.lambda.util.math.lerp
 import net.minecraft.client.input.Input
 import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
+import net.minecraft.util.math.Vec2f
 import kotlin.math.cos
 import kotlin.math.round
 import kotlin.math.sign
@@ -89,7 +89,7 @@ object RotationManager : RequestHandler(
             if (packet !is PlayerPositionLookS2CPacket) return@listen
 
             runGameScheduled {
-                reset(Rotation(packet.yaw, packet.pitch))
+                reset(Rotation(packet.change.yaw, packet.change.pitch))
             }
         }
 
@@ -130,7 +130,7 @@ object RotationManager : RequestHandler(
         serverRotation = activeRotation/*.fixSensitivity(prevServerRotation)*/
 
         // Handle LOCK mode
-        if (activeRequest?.mode == RotationMode.Lock) {
+        if (activeRequest?.rotationMode == RotationMode.Lock) {
             mc.player?.yaw = serverRotation.yawF
             mc.player?.pitch = serverRotation.pitchF
         }
@@ -277,5 +277,5 @@ object RotationManager : RequestHandler(
         }
     }
 
-    override fun preEvent(): Event = UpdateManagerEvent.Rotation.post()
+    override fun preEvent() = UpdateManagerEvent.Rotation.post()
 }
diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt
index 4eaf134ca..7610f7a0f 100644
--- a/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt
+++ b/common/src/main/kotlin/com/lambda/interaction/request/rotating/RotationMode.kt
@@ -17,12 +17,6 @@
 
 package com.lambda.interaction.request.rotating
 
-import com.lambda.interaction.request.rotation.RotationMode.Lock
-import com.lambda.interaction.request.rotation.RotationMode.None
-import com.lambda.interaction.request.rotation.RotationMode.Silent
-import com.lambda.interaction.request.rotation.RotationMode.Sync
-
-
 /**
  * @property Silent Spoofing server-side rotation.
  * @property Sync Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim).
diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt
index deb35c682..3c5755de6 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt
@@ -30,6 +30,7 @@ import com.lambda.util.Communication.prefix
 import com.lambda.util.Formatting.string
 import com.lambda.util.combat.CombatUtils.hasDeadlyCrystal
 import com.lambda.util.combat.DamageUtils.isFallDeadly
+import com.lambda.util.extension.fullHealth
 import com.lambda.util.extension.tickDelta
 import com.lambda.util.player.SlotUtils.combined
 import com.lambda.util.text.buildText
diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
index a049bb286..6f8919576 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
@@ -25,7 +25,7 @@ import com.lambda.context.SafeContext
 import com.lambda.event.events.PlayerPacketEvent
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.interaction.material.StackSelection.Companion.select
+import com.lambda.interaction.material.StackSelection.Companion.selectStack
 import com.lambda.interaction.material.container.ContainerManager.transfer
 import com.lambda.interaction.material.container.containers.MainHandContainer
 import com.lambda.interaction.request.rotating.RotationManager
@@ -34,12 +34,12 @@ import com.lambda.module.Module
 import com.lambda.module.tag.ModuleTag
 import com.lambda.task.RootTask.run
 import com.lambda.util.item.ItemStackUtils.attackDamage
-import com.lambda.util.item.ItemStackUtils.itemAttackSpeed
+import com.lambda.util.item.ItemStackUtils.attackSpeed
 import com.lambda.util.math.random
-import com.lambda.util.player.SlotUtils.combined
 import com.lambda.util.world.raycast.InteractionMask
 import com.lambda.util.world.raycast.RayCastUtils.entityResult
 import net.minecraft.entity.LivingEntity
+import net.minecraft.registry.tag.ItemTags
 import net.minecraft.util.Hand
 import net.minecraft.util.math.Vec3d
 
@@ -102,9 +102,10 @@ object KillAura : Module(
         listen {
             target?.let { entity ->
                 if (swap) {
-                    val selection = player.combined
-                        .maxBy { stack -> stack.attackDamage } // ToDo: Write our own enchantment utils
-                        .select()
+                    val selection = selectStack(
+                        sorter = compareByDescending { attackDamage(stack = it) }
+                    ) { isTag(ItemTags.SWORDS) }
+
 
                     if (!selection.selector(player.mainHandStack)) {
                         selection.transfer(MainHandContainer)
@@ -130,7 +131,7 @@ object KillAura : Module(
         // Cooldown check
         when (attackMode) {
             AttackMode.Cooldown -> {
-                if (player.lastAttackedTicks < 20/player.itemAttackSpeed + cooldownOffset) return
+                if (player.lastAttackedTicks < 20/player.attackSpeed() + cooldownOffset) return
             }
 
             AttackMode.Delay -> {
diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
index ff7bb76c7..20c968103 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
index 16e0c4e5f..c935f95f9 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/FastBreak.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt
index 2779f39ee..960d3c8d0 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt
@@ -24,7 +24,6 @@ import com.lambda.event.events.PlayerEvent
 import com.lambda.event.events.RenderEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.request.rotating.Rotation
-import com.lambda.interaction.request.rotation.RotationConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.interaction.request.rotating.RotationManager.onRotate
 import com.lambda.interaction.request.rotating.RotationMode
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index 77fcff9e7..182e0e922 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -26,7 +26,6 @@ import com.lambda.task.RootTask.run
 import com.lambda.task.Task
 import com.lambda.task.tasks.BuildTask.Companion.build
 import com.lambda.util.BaritoneUtils
-import com.lambda.util.BlockUtils.blockPos
 import com.lambda.util.BlockUtils.blockState
 import net.minecraft.util.math.BlockPos
 
@@ -50,8 +49,7 @@ object Nuker : Module(
             task = tickingBlueprint {
                 val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width)
                     .asSequence()
-                    .map { it.blockPos }
-                    .filter { !blockState(it).isAir }
+                    .filter { !world.isAir(it) }
                     .filter { !flatten || it.y >= player.blockPos.y }
                     .filter { !instantOnly || blockState(it).getHardness(world, it) <= TaskFlowModule.build.breaking.breakThreshold }
                     .filter { pos ->
@@ -68,7 +66,6 @@ object Nuker : Module(
 
                 if (fillFloor) {
                     val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width)
-                        .map { it.blockPos }
                         .associateWith { TargetState.Solid }
                     return@tickingBlueprint selection + floor
                 }
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt
index cf4ae0f39..07d30022a 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt
@@ -32,11 +32,11 @@ import com.lambda.event.EventFlow.lambdaScope
 import com.lambda.event.events.KeyboardEvent
 import com.lambda.event.events.MovementEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.interaction.request.rotation.Rotation
-import com.lambda.interaction.request.rotation.RotationConfig
-import com.lambda.interaction.request.rotation.RotationManager.onRotate
-import com.lambda.interaction.request.rotation.RotationMode
-import com.lambda.interaction.request.rotation.visibilty.lookAt
+import com.lambda.interaction.request.rotating.Rotation
+import com.lambda.interaction.request.rotating.RotationConfig
+import com.lambda.interaction.request.rotating.RotationManager.onRotate
+import com.lambda.interaction.request.rotating.RotationMode
+import com.lambda.interaction.request.rotating.visibilty.lookAt
 import com.lambda.module.Module
 import com.lambda.module.modules.client.GuiSettings
 import com.lambda.module.modules.player.Replay.InputAction.Companion.toAction
@@ -71,6 +71,8 @@ import net.minecraft.util.math.Vec3d
 import java.io.File
 import java.lang.reflect.Type
 import java.time.format.DateTimeFormatter
+import kotlin.collections.removeFirstOrNull
+import kotlin.collections.take
 import kotlin.io.path.pathString
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.milliseconds
diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
index df1c1d534..5fd623333 100644
--- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
+++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
index 9f973a507..163640b6d 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@ import baritone.api.pathing.goals.GoalBlock
 import com.lambda.Lambda.LOG
 import com.lambda.config.groups.BuildConfig
 import com.lambda.config.groups.InteractionConfig
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
@@ -35,6 +34,7 @@ import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.construction.result.BreakResult
 import com.lambda.interaction.construction.result.BuildResult
 import com.lambda.interaction.construction.result.Drawable
+import com.lambda.interaction.construction.result.InteractResult
 import com.lambda.interaction.construction.result.Navigable
 import com.lambda.interaction.construction.result.PlaceResult
 import com.lambda.interaction.construction.result.Resolvable
@@ -43,10 +43,12 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
 import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation
 import com.lambda.interaction.construction.verify.TargetState
 import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer
-import com.lambda.interaction.request.breaking.BreakRequest
+import com.lambda.interaction.request.breaking.BreakRequest.Companion.breakRequest
 import com.lambda.interaction.request.hotbar.HotbarConfig
+import com.lambda.interaction.request.interacting.InteractRequest
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.placing.PlaceRequest
-import com.lambda.interaction.request.rotation.RotationConfig
+import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
 import com.lambda.util.BaritoneUtils
@@ -204,12 +206,12 @@ class BuildTask @Ta5kBuilder constructor(
         dropsToCollect
             .firstOrNull()
             ?.let { itemDrop ->
-                if (pendingInteractions.isNotEmpty()) return true
+                if (pendingInteractions.isNotEmpty()) return@let true
 
                 if (!world.entities.contains(itemDrop)) {
                     dropsToCollect.remove(itemDrop)
                     BaritoneUtils.cancel()
-                    return true
+                    return@let true
                 }
 
                 val noInventorySpace = player.hotbarAndStorage.none { it.isEmpty }
@@ -218,16 +220,16 @@ class BuildTask @Ta5kBuilder constructor(
                         it.stack.item.block in TaskFlowModule.inventory.disposables
                     } ?: run {
                         failure("No item in inventory to throw but inventory is full and cant pick up item drop")
-                        return true
+                        return@let true
                     }
                     transfer(player.currentScreenHandler) {
                         throwStack(stackToThrow.id)
                     }.execute(this@BuildTask)
-                    return true
+                    return@let true
                 }
 
                 BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos))
-                return true
+                return@let true
             } ?: false
 
     fun iteratePropagating() =
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
index b81d5b889..d1b2b4522 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt
@@ -21,7 +21,6 @@ import com.lambda.config.groups.InteractionConfig
 import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.interaction.request.rotation.RotationConfig
 import com.lambda.interaction.request.interacting.InteractConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.interaction.request.rotating.visibilty.lookAtBlock
diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
index 9dcdc6d28..26968983f 100644
--- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
+++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt
@@ -33,7 +33,6 @@ import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
 import com.lambda.task.tasks.BuildTask.Companion.build
-import com.lambda.util.BlockUtils.blockPos
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.item.ItemUtils.shulkerBoxes
 import net.minecraft.block.ChestBlock
@@ -63,10 +62,8 @@ class PlaceContainer @Ta5kBuilder constructor(
             }
 
             val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4)
-                .map { it.blockPos }
                 .flatMap {
-                    it.blockPos
-                        .toStructure(TargetState.Stack(startStack))
+                    it.toStructure(TargetState.Stack(startStack))
                         .toBlueprint()
                         .simulate(player.eyePos)
                 }
@@ -82,8 +79,8 @@ class PlaceContainer @Ta5kBuilder constructor(
                 ?.blockPos
                 ?.toStructure(TargetState.Stack(startStack))
         }.build(
-            true,
-            false,
+            finishOnDone = true,
+            collectDrops = false,
             build = build,
             rotation = rotation,
             interact = interact,
diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
index 108990024..f43d660bc 100644
--- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 Lambda
+ * Copyright 2025 Lambda
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 package com.lambda.util
 
 import com.lambda.context.SafeContext
-import com.lambda.util.math.MathUtils.floorToInt
+import com.lambda.util.player.gamemode
 import net.minecraft.block.AbstractCauldronBlock
 import net.minecraft.block.AbstractFurnaceBlock
 import net.minecraft.block.AbstractSignBlock
@@ -28,7 +28,6 @@ import net.minecraft.block.BeaconBlock
 import net.minecraft.block.BedBlock
 import net.minecraft.block.BeehiveBlock
 import net.minecraft.block.BellBlock
-import net.minecraft.block.Block
 import net.minecraft.block.BlockState
 import net.minecraft.block.Blocks
 import net.minecraft.block.BrewingStandBlock
@@ -80,19 +79,12 @@ import net.minecraft.block.StructureBlock
 import net.minecraft.block.SweetBerryBushBlock
 import net.minecraft.block.TntBlock
 import net.minecraft.block.TrapdoorBlock
+import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.fluid.FluidState
 import net.minecraft.fluid.Fluids
-import net.minecraft.item.Item
-import net.minecraft.util.math.*
 import net.minecraft.item.ItemStack
-import net.minecraft.item.SwordItem
-import net.minecraft.registry.tag.FluidTags
 import net.minecraft.state.property.Property
-import net.minecraft.util.math.BlockPos
-import net.minecraft.util.math.Direction
-import net.minecraft.util.math.EightWayDirection
-import net.minecraft.util.math.Vec3d
-import net.minecraft.util.math.Vec3i
+import net.minecraft.util.math.*
 import net.minecraft.world.BlockView
 
 object BlockUtils {
@@ -155,7 +147,7 @@ object BlockUtils {
 
     val allSigns = signs + wallSigns + hangingSigns + hangingWallSigns
 
-    val interactionClasses = setOf(
+    val interactionBlocks = setOf(
         AbstractCauldronBlock::class,
         AbstractFurnaceBlock::class,
         AbstractSignBlock::class,
@@ -217,10 +209,10 @@ object BlockUtils {
     )
 
     val fluids = listOf(
-        Fluids.FLOWING_LAVA,
         Fluids.LAVA,
-        Fluids.FLOWING_WATER,
+        Fluids.FLOWING_LAVA,
         Fluids.WATER,
+        Fluids.FLOWING_WATER,
         Fluids.EMPTY,
     )
 
@@ -248,55 +240,7 @@ object BlockUtils {
         world: BlockView,
         blockPos: BlockPos,
         item: ItemStack
-    ): Float =
-        if ((block is BambooShootBlock || block is BambooBlock) && item.item is SwordItem) {
-            1.0f
-        } else {
-            val hardness = getHardness(world, blockPos)
-            if (hardness == -1.0f) 0.0f else {
-                val harvestMultiplier = if (item.canHarvest(this)) 30 else 100
-                player.getItemBlockBreakingSpeed(this, item) / hardness / harvestMultiplier
-            }
-        }
-
-    fun ItemStack.canHarvest(blockState: BlockState) =
-        !blockState.isToolRequired || isSuitableFor(blockState)
-
-    fun PlayerEntity.getItemBlockBreakingSpeed(blockState: BlockState, item: ItemStack): Float {
-        var speedMultiplier = item.getMiningSpeedMultiplier(blockState)
-        if (speedMultiplier > 1.0f) {
-            val level = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, item)
-            if (level > 0 && !item.isEmpty) {
-                speedMultiplier += (level * level + 1)
-            }
-        }
-
-        if (StatusEffectUtil.hasHaste(this)) {
-            speedMultiplier *= 1.0f + (StatusEffectUtil.getHasteAmplifier(this) + 1) * 0.2f
-        }
-
-        getStatusEffect(StatusEffects.MINING_FATIGUE)?.amplifier?.let { fatigue ->
-            val fatigueMultiplier = when (fatigue) {
-                0 -> 0.3f
-                1 -> 0.09f
-                2 -> 0.0027f
-                3 -> 8.1E-4f
-                else -> 8.1E-4f
-            }
-
-            speedMultiplier *= fatigueMultiplier
-        }
-
-        if (isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(this)) {
-            speedMultiplier /= 5.0f
-        }
-
-        if (!isOnGround) {
-            speedMultiplier /= 5.0f
-        }
-
-        return speedMultiplier
-    }
+    ): Float = 0f
 
     val BlockState.isEmpty get() = matches(emptyState)
     val BlockState.isNotEmpty get() = !isEmpty
diff --git a/common/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt b/common/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
new file mode 100644
index 000000000..86c7688bb
--- /dev/null
+++ b/common/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.util
+
+import net.minecraft.component.DataComponentTypes
+import net.minecraft.component.type.ItemEnchantmentsComponent
+import net.minecraft.enchantment.Enchantment
+import net.minecraft.entity.EquipmentSlot
+import net.minecraft.entity.LivingEntity
+import net.minecraft.item.ItemStack
+import net.minecraft.registry.RegistryKey
+import net.minecraft.registry.entry.RegistryEntry
+
+object EnchantmentUtils {
+    /**
+     * Returns the list of enchantments from a given [ItemStack]
+     */
+    val ItemStack.enchantments: ItemEnchantmentsComponent
+        get() = getOrDefault(DataComponentTypes.ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT)
+
+    /**
+     * Returns whether the given [ItemStack] has enchantments
+     */
+    val ItemStack.hasEnchantments: Boolean
+        get() = !getOrDefault(DataComponentTypes.ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT).isEmpty
+                || getOrDefault(DataComponentTypes.STORED_ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT).isEmpty
+
+    /**
+     * Returns the given enchantment level from a [net.minecraft.item.ItemStack]
+     */
+    fun ItemStack.getEnchantment(key: RegistryKey) =
+        getOrDefault(DataComponentTypes.ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT)
+            .enchantmentEntries.find { it.key == key }
+            ?.intValue
+            ?: 0
+
+
+    /**
+     * Iterates over all the enchantments for the given [ItemStack]
+     */
+    fun  ItemStack.forEachEnchantment(block: (RegistryEntry, Int) -> T) =
+        enchantments.enchantmentEntries.asSequence()
+            .map { block(it.key, it.intValue) }
+
+    /**
+     * Iterates over all the enchantments of the given [net.minecraft.entity.LivingEntity]'s [EquipmentSlot]
+     */
+    fun  LivingEntity.forEachSlot(vararg slots: EquipmentSlot, block: (entry: RegistryEntry, level: Int) -> T) =
+        slots.flatMap { getEquippedStack(it).forEachEnchantment(block) }
+}
diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt
index b2d419933..dd4325a73 100644
--- a/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt
@@ -17,48 +17,63 @@
 
 package com.lambda.util.item
 
+import com.lambda.context.SafeContext
 import com.lambda.util.collections.Cacheable.Companion.cacheable
 import net.minecraft.component.DataComponentTypes
+import net.minecraft.component.type.AttributeModifiersComponent
+import net.minecraft.entity.LivingEntity
 import net.minecraft.entity.attribute.EntityAttributes
-import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.item.ItemStack
 
 object ItemStackUtils {
+    // FixMe: Change this fucking retarded stuff when mojang wake up from their coma and realize they fucked this shit up
+    //  - The client and the server entity attributes are not synced,
+    //  - Enchantments do not change attributes,
+    //  - All enchantment utils are bound to the server
+
+    /**
+     * Returns the attack damage for the given [stack], the value is affected by potion effects and enchantments
+     */
+    fun SafeContext.attackDamage(entity: LivingEntity = player, stack: ItemStack = entity.mainHandStack) = entity.attackDamage(stack)
+
+    /**
+     * Returns the attack damage for the given [stack], the value is affected by potion effects and enchantments
+     */
+    fun LivingEntity.attackDamage(stack: ItemStack = mainHandStack) =
+        (stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT)
+            .modifiers.find { it.attribute == EntityAttributes.ATTACK_DAMAGE }?.modifier?.value ?: 0.0) +
+                getAttributeValue(EntityAttributes.ATTACK_DAMAGE)
     /**
-     * Returns the full attack damage of the main hand item.
+     * Returns the attack speed for the given [stack], the value is affected by potion effects
      *
-     * The player attack damage base value is 1 and can be modified by potion effects such as
-     * strength and these modifications are held into account.
+     * The value represents the number of attacks-per-tick
      */
-    val PlayerEntity.itemAttackDamage: Double
-        get() = getAttributeValue(EntityAttributes.ATTACK_DAMAGE) + mainHandStack.attackDamage
+    fun SafeContext.attackSpeed(entity: LivingEntity = player, stack: ItemStack = entity.mainHandStack) = entity.attackSpeed(stack)
 
     /**
-     * Returns the full attack speed of the main hand item.
+     * Returns the attack speed for the given [stack], the value is affected by potion effects
      *
-     * The player attack speed base value is 4 and can be modified by potion effects such as
-     * haste and mining fatigue and these modifications are held into account.
+     * The value represents the number of attacks-per-tick
      */
-    val PlayerEntity.itemAttackSpeed: Double
-        get() = getAttributeValue(EntityAttributes.ATTACK_SPEED) + mainHandStack.attackSpeed
+    fun LivingEntity.attackSpeed(stack: ItemStack = mainHandStack) =
+        (stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT)
+            .modifiers.find { it.attribute == EntityAttributes.ATTACK_SPEED }?.modifier?.value ?: 0.0) +
+                getAttributeValue(EntityAttributes.ATTACK_SPEED)
 
     /**
-     * Returns the base attack damage of the given [ItemStack] or 2 as a fallback
+     * Returns the mining speed for the given [stack], the value is affected by potion effects and enchantments
      */
-    val ItemStack.attackDamage: Double
-        get() = get(DataComponentTypes.ATTRIBUTE_MODIFIERS)
-            ?.modifiers
-            ?.find { it.attribute == EntityAttributes.ATTACK_DAMAGE }
-            ?.modifier?.value ?: 2.0
+    fun SafeContext.miningSpeed(entity: LivingEntity = player, stack: ItemStack = entity.mainHandStack) = entity.miningSpeed(stack)
 
     /**
-     * Returns the base attack speed of the given [ItemStack] or 4 as a fallback
+     * Returns the mining speed for the given [stack], the value is affected by potion effects and enchantments
      */
-    val ItemStack.attackSpeed: Double
-        get() = get(DataComponentTypes.ATTRIBUTE_MODIFIERS)
-            ?.modifiers
-            ?.find { it.attribute == EntityAttributes.ATTACK_SPEED }
-            ?.modifier?.value ?: 4.0
+    fun LivingEntity.miningSpeed(stack: ItemStack = mainHandStack) =
+        (stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT)
+            .modifiers.find { it.attribute == EntityAttributes.MINING_EFFICIENCY ||
+                    it.attribute == EntityAttributes.SUBMERGED_MINING_SPEED }?.modifier?.value ?: 0.0) +
+                if (isSubmergedInWater) getAttributeValue(EntityAttributes.SUBMERGED_MINING_SPEED)
+                else getAttributeValue(EntityAttributes.MINING_EFFICIENCY)
 
     val ItemStack.spaceLeft get() = maxCount - count
     val ItemStack.hasSpace get() = spaceLeft > 0
diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt
index 0f894c26d..1ed212185 100644
--- a/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt
@@ -25,6 +25,7 @@ import net.minecraft.item.Items
 
 object ItemUtils {
 
+
     val pickaxes = setOf(
         Items.WOODEN_PICKAXE,
         Items.STONE_PICKAXE,
@@ -123,19 +124,6 @@ object ItemUtils {
 
     val Item.block: Block get() = Block.getBlockFromItem(this)
 
-    fun findBestToolsForBreaking(
-        blockState: BlockState,
-        availableTools: Set = tools,
-    ) = availableTools.map {
-        it to it.getMiningSpeedMultiplier(it.defaultStack, blockState)
-    }.filter { (item, speed) ->
-        speed > 1.0 && item.isSuitableFor(blockState)
-    }.sortedByDescending {
-        it.second
-    }.map {
-        it.first
-    }
-
     fun Int.toItemCount(): String {
         if (this < 0) {
             return "Invalid input"
diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt
index 20d8b384b..b0d280893 100644
--- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt
+++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt
@@ -24,7 +24,7 @@ import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.client.network.OtherClientPlayerEntity
 import net.minecraft.client.network.PlayerListEntry
 import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
 import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket
 import net.minecraft.util.Hand
 
@@ -84,4 +84,4 @@ fun SafeContext.swingHandClient(hand: Hand) {
     }
 }
 
-fun SafeContext.isItemOnCooldown(item: Item) = player.itemCooldownManager.isCoolingDown(item)
+fun SafeContext.isItemOnCooldown(stack: ItemStack) = player.itemCooldownManager.isCoolingDown(stack)
diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json
index c589c6a77..ad37c083a 100644
--- a/common/src/main/resources/lambda.mixins.common.json
+++ b/common/src/main/resources/lambda.mixins.common.json
@@ -15,7 +15,6 @@
     "entity.FireworkRocketEntityMixin",
     "entity.LivingEntityMixin",
     "entity.PlayerEntityMixin",
-    "entity.PlayerInventoryMixin",
     "input.KeyBindingMixin",
     "input.KeyboardMixin",
     "input.MouseMixin",

From 2dad5ca92f3f73e1656b53a6f29023fb166bbbba Mon Sep 17 00:00:00 2001
From: Edouard127 <46357922+Edouard127@users.noreply.github.com>
Date: Tue, 5 Aug 2025 20:07:13 -0400
Subject: [PATCH 349/364] changes

---
 .../kotlin/com/lambda/config/Configurable.kt  |  2 +-
 .../com/lambda/config/groups/BreakSettings.kt |  4 +-
 .../lambda/config/groups/HotbarSettings.kt    |  2 +-
 .../lambda/config/groups/InventoryConfig.kt   | 75 -------------------
 .../lambda/config/groups/InventorySettings.kt |  2 +-
 .../com/lambda/config/groups/PlaceSettings.kt |  2 +-
 .../settings/complex/BlockPosSetting.kt       |  4 -
 .../construction/result/BreakResult.kt        |  2 +-
 .../construction/result/BuildResult.kt        |  2 +-
 .../construction/verify/TargetState.kt        |  2 +-
 .../material/container/ContainerManager.kt    |  2 +-
 .../material/container/MaterialContainer.kt   |  2 +-
 .../material/transfer/SlotTransfer.kt         |  2 +-
 .../request/breaking/BreakManager.kt          |  1 +
 .../request/breaking/BreakRequest.kt          |  2 +-
 .../request/hotbar/HotbarManager.kt           |  7 --
 .../request/interacting/InteractionManager.kt |  1 +
 .../request/inventory/InventoryManager.kt     |  1 +
 .../request/inventory/InventoryRequest.kt     |  1 -
 .../request/placing/PlaceManager.kt           |  1 +
 .../request/rotating/RotationConfig.kt        |  5 --
 .../lambda/module/modules/combat/AutoTotem.kt |  1 +
 .../module/modules/debug/PropertyPrinter.kt   |  8 +-
 .../lambda/module/modules/debug/SilentSwap.kt |  2 +-
 .../lambda/module/modules/debug/StateInfo.kt  |  8 +-
 .../lambda/module/modules/player/AntiAim.kt   |  7 +-
 .../module/modules/player/PacketMine.kt       |  8 +-
 .../lambda/module/modules/render/FreeLook.kt  |  3 +-
 .../com/lambda/task/tasks/AcquireMaterial.kt  |  2 +-
 src/main/kotlin/com/lambda/util/BlockUtils.kt |  8 ++
 30 files changed, 45 insertions(+), 124 deletions(-)
 delete mode 100644 src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt

diff --git a/src/main/kotlin/com/lambda/config/Configurable.kt b/src/main/kotlin/com/lambda/config/Configurable.kt
index c77d0127a..2937bb779 100644
--- a/src/main/kotlin/com/lambda/config/Configurable.kt
+++ b/src/main/kotlin/com/lambda/config/Configurable.kt
@@ -257,7 +257,7 @@ abstract class Configurable(
     inline fun  setting(
         name: String,
         immutableList: Set,
-        defaultValue: Set,
+        defaultValue: Set = immutableList,
         description: String = "",
         noinline visibility: () -> Boolean = { true },
     ) = SetSetting(
diff --git a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 1463b7ddb..c2c06ba75 100644
--- a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -50,7 +50,7 @@ class BreakSettings(
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
 
     // Timing
-    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
+    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), description = "The sub-tick timing at which break actions can be performed") { vis() && page == Page.General }
 
     // Swap
     override val swapMode by c.setting("Swap Mode", BreakConfig.SwapMode.End, "Decides when to swap to the best suited tool when breaking a block") { vis() && page == Page.General }
@@ -71,7 +71,7 @@ class BreakSettings(
     override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill") { vis() && page == Page.General }
     override val avoidSupporting by c.setting("Avoid Supporting", true, "Avoids breaking the block supporting the player") { vis() && page == Page.General }
     override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.General }
-    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.General }
+    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, description = "Blocks that wont be broken") { vis() && page == Page.General }
 
     // Tool
     override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block") { vis() && page == Page.General }
diff --git a/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
index 73038317b..a5f48f76c 100644
--- a/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt
@@ -29,5 +29,5 @@ class HotbarSettings(
     override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", visibility = vis)
     override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() }
     override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", visibility = vis)
-    override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Post), "The sub-tick timing at which hotbar actions are performed", visibility = vis)
+    override val sequenceStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Post), description = "The sub-tick timing at which hotbar actions are performed", visibility = vis)
 }
diff --git a/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
deleted file mode 100644
index 9baaa1def..000000000
--- a/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2025 Lambda
- *
- * This program 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.
- *
- * This program 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 this program.  If not, see .
- */
-
-package com.lambda.interaction.request.inventory
-
-import com.lambda.interaction.material.ContainerSelection
-import com.lambda.interaction.material.StackSelection
-import com.lambda.interaction.material.container.MaterialContainer
-import com.lambda.interaction.request.RequestConfig
-import net.minecraft.block.Block
-
-interface InventoryConfig : RequestConfig {
-    val disposables: Set
-    val swapWithDisposables: Boolean
-    val providerPriority: Priority
-    val storePriority: Priority
-
-    val accessShulkerBoxes: Boolean
-    val accessEnderChest: Boolean
-    val accessChests: Boolean
-    val accessStashes: Boolean
-
-    val containerSelection: ContainerSelection
-        get() = ContainerSelection.Companion.selectContainer {
-            val allowedContainers = mutableSetOf().apply {
-                addAll(MaterialContainer.Rank.entries)
-                if (!accessShulkerBoxes) remove(MaterialContainer.Rank.SHULKER_BOX)
-                if (!accessEnderChest) remove(MaterialContainer.Rank.ENDER_CHEST)
-                if (!accessChests) remove(MaterialContainer.Rank.CHEST)
-                if (!accessStashes) remove(MaterialContainer.Rank.STASH)
-            }
-            ofAnyType(*allowedContainers.toTypedArray())
-        }
-
-    enum class Priority {
-        WithMinItems,
-        WithMaxItems;
-
-        fun materialComparator(selection: StackSelection) =
-            when (this) {
-                WithMaxItems -> compareBy { it.rank }
-                    .thenByDescending { it.materialAvailable(selection) }
-                    .thenBy { it.name }
-
-                WithMinItems -> compareBy { it.rank }
-                    .thenBy { it.materialAvailable(selection) }
-                    .thenBy { it.name }
-            }
-
-        fun spaceComparator(selection: StackSelection) =
-            when (this) {
-                WithMaxItems -> compareBy { it.rank }
-                    .thenByDescending { it.spaceAvailable(selection) }
-                    .thenBy { it.name }
-
-                WithMinItems -> compareBy { it.rank }
-                    .thenBy { it.spaceAvailable(selection) }
-                    .thenBy { it.name }
-            }
-    }
-}
diff --git a/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
index 03eda4d8b..acd86a28f 100644
--- a/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt
@@ -28,7 +28,7 @@ class InventorySettings(
 ) : InventoryConfig {
     val page by c.setting("Inventory Page", Page.Container, "The page to open when the module is enabled", vis)
 
-    override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container}
+    override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, ItemUtils.defaultDisposables, "Items that will be included when checking for a free slot / are allowed to be droped when inventory is full") { vis() && page == Page.Container}
     override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones") { vis() && page == Page.Container}
     override val providerPriority by c.setting("Provider Priority", Priority.WithMinItems, "What container to prefer when retrieving the item from") { vis() && page == Page.Container}
     override val storePriority by c.setting("Store Priority", Priority.WithMinItems, "What container to prefer when storing the item to") { vis() && page == Page.Container}
diff --git a/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
index 4118f7a24..64a332cc2 100644
--- a/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
@@ -30,7 +30,7 @@ class PlaceSettings(
     override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", visibility = vis)
     override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces", visibility = vis)
     override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() }
-    override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), "The sub-tick timing at which break actions are performed", visibility = vis)
+    override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), description = "The sub-tick timing at which break actions are performed", visibility = vis)
     override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation", visibility = vis)
     override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements", visibility = vis)
     override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick", visibility = vis)
diff --git a/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt
index 98be60bf2..020a25b69 100644
--- a/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt
+++ b/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt
@@ -43,10 +43,6 @@ class BlockPosSetting(
     description,
     visibility
 ) {
-    private var x = "${value.x}"
-    private var y = value.y
-    private var z = value.z
-
     override val layout: ImGuiBuilder.() -> Unit
         get() =
         {
diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
index bac333af1..b6f032a47 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt
@@ -19,13 +19,13 @@ package com.lambda.interaction.construction.result
 
 import baritone.api.pathing.goals.GoalBlock
 import baritone.api.pathing.goals.GoalInverted
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.material.StackSelection.Companion.selectStack
 import com.lambda.interaction.material.container.ContainerManager.transfer
 import com.lambda.interaction.material.container.MaterialContainer
 import com.lambda.interaction.material.container.containers.MainHandContainer
+import com.lambda.interaction.request.inventory.InventoryConfig
 import net.minecraft.block.BlockState
 import net.minecraft.item.Item
 import net.minecraft.util.math.BlockPos
diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt
index 9b86016c8..5e8d657f9 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt
@@ -19,7 +19,6 @@ package com.lambda.interaction.construction.result
 
 import baritone.api.pathing.goals.GoalBlock
 import baritone.api.pathing.goals.GoalNear
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.material.StackSelection
@@ -27,6 +26,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select
 import com.lambda.interaction.material.container.ContainerManager.transfer
 import com.lambda.interaction.material.container.MaterialContainer
 import com.lambda.interaction.material.container.containers.MainHandContainer
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.util.BlockUtils.blockState
 import com.lambda.util.Nameable
 import net.minecraft.block.BlockState
diff --git a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
index fc85de199..e10e99f12 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
@@ -17,8 +17,8 @@
 
 package com.lambda.interaction.construction.verify
 
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.material.container.ContainerManager.findDisposable
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.matches
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
index dfab02536..ccf6d179f 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt
@@ -17,7 +17,6 @@
 
 package com.lambda.interaction.material.container
 
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.core.Loadable
 import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.PlayerEvent
@@ -27,6 +26,7 @@ import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.StackSelection.Companion.select
 import com.lambda.interaction.material.container.containers.ChestContainer
 import com.lambda.interaction.material.container.containers.EnderChestContainer
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.util.BlockUtils.blockEntity
 import com.lambda.util.Communication.info
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
index debd82071..3fdd3a135 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt
@@ -17,7 +17,6 @@
 
 package com.lambda.interaction.material.container
 
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
@@ -25,6 +24,7 @@ import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial
 import com.lambda.interaction.material.container.containers.ShulkerBoxContainer
 import com.lambda.interaction.material.transfer.TransferResult
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.task.Task
 import com.lambda.util.Communication.logError
 import com.lambda.util.Nameable
diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt
index 39e7c1987..c2b816af5 100644
--- a/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt
+++ b/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt
@@ -17,12 +17,12 @@
 
 package com.lambda.interaction.material.transfer
 
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.transfer.TransactionExecutor.Companion.transfer
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
 import com.lambda.util.extension.containerSlots
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index d48442090..3b3ef952c 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -24,6 +24,7 @@ import com.lambda.event.events.ConnectionEvent
 import com.lambda.event.events.EntityEvent
 import com.lambda.event.events.RenderEvent
 import com.lambda.event.events.TickEvent
+import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.events.WorldEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
index f21a46885..3fe4abfe9 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt
@@ -23,7 +23,7 @@ import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.construction.context.BuildContext
 import com.lambda.interaction.request.Request
 import com.lambda.interaction.request.hotbar.HotbarConfig
-import com.lambda.config.groups.InventoryConfig
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.interaction.request.rotating.RotationConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.threading.runSafe
diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index 953377901..9b50cc492 100644
--- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -26,15 +26,8 @@ import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap
-import com.lambda.mixin.render.InGameHudMixin
 import com.lambda.threading.runSafe
 
-/**
- * See mixins:
- * @see PlayerInventoryMixin.handleSpoofedMainHandStack
- * @see PlayerInventoryMixin.handleSpoofedBlockBreakingSpeed
- * @see InGameHudMixin.onTick
- */
 object HotbarManager : RequestHandler(
     1,
     TickEvent.Pre,
diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
index c1dc02e7a..0b5a418c3 100644
--- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt
@@ -22,6 +22,7 @@ import com.lambda.context.SafeContext
 import com.lambda.event.EventFlow.post
 import com.lambda.event.events.MovementEvent
 import com.lambda.event.events.TickEvent
+import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.construction.context.InteractionContext
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
index 18d5e25e6..4c86403e9 100644
--- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt
@@ -20,6 +20,7 @@ package com.lambda.interaction.request.inventory
 import com.lambda.context.SafeContext
 import com.lambda.event.EventFlow.post
 import com.lambda.event.events.TickEvent
+import com.lambda.event.events.UpdateManagerEvent
 
 import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.inventory.InventoryManager.activeRequest
diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
index 99991ca06..9b073b726 100644
--- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt
@@ -17,7 +17,6 @@
 
 package com.lambda.interaction.request.inventory
 
-import com.lambda.config.groups.InventoryConfig
 import com.lambda.interaction.request.Request
 
 class InventoryRequest(
diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
index bf05236fb..aac1c4dec 100644
--- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt
@@ -22,6 +22,7 @@ import com.lambda.event.Event
 import com.lambda.event.EventFlow.post
 import com.lambda.event.events.MovementEvent
 import com.lambda.event.events.TickEvent
+import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.construction.context.PlaceContext
 import com.lambda.interaction.request.ManagerUtils.isPosBlocked
diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
index a38dbed96..cb223b421 100644
--- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt
@@ -19,11 +19,6 @@ package com.lambda.interaction.request.rotating
 
 import com.lambda.interaction.request.RequestConfig
 
-/**
- * Abstract base class for configuring rotation behavior.
- *
- * @param priority The priority of this configuration.
- */
 interface RotationConfig : RequestConfig {
     /**
      * - [RotationMode.Silent] Spoofing server-side rotation.
diff --git a/src/main/kotlin/com/lambda/module/modules/combat/AutoTotem.kt b/src/main/kotlin/com/lambda/module/modules/combat/AutoTotem.kt
index dc169bd30..23fd244d0 100644
--- a/src/main/kotlin/com/lambda/module/modules/combat/AutoTotem.kt
+++ b/src/main/kotlin/com/lambda/module/modules/combat/AutoTotem.kt
@@ -37,6 +37,7 @@ import com.lambda.util.world.fastEntitySearch
 import net.minecraft.entity.mob.CreeperEntity
 import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.item.Items
+import net.minecraft.util.math.BlockPos
 
 object AutoTotem : Module(
     name = "AutoTotem",
diff --git a/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt b/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt
index 9f64699a8..3f74a3765 100644
--- a/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt
+++ b/src/main/kotlin/com/lambda/module/modules/debug/PropertyPrinter.kt
@@ -25,9 +25,9 @@ import net.minecraft.block.Block
 import net.minecraft.block.Blocks
 
 object PropertyPrinter : Module(
-    "PropertyPrinter",
-    "Prints all properties coupled with all the states that use them into a text file",
-    setOf(ModuleTag.DEBUG)
+    name = "PropertyPrinter",
+    description = "Prints all properties coupled with all the states that use them into a text file",
+    tag = ModuleTag.DEBUG,
 ) {
     init {
         onEnable {
@@ -49,4 +49,4 @@ object PropertyPrinter : Module(
             disable()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt b/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
index 20c968103..6e92860a7 100644
--- a/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
+++ b/src/main/kotlin/com/lambda/module/modules/debug/SilentSwap.kt
@@ -29,7 +29,7 @@ import com.lambda.util.Communication.info
 object SilentSwap : Module(
     name = "SilentSwap",
     description = "SilentSwap",
-    defaultTags = setOf(ModuleTag.DEBUG),
+    tag = ModuleTag.DEBUG,
 ) {
     private val hotbar = HotbarSettings(this)
 
diff --git a/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt b/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt
index 46ea910ca..95a6f3ff6 100644
--- a/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt
+++ b/src/main/kotlin/com/lambda/module/modules/debug/StateInfo.kt
@@ -30,9 +30,9 @@ import net.minecraft.state.property.Property
 import net.minecraft.util.hit.BlockHitResult
 
 object StateInfo : Module(
-    "StateInfo",
-    "Prints the target block's state into chat",
-    setOf(ModuleTag.DEBUG)
+    name = "StateInfo",
+    description = "Prints the target block's state into chat",
+    tag = ModuleTag.DEBUG,
 ) {
     private val printBind by setting("Print", KeyCode.UNBOUND, "The bind used to print the info to chat")
 
@@ -74,4 +74,4 @@ object StateInfo : Module(
     private fun ?> nameValue(property: Property, value: Comparable<*>): String {
         return property.name(value as T)
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
index dec99ab76..a328423b4 100644
--- a/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
+++ b/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt
@@ -20,6 +20,7 @@ package com.lambda.module.modules.player
 import com.lambda.config.groups.RotationSettings
 import com.lambda.context.SafeContext
 import com.lambda.event.events.TickEvent
+import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.interaction.request.Request.Companion.submit
 import com.lambda.interaction.request.rotating.Rotation
@@ -36,9 +37,9 @@ import net.minecraft.util.math.MathHelper.wrapDegrees
 import kotlin.random.Random
 
 object AntiAim : Module(
-    "AntiAim",
-    "Rotates the player using the given configs",
-    setOf(ModuleTag.PLAYER, ModuleTag.MOVEMENT)
+    name = "AntiAim",
+    description = "Rotates the player using the given configs",
+    tag = ModuleTag.MOVEMENT,
 ) {
     private val page by setting("Page", Page.General)
     private val yaw by setting("Yaw Mode", YawMode.Spin, "The mode used when setting the players yaw") { page == Page.General }
diff --git a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index b882d8052..354a26bb5 100644
--- a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -49,9 +49,9 @@ import java.awt.Color
 import java.util.concurrent.ConcurrentLinkedQueue
 
 object PacketMine : Module(
-    "PacketMine",
-    "automatically breaks blocks, and does it faster",
-    setOf(ModuleTag.PLAYER)
+    name = "PacketMine",
+    description = "automatically breaks blocks, and does it faster",
+    tag = ModuleTag.PLAYER
 ) {
     private val page by setting("Page", Page.Build)
 
@@ -274,4 +274,4 @@ object PacketMine : Module(
         State,
         Box
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/com/lambda/module/modules/render/FreeLook.kt b/src/main/kotlin/com/lambda/module/modules/render/FreeLook.kt
index f7163f9a3..3a22d59d7 100644
--- a/src/main/kotlin/com/lambda/module/modules/render/FreeLook.kt
+++ b/src/main/kotlin/com/lambda/module/modules/render/FreeLook.kt
@@ -17,11 +17,10 @@
 
 package com.lambda.module.modules.render
 
-import com.lambda.Lambda
 import com.lambda.Lambda.mc
 import com.lambda.event.events.PlayerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
-import com.lambda.interaction.request.rotation.Rotation
+import com.lambda.interaction.request.rotating.Rotation
 import com.lambda.module.Module
 import com.lambda.module.tag.ModuleTag
 import com.lambda.util.extension.rotation
diff --git a/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt
index 3468d3668..6914b2dd9 100644
--- a/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt
+++ b/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt
@@ -17,11 +17,11 @@
 
 package com.lambda.task.tasks
 
-import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.context.SafeContext
 import com.lambda.interaction.material.StackSelection
 import com.lambda.interaction.material.container.ContainerManager
 import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial
+import com.lambda.interaction.request.inventory.InventoryConfig
 import com.lambda.module.modules.client.TaskFlowModule
 import com.lambda.task.Task
 
diff --git a/src/main/kotlin/com/lambda/util/BlockUtils.kt b/src/main/kotlin/com/lambda/util/BlockUtils.kt
index f43d660bc..120ff5b0e 100644
--- a/src/main/kotlin/com/lambda/util/BlockUtils.kt
+++ b/src/main/kotlin/com/lambda/util/BlockUtils.kt
@@ -28,6 +28,7 @@ import net.minecraft.block.BeaconBlock
 import net.minecraft.block.BedBlock
 import net.minecraft.block.BeehiveBlock
 import net.minecraft.block.BellBlock
+import net.minecraft.block.Block
 import net.minecraft.block.BlockState
 import net.minecraft.block.Blocks
 import net.minecraft.block.BrewingStandBlock
@@ -82,6 +83,7 @@ import net.minecraft.block.TrapdoorBlock
 import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.fluid.FluidState
 import net.minecraft.fluid.Fluids
+import net.minecraft.item.Item
 import net.minecraft.item.ItemStack
 import net.minecraft.state.property.Property
 import net.minecraft.util.math.*
@@ -248,4 +250,10 @@ object BlockUtils {
     val BlockState.emptyState: BlockState get() = fluidState.blockState
     fun isBroken(oldState: BlockState, newState: BlockState) = oldState.isNotEmpty && oldState.emptyState.matches(newState)
     fun isNotBroken(oldState: BlockState, newState: BlockState) = !isBroken(oldState, newState)
+
+    val Vec3i.blockPos: BlockPos get() = BlockPos(this)
+    val Block.item: Item get() = asItem()
+    fun BlockPos.vecOf(direction: Direction): Vec3d = toCenterPos().add(Vec3d.of(direction.vector).multiply(0.5))
+    fun BlockPos.offset(eightWayDirection: EightWayDirection, amount: Int): BlockPos =
+        add(eightWayDirection.offsetX * amount, 0, eightWayDirection.offsetZ * amount)
 }

From 5d060fb751f8dcb207e8aef497145018a6c40aa2 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 01:52:59 +0100
Subject: [PATCH 350/364] cap texture overlay progress at 9

---
 .../kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index c0ea46392..d5c68991a 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -132,7 +132,7 @@ data class BreakInfo(
         val item = if (swapMode.isEnabled() && swapMode != BreakConfig.SwapMode.Start) player.inventory.getStack(context.hotbarIndex) else player.mainHandStack
         val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
         val progress = (breakDelta * breakingTicks) / (getBreakThreshold() + (breakDelta * breakConfig.fudgeFactor))
-        return if (progress > 0.0f) (progress * 10.0f).toInt().coerceAtMost(10) else -1
+        return if (progress > 0.0f) (progress * 10.0f).toInt().coerceAtMost(9) else -1
     }
 
     fun getBreakThreshold() = type.getBreakThreshold(breakConfig)

From 84f15134a3ea041b3ac16ad1f75002ca7f3ddf59 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 01:59:56 +0100
Subject: [PATCH 351/364] tick stage check in shouldSwap

---
 .../kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index d5c68991a..4fe8733d1 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -103,6 +103,7 @@ data class BreakInfo(
     }
 
     fun shouldSwap(player: ClientPlayerEntity, world: WorldView): Boolean {
+        if (BreakManager.tickStage !in breakConfig.breakStageMask) return false
         val item = player.inventory.getStack(context.hotbarIndex)
         val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
         val breakProgress = breakDelta * ((breakingTicks + 1) - breakConfig.fudgeFactor).let {

From 7a102e969568dfaafffb72a85f287a0ac9b6f67a Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 02:10:27 +0100
Subject: [PATCH 352/364] move shouldProgress into its own variable for use in
 swap checks

---
 .../interaction/request/breaking/BreakInfo.kt      |  3 ++-
 .../interaction/request/breaking/BreakManager.kt   | 14 ++++++++------
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 4fe8733d1..2b4f63fe1 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -49,6 +49,7 @@ data class BreakInfo(
         } == true
     }
 
+    var shouldProgress = false
     var breaking = false
     var abandoned = false
     var breakingTicks = 0
@@ -103,7 +104,7 @@ data class BreakInfo(
     }
 
     fun shouldSwap(player: ClientPlayerEntity, world: WorldView): Boolean {
-        if (BreakManager.tickStage !in breakConfig.breakStageMask) return false
+        if (!shouldProgress) return false
         val item = player.inventory.getStack(context.hotbarIndex)
         val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
         val breakProgress = breakDelta * ((breakingTicks + 1) - breakConfig.fudgeFactor).let {
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 3b3ef952c..b4fc1167a 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -299,7 +299,12 @@ object BreakManager : RequestHandler(
                             }
                     }
                     .also  {
-                        it.forEach { it.couldReBreak.update() }
+                        it.forEach {
+                            it.couldReBreak.update()
+                            it.shouldProgress = !it.progressedThisTick &&
+                                    tickStage in it.breakConfig.breakStageMask &&
+                                    rotated || !it.isPrimary
+                        }
                     }
                     .also {
                         if (breakInfos.none { it?.shouldSwap(player, world) == true }) return@also
@@ -329,10 +334,7 @@ object BreakManager : RequestHandler(
                     }
                     .asReversed()
                     .forEach { info ->
-                        if (info.progressedThisTick) return@forEach
-                        if (tickStage !in info.breakConfig.breakStageMask) return@forEach
-                        if (!rotated && info.isPrimary) return@run
-
+                        if (!info.shouldProgress) return@forEach
                         updateBreakProgress(info)
                     }
             }
@@ -814,7 +816,7 @@ object BreakManager : RequestHandler(
         config: BreakConfig,
         item: ItemStack? = null
     ) = runSafe {
-        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.selectedStack)
+        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.mainHandStack)
         //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
 //        if (config.desyncFix) {
 //            val nextTickPrediction = buildPlayerPrediction().next()

From 9df5ac4de8a7f00f5147ab24fceccf72e23a50b6 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 03:03:01 +0100
Subject: [PATCH 353/364] almost working block break speed. Might need to
 rework the hotbar manager to account for the new selected slot logic
 bypassing  serverSlot

---
 .../request/breaking/BreakManager.kt          |  5 +-
 .../request/breaking/ReBreakManager.kt        |  3 +-
 src/main/kotlin/com/lambda/util/BlockUtils.kt | 55 ++++++++++++++++++-
 3 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index b4fc1167a..fb292628a 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -62,6 +62,7 @@ import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending
+import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.interacting.InteractionManager
 import com.lambda.interaction.request.placing.PlaceManager
 import com.lambda.interaction.request.rotating.RotationRequest
@@ -809,14 +810,14 @@ object BreakManager : RequestHandler(
         return true
     }
 
-    private fun BlockState.calcBreakDelta(
+    fun BlockState.calcBreakDelta(
         player: ClientPlayerEntity,
         world: BlockView,
         pos: BlockPos,
         config: BreakConfig,
         item: ItemStack? = null
     ) = runSafe {
-        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.mainHandStack)
+        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.getStack(HotbarManager.serverSlot))
         //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
 //        if (config.desyncFix) {
 //            val nextTickPrediction = buildPlayerPrediction().next()
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
index 663eb8199..f5f98113c 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
@@ -22,6 +22,7 @@ import com.lambda.event.events.TickEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
 import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
 import com.lambda.interaction.construction.context.BreakContext
+import com.lambda.interaction.request.breaking.BreakManager.calcBreakDelta
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
 import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
@@ -86,7 +87,7 @@ object ReBreakManager {
             reBreak.updateInfo(ctx, breakRequest)
 
             val context = reBreak.context
-            val breakDelta = context.cachedState.calcBlockBreakingDelta(player, world, context.blockPos)
+            val breakDelta = context.cachedState.calcBreakDelta(player, world, context.blockPos, reBreak.breakConfig)
             return@runSafe if ((reBreak.breakingTicks - reBreak.breakConfig.fudgeFactor) * breakDelta >= reBreak.breakConfig.breakThreshold) {
                 if (reBreak.breakConfig.breakConfirmation != BreakConfig.BreakConfirmationMode.AwaitThenBreak) {
                     destroyBlock(reBreak)
diff --git a/src/main/kotlin/com/lambda/util/BlockUtils.kt b/src/main/kotlin/com/lambda/util/BlockUtils.kt
index 120ff5b0e..97475142d 100644
--- a/src/main/kotlin/com/lambda/util/BlockUtils.kt
+++ b/src/main/kotlin/com/lambda/util/BlockUtils.kt
@@ -18,6 +18,7 @@
 package com.lambda.util
 
 import com.lambda.context.SafeContext
+import com.lambda.util.EnchantmentUtils.getEnchantment
 import com.lambda.util.player.gamemode
 import net.minecraft.block.AbstractCauldronBlock
 import net.minecraft.block.AbstractFurnaceBlock
@@ -80,11 +81,16 @@ import net.minecraft.block.StructureBlock
 import net.minecraft.block.SweetBerryBushBlock
 import net.minecraft.block.TntBlock
 import net.minecraft.block.TrapdoorBlock
+import net.minecraft.enchantment.Enchantments
+import net.minecraft.entity.attribute.EntityAttributes
+import net.minecraft.entity.effect.StatusEffectUtil
+import net.minecraft.entity.effect.StatusEffects
 import net.minecraft.entity.player.PlayerEntity
 import net.minecraft.fluid.FluidState
 import net.minecraft.fluid.Fluids
 import net.minecraft.item.Item
 import net.minecraft.item.ItemStack
+import net.minecraft.registry.tag.FluidTags
 import net.minecraft.state.property.Property
 import net.minecraft.util.math.*
 import net.minecraft.world.BlockView
@@ -242,7 +248,54 @@ object BlockUtils {
         world: BlockView,
         blockPos: BlockPos,
         item: ItemStack
-    ): Float = 0f
+    ): Float {
+        val hardness = getHardness(world, blockPos)
+        return if (hardness == -1.0f) 0.0f else {
+            val harvestMultiplier = if (item.canHarvest(this)) 30 else 100
+            player.getItemBlockBreakingSpeed(this, item) / hardness / harvestMultiplier
+        }
+    }
+
+    fun ItemStack.canHarvest(blockState: BlockState) =
+        !blockState.isToolRequired || isSuitableFor(blockState)
+
+    fun PlayerEntity.getItemBlockBreakingSpeed(blockState: BlockState, item: ItemStack): Float {
+        var speedMultiplier = item.getMiningSpeedMultiplier(blockState)
+        if (speedMultiplier > 1.0f) {
+            val level = item.getEnchantment(Enchantments.EFFICIENCY)
+            if (level > 0 && !item.isEmpty) {
+                speedMultiplier += (level * level + 1)
+            }
+        }
+
+        if (StatusEffectUtil.hasHaste(this)) {
+            speedMultiplier *= 1.0f + (StatusEffectUtil.getHasteAmplifier(this) + 1) * 0.2f
+        }
+
+        getStatusEffect(StatusEffects.MINING_FATIGUE)?.amplifier?.let { fatigue ->
+            val fatigueMultiplier = when (fatigue) {
+                0 -> 0.3f
+                1 -> 0.09f
+                2 -> 0.0027f
+                3 -> 8.1E-4f
+                else -> 8.1E-4f
+            }
+
+            speedMultiplier *= fatigueMultiplier
+        }
+
+        if (isSubmergedIn(FluidTags.WATER)) {
+            getAttributeInstance(EntityAttributes.SUBMERGED_MINING_SPEED)?.let { speed ->
+                speedMultiplier *= speed.getValue().toFloat()
+            }
+        }
+
+        if (!isOnGround) {
+            speedMultiplier /= 5.0f
+        }
+
+        return speedMultiplier
+    }
 
     val BlockState.isEmpty get() = matches(emptyState)
     val BlockState.isNotEmpty get() = !isEmpty

From eedafd9a947c5486212327abc5b4d1daed6783c4 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 11:10:42 +0100
Subject: [PATCH 354/364] add FixMe's to HotbarManager and EnchantmentUtils

---
 .../com/lambda/interaction/request/hotbar/HotbarManager.kt       | 1 +
 src/main/kotlin/com/lambda/util/EnchantmentUtils.kt              | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index 9b50cc492..14e6accf1 100644
--- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -28,6 +28,7 @@ import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap
 import com.lambda.threading.runSafe
 
+//FixMe: as of 1.21.5, player.mainHandStack no longer points back to the serverSlot's stack
 object HotbarManager : RequestHandler(
     1,
     TickEvent.Pre,
diff --git a/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt b/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
index 86c7688bb..c6f5ad5d5 100644
--- a/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
+++ b/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
@@ -40,6 +40,7 @@ object EnchantmentUtils {
         get() = !getOrDefault(DataComponentTypes.ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT).isEmpty
                 || getOrDefault(DataComponentTypes.STORED_ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT).isEmpty
 
+    //FixMe: doesn't work with, at least, efficiency on pickaxes
     /**
      * Returns the given enchantment level from a [net.minecraft.item.ItemStack]
      */

From c2f2ec1131827a5d403bca078258df776b11cc58 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 22:54:00 +0100
Subject: [PATCH 355/364] player.mainHandStack and, in theory, all other
 references to the players selected slot / stack will return the hotbar
 managers selection if there's an active request. This is different compared
 to the way it worked in 1.20.4 as previously it only altered the interaction
 managers selected slot

---
 .../mixin/entity/PlayerInventoryMixin.java    | 46 +++++++++++++++++++
 .../request/breaking/BreakManager.kt          |  5 +-
 .../request/hotbar/HotbarManager.kt           |  7 +--
 src/main/resources/lambda.mixins.common.json  |  1 +
 4 files changed, 50 insertions(+), 9 deletions(-)
 create mode 100644 src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java

diff --git a/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java b/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
new file mode 100644
index 000000000..aadc2041a
--- /dev/null
+++ b/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.mixin.entity;
+
+import com.lambda.interaction.request.hotbar.HotbarManager;
+import com.lambda.interaction.request.hotbar.HotbarRequest;
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import net.minecraft.entity.player.PlayerInventory;
+import org.objectweb.asm.Opcodes;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(PlayerInventory.class)
+public class PlayerInventoryMixin {
+    @SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference"})
+    @ModifyExpressionValue(method = "*", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/PlayerInventory;selectedSlot:I", opcode = Opcodes.GETFIELD))
+    private int modifySelectedSlot(int original) {
+        final HotbarRequest hotbarRequest = HotbarManager.INSTANCE.getActiveRequest();
+        if (hotbarRequest == null) return original;
+        return hotbarRequest.getSlot();
+    }
+
+    @Inject(method = "getSelectedSlot", at = @At("HEAD"), cancellable = true)
+    private void redirectGetSelectedSlot(CallbackInfoReturnable cir) {
+        final HotbarRequest hotbarRequest = HotbarManager.INSTANCE.getActiveRequest();
+        if (hotbarRequest == null) return;
+        cir.setReturnValue(hotbarRequest.getSlot());
+    }
+}
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index fb292628a..5a52fe943 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -62,7 +62,6 @@ import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.startPending
-import com.lambda.interaction.request.hotbar.HotbarManager
 import com.lambda.interaction.request.interacting.InteractionManager
 import com.lambda.interaction.request.placing.PlaceManager
 import com.lambda.interaction.request.rotating.RotationRequest
@@ -304,7 +303,7 @@ object BreakManager : RequestHandler(
                             it.couldReBreak.update()
                             it.shouldProgress = !it.progressedThisTick &&
                                     tickStage in it.breakConfig.breakStageMask &&
-                                    rotated || !it.isPrimary
+                                    (rotated || !it.isPrimary)
                         }
                     }
                     .also {
@@ -817,7 +816,7 @@ object BreakManager : RequestHandler(
         config: BreakConfig,
         item: ItemStack? = null
     ) = runSafe {
-        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.inventory.getStack(HotbarManager.serverSlot))
+        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.mainHandStack)
         //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
 //        if (config.desyncFix) {
 //            val nextTickPrediction = buildPlayerPrediction().next()
diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index 14e6accf1..4cdc46165 100644
--- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -20,7 +20,6 @@ package com.lambda.interaction.request.hotbar
 import com.lambda.context.SafeContext
 import com.lambda.event.Event
 import com.lambda.event.EventFlow.post
-import com.lambda.event.events.InventoryEvent
 import com.lambda.event.events.TickEvent
 import com.lambda.event.events.UpdateManagerEvent
 import com.lambda.event.listener.SafeListener.Companion.listen
@@ -45,7 +44,7 @@ object HotbarManager : RequestHandler(
     private var maxSwapsThisTick = 0
     private var swapDelay = 0
 
-    private var activeRequest: HotbarRequest? = null
+    var activeRequest: HotbarRequest? = null
 
     override fun load(): String {
         super.load()
@@ -60,10 +59,6 @@ object HotbarManager : RequestHandler(
             activeInfo.keepTicks--
         }
 
-        listen(priority = Int.MIN_VALUE) {
-            it.slot = activeRequest?.slot ?: return@listen
-        }
-
         return "Loaded Hotbar Manager"
     }
 
diff --git a/src/main/resources/lambda.mixins.common.json b/src/main/resources/lambda.mixins.common.json
index 3fd38ba27..1c344e20f 100644
--- a/src/main/resources/lambda.mixins.common.json
+++ b/src/main/resources/lambda.mixins.common.json
@@ -15,6 +15,7 @@
     "entity.FireworkRocketEntityMixin",
     "entity.LivingEntityMixin",
     "entity.PlayerEntityMixin",
+    "entity.PlayerInventoryMixin",
     "input.KeyBindingMixin",
     "input.KeyboardMixin",
     "input.MouseMixin",

From 6bf0c2671be8a1b2ab098ba6f8dd78d5abc705d3 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Fri, 8 Aug 2025 22:58:00 +0100
Subject: [PATCH 356/364] removed FixMe

---
 .../com/lambda/interaction/request/hotbar/HotbarManager.kt       | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
index 4cdc46165..4264d1d5a 100644
--- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt
@@ -27,7 +27,6 @@ import com.lambda.interaction.request.RequestHandler
 import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap
 import com.lambda.threading.runSafe
 
-//FixMe: as of 1.21.5, player.mainHandStack no longer points back to the serverSlot's stack
 object HotbarManager : RequestHandler(
     1,
     TickEvent.Pre,

From 1285ae0b4eacb7fa3f86ca91458b3f17a175b4a7 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sat, 9 Aug 2025 00:24:28 +0100
Subject: [PATCH 357/364] only snap to area if the players current rotation
 isn't in the desired direction

---
 .../request/rotating/visibilty/RotationTargets.kt          | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt
index 3109e5f2e..62c934a5e 100644
--- a/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/RotationTargets.kt
@@ -56,11 +56,8 @@ fun lookInDirection(direction: PlaceDirection) =
     RotationTarget(null, {
         PlaceDirection.fromRotation(RotationManager.activeRotation) == direction
     }) {
-        if (!direction.isInArea(RotationManager.activeRotation) || !direction.isInArea(player.rotation)) {
-            direction.snapToArea(RotationManager.activeRotation)
-        } else {
-            player.rotation
-        }
+        if (!direction.isInArea(player.rotation)) direction.snapToArea(RotationManager.activeRotation)
+        else player.rotation
     }
 
 /**

From 10bf9cddc604fd1cc10b95f0b0d1d7b075e95804 Mon Sep 17 00:00:00 2001
From: Edouard127 <46357922+Edouard127@users.noreply.github.com>
Date: Sat, 9 Aug 2025 13:38:29 -0400
Subject: [PATCH 358/364] Fixed enchantment functions

---
 src/main/kotlin/com/lambda/util/EnchantmentUtils.kt | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt b/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
index c6f5ad5d5..2e9571e09 100644
--- a/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
+++ b/src/main/kotlin/com/lambda/util/EnchantmentUtils.kt
@@ -40,13 +40,12 @@ object EnchantmentUtils {
         get() = !getOrDefault(DataComponentTypes.ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT).isEmpty
                 || getOrDefault(DataComponentTypes.STORED_ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT).isEmpty
 
-    //FixMe: doesn't work with, at least, efficiency on pickaxes
     /**
      * Returns the given enchantment level from a [net.minecraft.item.ItemStack]
      */
     fun ItemStack.getEnchantment(key: RegistryKey) =
         getOrDefault(DataComponentTypes.ENCHANTMENTS, ItemEnchantmentsComponent.DEFAULT)
-            .enchantmentEntries.find { it.key == key }
+            .enchantmentEntries.find { it.key?.matchesKey(key) == true }
             ?.intValue
             ?: 0
 

From 52b051a8b96dce77514bcd8398ecb154be4ac098 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 10 Aug 2025 00:20:32 +0100
Subject: [PATCH 359/364] no silent swapping for primary breaks >:(

Yet another thing ruined...
---
 .../interaction/request/breaking/BreakInfo.kt |  4 +-
 .../request/breaking/BreakManager.kt          | 53 ++++++++++---------
 2 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 2b4f63fe1..93437d7c7 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -107,9 +107,7 @@ data class BreakInfo(
         if (!shouldProgress) return false
         val item = player.inventory.getStack(context.hotbarIndex)
         val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
-        val breakProgress = breakDelta * ((breakingTicks + 1) - breakConfig.fudgeFactor).let {
-            if (isSecondary) it + 1 else it
-        }
+        val breakProgress = breakDelta * (breakingTicks + 1)
         return if (couldReBreak.value == true)
             breakConfig.swapMode.isEnabled()
         else when (breakConfig.swapMode) {
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 5a52fe943..10f28a12f 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -71,6 +71,7 @@ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.isNotEmpty
+import com.lambda.util.Communication.info
 import com.lambda.util.extension.partialTicks
 import com.lambda.util.item.ItemUtils.block
 import com.lambda.util.math.lerp
@@ -298,7 +299,7 @@ object BreakManager : RequestHandler(
                                 if (instantBreaks.isEmpty()) rotation.submit(false) else rotation
                             }
                     }
-                    .also  {
+                    .also {
                         it.forEach {
                             it.couldReBreak.update()
                             it.shouldProgress = !it.progressedThisTick &&
@@ -306,33 +307,32 @@ object BreakManager : RequestHandler(
                                     (rotated || !it.isPrimary)
                         }
                     }
+                    .asReversed()
                     .also {
-                        if (breakInfos.none { it?.shouldSwap(player, world) == true }) return@also
-
-                        it.firstOrNull()?.let { info ->
-                            secondaryBreak?.let { secondary ->
-                                val breakDelta = secondary.context.cachedState.calcBreakDelta(
-                                    player,
-                                    world,
-                                    secondary.context.blockPos,
-                                    secondary.breakConfig,
-                                    player.inventory.getStack(secondary.context.hotbarIndex)
-                                )
-                                val breakAmount = breakDelta * ((secondary.breakingTicks - secondary.breakConfig.fudgeFactor) + 1)
-                                val minKeepTicks = if (breakAmount >= 1.0f) 1 else 0
-                                if (!info.context.requestSwap(info.request, minKeepTicks)) {
-                                    secondary.serverBreakTicks = 0
-                                    return@run
-                                }
-                                if (minKeepTicks > 0) {
-                                    secondary.serverBreakTicks++
-                                }
-                                return@also
+                        it.firstOrNull { it.shouldSwap(player, world) }?.let { info ->
+                            val breakDelta = info.context.cachedState.calcBreakDelta(
+                                player,
+                                world,
+                                info.context.blockPos,
+                                info.breakConfig,
+                                player.inventory.getStack(info.context.hotbarIndex)
+                            )
+                            val breakAmount = breakDelta * info.breakingTicks
+                            val minKeepTicks = if (breakAmount >= info.getBreakThreshold() &&
+                                info.serverBreakTicks < info.breakConfig.fudgeFactor)
+                            {
+                                1
+                            } else 0
+
+                            if (!info.context.requestSwap(info.request, minKeepTicks)) {
+                                info.serverBreakTicks = 0
+                                return@run
+                            }
+                            if (minKeepTicks > 0) {
+                                info.serverBreakTicks++
                             }
-                            if (!info.context.requestSwap(info.request)) return@run
                         }
                     }
-                    .asReversed()
                     .forEach { info ->
                         if (!info.shouldProgress) return@forEach
                         updateBreakProgress(info)
@@ -640,6 +640,8 @@ object BreakManager : RequestHandler(
         val config = info.breakConfig
         val ctx = info.context
 
+        info.progressedThisTick = true
+
         if (!info.breaking) {
             if (!startBreaking(info)) {
                 info.nullify()
@@ -653,7 +655,6 @@ object BreakManager : RequestHandler(
             return true
         }
 
-        info.progressedThisTick = true
         val hitResult = ctx.result
 
         if (gamemode.isCreative && world.worldBorder.contains(ctx.blockPos)) {
@@ -710,7 +711,7 @@ object BreakManager : RequestHandler(
         }
 
         val swing = config.swing
-        if (overBreakThreshold && (!info.isSecondary || info.serverBreakTicks >= info.breakConfig.fudgeFactor + 1)) {
+        if (overBreakThreshold && info.serverBreakTicks >= info.breakConfig.fudgeFactor) {
             if (info.isPrimary) {
                 onBlockBreak(info)
                 info.stopBreakPacket(world, interaction)

From ab924867fc5eb9ec7da11325fdaaf4826a864a89 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 10 Aug 2025 00:27:05 +0100
Subject: [PATCH 360/364] default fudge factor to 2

---
 src/main/kotlin/com/lambda/config/groups/BreakSettings.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index c2c06ba75..828f87280 100644
--- a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -45,7 +45,7 @@ class BreakSettings(
 
     // Fixes / Delays
     override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken") { vis() && page == Page.General }
-    override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
+    override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break") { vis() && page == Page.General }
 //    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
     override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks") { vis() && page == Page.General }
 

From 30e1bebb5ae3a75e0a2dfc3c1ea21a9b0321c6c5 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 10 Aug 2025 02:31:16 +0100
Subject: [PATCH 361/364] efficiency check for minKeepTicks

---
 .../request/breaking/BreakManager.kt          | 31 +++++++++++++------
 src/main/kotlin/com/lambda/util/BlockUtils.kt |  9 +++---
 2 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 10f28a12f..6ed767c0f 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -310,15 +310,27 @@ object BreakManager : RequestHandler(
                     .asReversed()
                     .also {
                         it.firstOrNull { it.shouldSwap(player, world) }?.let { info ->
-                            val breakDelta = info.context.cachedState.calcBreakDelta(
+                            val cachedState = info.context.cachedState
+                            val swapStack = player.inventory.getStack(info.context.hotbarIndex)
+
+                            val breakAmount = cachedState.calcBreakDelta(
+                                player,
+                                world,
+                                info.context.blockPos,
+                                info.breakConfig,
+                                swapStack
+                            ) * info.breakingTicks
+                            val breakAmountNoEfficiency = cachedState.calcBreakDelta(
                                 player,
                                 world,
                                 info.context.blockPos,
                                 info.breakConfig,
-                                player.inventory.getStack(info.context.hotbarIndex)
-                            )
-                            val breakAmount = breakDelta * info.breakingTicks
-                            val minKeepTicks = if (breakAmount >= info.getBreakThreshold() &&
+                                swapStack,
+                                ignoreEfficiency = true
+                            ) * info.breakingTicks
+
+                            val minKeepTicks = if ((breakAmount >= info.getBreakThreshold() || info.couldReBreak.value == true) &&
+                                breakAmountNoEfficiency < info.getBreakThreshold() &&
                                 info.serverBreakTicks < info.breakConfig.fudgeFactor)
                             {
                                 1
@@ -334,8 +346,8 @@ object BreakManager : RequestHandler(
                         }
                     }
                     .forEach { info ->
-                        if (!info.shouldProgress) return@forEach
-                        updateBreakProgress(info)
+                        if (info.shouldProgress)
+                            updateBreakProgress(info)
                     }
             }
         }
@@ -815,9 +827,10 @@ object BreakManager : RequestHandler(
         world: BlockView,
         pos: BlockPos,
         config: BreakConfig,
-        item: ItemStack? = null
+        item: ItemStack? = null,
+        ignoreEfficiency: Boolean = false
     ) = runSafe {
-        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.mainHandStack)
+        val delta = calcItemBlockBreakingDelta(player, world, pos, item ?: player.mainHandStack, ignoreEfficiency)
         //ToDo: This setting requires some fixes / improvements in the player movement prediction to work properly. Currently, it's broken
 //        if (config.desyncFix) {
 //            val nextTickPrediction = buildPlayerPrediction().next()
diff --git a/src/main/kotlin/com/lambda/util/BlockUtils.kt b/src/main/kotlin/com/lambda/util/BlockUtils.kt
index 97475142d..39dd07e39 100644
--- a/src/main/kotlin/com/lambda/util/BlockUtils.kt
+++ b/src/main/kotlin/com/lambda/util/BlockUtils.kt
@@ -247,22 +247,23 @@ object BlockUtils {
         player: PlayerEntity,
         world: BlockView,
         blockPos: BlockPos,
-        item: ItemStack
+        item: ItemStack,
+        ignoreEfficiency: Boolean = false
     ): Float {
         val hardness = getHardness(world, blockPos)
         return if (hardness == -1.0f) 0.0f else {
             val harvestMultiplier = if (item.canHarvest(this)) 30 else 100
-            player.getItemBlockBreakingSpeed(this, item) / hardness / harvestMultiplier
+            player.getItemBlockBreakingSpeed(this, item, ignoreEfficiency) / hardness / harvestMultiplier
         }
     }
 
     fun ItemStack.canHarvest(blockState: BlockState) =
         !blockState.isToolRequired || isSuitableFor(blockState)
 
-    fun PlayerEntity.getItemBlockBreakingSpeed(blockState: BlockState, item: ItemStack): Float {
+    fun PlayerEntity.getItemBlockBreakingSpeed(blockState: BlockState, item: ItemStack, ignoreEfficiency: Boolean = false): Float {
         var speedMultiplier = item.getMiningSpeedMultiplier(blockState)
         if (speedMultiplier > 1.0f) {
-            val level = item.getEnchantment(Enchantments.EFFICIENCY)
+            val level = if (ignoreEfficiency) 0 else item.getEnchantment(Enchantments.EFFICIENCY)
             if (level > 0 && !item.isEmpty) {
                 speedMultiplier += (level * level + 1)
             }

From 8c3324d67a3c0245f26d7ffcaf16b50fe8648d0b Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Sun, 10 Aug 2025 15:37:58 +0100
Subject: [PATCH 362/364] fix nuker

---
 src/main/kotlin/com/lambda/module/modules/player/Nuker.kt | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
index 5ebec0f7f..0f5c8457e 100644
--- a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
+++ b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt
@@ -26,6 +26,7 @@ import com.lambda.task.RootTask.run
 import com.lambda.task.Task
 import com.lambda.task.tasks.BuildTask.Companion.build
 import com.lambda.util.BaritoneUtils
+import com.lambda.util.BlockUtils.blockPos
 import com.lambda.util.BlockUtils.blockState
 import net.minecraft.util.math.BlockPos
 
@@ -49,6 +50,7 @@ object Nuker : Module(
             task = tickingBlueprint {
                 val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width)
                     .asSequence()
+                    .map { it.blockPos }
                     .filter { !world.isAir(it) }
                     .filter { !flatten || it.y >= player.blockPos.y }
                     .filter { !instantOnly || blockState(it).getHardness(world, it) <= TaskFlowModule.build.breaking.breakThreshold }
@@ -66,6 +68,7 @@ object Nuker : Module(
 
                 if (fillFloor) {
                     val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width)
+                        .map { it.blockPos }
                         .associateWith { TargetState.Solid }
                     return@tickingBlueprint selection + floor
                 }

From adfbb462acf9d16177bd593922cba4dba9a48836 Mon Sep 17 00:00:00 2001
From: beanbag44 
Date: Tue, 12 Aug 2025 22:28:06 +0100
Subject: [PATCH 363/364] more goofy not working fixes

---
 .../interaction/request/breaking/BreakInfo.kt |  58 ++--
 .../request/breaking/BreakManager.kt          | 264 ++++++++++--------
 .../request/breaking/BrokenBlockHandler.kt    |   4 +-
 .../request/breaking/ReBreakManager.kt        |   4 -
 .../kotlin/com/lambda/util/OneSetPerTick.kt   |  81 ++++++
 5 files changed, 253 insertions(+), 158 deletions(-)
 create mode 100644 src/main/kotlin/com/lambda/util/OneSetPerTick.kt

diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
index 93437d7c7..99c46a3df 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt
@@ -19,60 +19,58 @@ package com.lambda.interaction.request.breaking
 
 import com.lambda.interaction.construction.context.BreakContext
 import com.lambda.interaction.request.ActionInfo
-import com.lambda.threading.runSafe
 import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
-import com.lambda.util.collections.updatableLazy
+import com.lambda.util.OneSetPerTick
 import net.minecraft.client.network.ClientPlayerEntity
 import net.minecraft.client.network.ClientPlayerInteractionManager
 import net.minecraft.client.world.ClientWorld
 import net.minecraft.entity.ItemEntity
 import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.ItemStack
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
 import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action
-import net.minecraft.world.WorldView
+import net.minecraft.world.BlockView
 
 data class BreakInfo(
     override var context: BreakContext,
     var type: BreakType,
     var request: BreakRequest
 ) : ActionInfo {
+    // Delegates
     val breakConfig get() = request.build.breaking
     override val pendingInteractionsList get() = request.pendingInteractions
 
-    var updatedThisTick = true
-    var progressedThisTick = false
+    // Pre Processing
+    var shouldProgress = false
+    var couldReBreak by OneSetPerTick(false, true)
+    var shouldSwap by OneSetPerTick(false, true)
+    var swapStack: ItemStack by OneSetPerTick(ItemStack.EMPTY, true)
+    var minSwapTicks by OneSetPerTick(0, true)
     var serverBreakTicks = 0
 
-    var couldReBreak = updatableLazy {
-        runSafe {
-            ReBreakManager.couldReBreak(this@BreakInfo, player, world)
-        } == true
-    }
+    // BreakInfo Specific
+    var updatedThisTick by OneSetPerTick(false, resetAfterTick = true).apply { set(true) }
+    var updatedPreProcessingThisTick by OneSetPerTick(false, true, true)
+    var progressedThisTick by OneSetPerTick(false, true, true)
 
-    var shouldProgress = false
+    // Processing
     var breaking = false
     var abandoned = false
-    var breakingTicks = 0
-    var soundsCooldown = 0.0f
-
+    var breakingTicks by OneSetPerTick(0, true)
+    var soundsCooldown by OneSetPerTick(0f, true)
     var vanillaInstantBreakable = false
-    val reBreakable get() = !vanillaInstantBreakable && isPrimary
-
-    val isPrimary get() = type == BreakType.Primary
-    val isSecondary get() = type == BreakType.Secondary
-    val isRedundant get() = type == BreakType.RedundantSecondary
-    val isReBreaking get() = type == BreakType.ReBreak
+    val reBreakable get() = !vanillaInstantBreakable && type == BreakType.Primary
 
+    // Post Processing
     @Volatile
     var broken = false; private set
     private var item: ItemEntity? = null
-
     val callbacksCompleted
         @Synchronized get() = broken && (request.onItemDrop == null || item != null)
 
     @Synchronized
     fun internalOnBreak() {
-        if (!isReBreaking) broken = true
+        if (type != BreakType.ReBreak) broken = true
         item?.let { item ->
             request.onItemDrop?.invoke(item)
         }
@@ -80,8 +78,8 @@ data class BreakInfo(
 
     @Synchronized
     fun internalOnItemDrop(item: ItemEntity) {
-        if (!isReBreaking) this.item = item
-        if (broken || isReBreaking) {
+        if (type != BreakType.ReBreak) this.item = item
+        if (broken || type == BreakType.ReBreak) {
             request.onItemDrop?.invoke(item)
         }
     }
@@ -90,12 +88,7 @@ data class BreakInfo(
         updatedThisTick = true
         this.context = context
         request?.let { this.request = it }
-        if (isRedundant) type = BreakType.Secondary
-    }
-
-    fun tickStats() {
-        updatedThisTick = false
-        progressedThisTick = false
+        if (type == BreakType.RedundantSecondary) type = BreakType.Secondary
     }
 
     fun resetCallbacks() {
@@ -103,12 +96,11 @@ data class BreakInfo(
         item = null
     }
 
-    fun shouldSwap(player: ClientPlayerEntity, world: WorldView): Boolean {
-        if (!shouldProgress) return false
+    fun shouldSwap(player: ClientPlayerEntity, world: BlockView): Boolean {
         val item = player.inventory.getStack(context.hotbarIndex)
         val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
         val breakProgress = breakDelta * (breakingTicks + 1)
-        return if (couldReBreak.value == true)
+        return if (couldReBreak)
             breakConfig.swapMode.isEnabled()
         else when (breakConfig.swapMode) {
             BreakConfig.SwapMode.None -> false
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
index 6ed767c0f..d343d94c5 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt
@@ -58,6 +58,8 @@ import com.lambda.interaction.request.breaking.BreakManager.simulateAbandoned
 import com.lambda.interaction.request.breaking.BreakManager.updateBreakProgress
 import com.lambda.interaction.request.breaking.BreakType.Primary
 import com.lambda.interaction.request.breaking.BreakType.ReBreak
+import com.lambda.interaction.request.breaking.BreakType.Secondary
+import com.lambda.interaction.request.breaking.BreakType.RedundantSecondary
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions
 import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs
@@ -71,7 +73,6 @@ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
 import com.lambda.util.BlockUtils.isEmpty
 import com.lambda.util.BlockUtils.isNotBroken
 import com.lambda.util.BlockUtils.isNotEmpty
-import com.lambda.util.Communication.info
 import com.lambda.util.extension.partialTicks
 import com.lambda.util.item.ItemUtils.block
 import com.lambda.util.math.lerp
@@ -88,6 +89,10 @@ import net.minecraft.util.Hand
 import net.minecraft.util.math.BlockPos
 import net.minecraft.util.math.Box
 import net.minecraft.world.BlockView
+import kotlin.collections.firstOrNull
+import kotlin.collections.forEach
+import kotlin.collections.lastOrNull
+import kotlin.math.max
 
 object BreakManager : RequestHandler(
     0,
@@ -108,7 +113,7 @@ object BreakManager : RequestHandler(
     val currentStackSelection
         get() = breakInfos
             .lastOrNull {
-                it?.isRedundant == false && (it.breakConfig.doubleBreak || it.isSecondary)
+                it != null && it.type != RedundantSecondary && (it.breakConfig.doubleBreak || it.type == Secondary)
             }?.context?.itemSelection
             ?: StackSelection.EVERYTHING.select()
 
@@ -121,6 +126,18 @@ object BreakManager : RequestHandler(
     private var rotationRequest: RotationRequest? = null
     private val rotated get() = rotationRequest?.done != false
 
+    private var swapped = false
+        set(value) {
+            field = value
+            if (!value)
+                breakInfos.forEach { it?.serverBreakTicks = 0 }
+        }
+    var swappedStack: ItemStack = ItemStack.EMPTY
+        set(value) {
+            if (value != field)
+                breakInfos.forEach { it?.serverBreakTicks = 0 }
+            field = value
+        }
     private var breakCooldown = 0
     var breaksThisTick = 0
     private var maxBreaksThisTick = 0
@@ -149,9 +166,6 @@ object BreakManager : RequestHandler(
             if (breakCooldown > 0) {
                 breakCooldown--
             }
-            breakInfos.forEach { info ->
-                info?.tickStats()
-            }
             activeRequest = null
             breaks = mutableListOf()
             instantBreaks = mutableListOf()
@@ -173,7 +187,7 @@ object BreakManager : RequestHandler(
                         return@listen
                     }
                     destroyBlock(info)
-                    if (info.isRedundant) {
+                    if (info.type == RedundantSecondary) {
                         info.nullify()
                         return@listen
                     }
@@ -219,11 +233,11 @@ object BreakManager : RequestHandler(
                         world,
                         info.context.blockPos,
                         info.breakConfig,
-                        if (!info.isRedundant && swapMode.isEnabled() && swapMode != BreakConfig.SwapMode.Start) activeStack else null
+                        if (info.type != RedundantSecondary && swapMode.isEnabled() && swapMode != BreakConfig.SwapMode.Start) activeStack else null
                     ).toDouble()
                     val currentDelta = info.breakingTicks * breakDelta
 
-                    val threshold = if (info.isPrimary) info.breakConfig.breakThreshold else 1f
+                    val threshold = if (info.type == Primary) info.breakConfig.breakThreshold else 1f
                     val adjustedThreshold = threshold + (breakDelta * config.fudgeFactor)
 
                     val currentProgress = currentDelta / adjustedThreshold
@@ -280,7 +294,6 @@ object BreakManager : RequestHandler(
         repeat(2) {
             breakRequest?.let { request ->
                 if (request.fresh) populateFrom(request)
-
                 if (performInstantBreaks(request)) {
                     processNewBreaks(request)
                 }
@@ -289,62 +302,11 @@ object BreakManager : RequestHandler(
             // Reversed so that the breaking order feels natural to the user as the primary break is always the
             // last break to be started
             run {
+                if (!handlePreProcessing()) return@run
                 breakInfos
                     .filterNotNull()
-                    .filter { !it.isRedundant && it.updatedThisTick }
-                    .also {
-                        rotationRequest = it.firstOrNull { info -> info.breakConfig.rotateForBreak }
-                            ?.let { info ->
-                                val rotation = info.context.rotation
-                                if (instantBreaks.isEmpty()) rotation.submit(false) else rotation
-                            }
-                    }
-                    .also {
-                        it.forEach {
-                            it.couldReBreak.update()
-                            it.shouldProgress = !it.progressedThisTick &&
-                                    tickStage in it.breakConfig.breakStageMask &&
-                                    (rotated || !it.isPrimary)
-                        }
-                    }
+                    .filter { it.type != RedundantSecondary && it.updatedThisTick }
                     .asReversed()
-                    .also {
-                        it.firstOrNull { it.shouldSwap(player, world) }?.let { info ->
-                            val cachedState = info.context.cachedState
-                            val swapStack = player.inventory.getStack(info.context.hotbarIndex)
-
-                            val breakAmount = cachedState.calcBreakDelta(
-                                player,
-                                world,
-                                info.context.blockPos,
-                                info.breakConfig,
-                                swapStack
-                            ) * info.breakingTicks
-                            val breakAmountNoEfficiency = cachedState.calcBreakDelta(
-                                player,
-                                world,
-                                info.context.blockPos,
-                                info.breakConfig,
-                                swapStack,
-                                ignoreEfficiency = true
-                            ) * info.breakingTicks
-
-                            val minKeepTicks = if ((breakAmount >= info.getBreakThreshold() || info.couldReBreak.value == true) &&
-                                breakAmountNoEfficiency < info.getBreakThreshold() &&
-                                info.serverBreakTicks < info.breakConfig.fudgeFactor)
-                            {
-                                1
-                            } else 0
-
-                            if (!info.context.requestSwap(info.request, minKeepTicks)) {
-                                info.serverBreakTicks = 0
-                                return@run
-                            }
-                            if (minKeepTicks > 0) {
-                                info.serverBreakTicks++
-                            }
-                        }
-                    }
                     .forEach { info ->
                         if (info.shouldProgress)
                             updateBreakProgress(info)
@@ -355,53 +317,11 @@ object BreakManager : RequestHandler(
         if (instantBreaks.isEmpty() && breaks.isEmpty()) {
             activeRequest = null
         }
-        if (breaksThisTick > 0 || breakInfos.any { it != null && !it.isRedundant }) {
+        if (breaksThisTick > 0 || breakInfos.any { it != null && it.type != RedundantSecondary }) {
             activeThisTick = true
         }
     }
 
-    private fun SafeContext.simulateAbandoned() {
-        // Cancelled but double breaking so requires break manager to continue the simulation
-        breakInfos
-            .asSequence()
-            .filterNotNull()
-            .filter { it.abandoned && !it.isRedundant }
-            .forEach { info ->
-                with (info.request) {
-                    info.context.blockPos
-                        .toStructure(TargetState.Empty)
-                        .toBlueprint()
-                        .simulate(player.eyePos, interact, rotation, inventory, build)
-                        .asSequence()
-                        .filterIsInstance()
-                        .filter { canAccept(it.context) }
-                        .sorted()
-                        .let { sim ->
-                            info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
-                        }
-                }
-            }
-    }
-
-    private fun SafeContext.checkForCancels() {
-        breakInfos
-            .filterNotNull()
-            .asSequence()
-            .filter { !it.updatedThisTick && tickStage in it.breakConfig.breakStageMask }
-            .forEach { info ->
-                if (info.isRedundant && !info.progressedThisTick) {
-                    val cachedState = info.context.cachedState
-                    if (cachedState.isEmpty || cachedState.isAir) {
-                        info.nullify()
-                        return@forEach
-                    }
-                    info.progressedThisTick = true
-                    info.breakingTicks++
-                }
-                else info.cancelBreak()
-            }
-    }
-
     /**
      * Filters the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches
      * in positions. If a match is found, the [BreakInfo] is updated with the new context. Otherwise, the break is cancelled.
@@ -423,7 +343,7 @@ object BreakManager : RequestHandler(
                 newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos && canAccept(ctx) }?.let { ctx ->
                     if (!info.updatedThisTick || info.abandoned) {
                         info.updateInfo(ctx, request)
-                        if (info.isRedundant)
+                        if (info.type == RedundantSecondary)
                             info.request.onStart?.invoke(info.context.blockPos)
                         else if (info.abandoned) {
                             info.abandoned = false
@@ -464,6 +384,43 @@ object BreakManager : RequestHandler(
         return blockState.isNotEmpty && hardness != 600f && hardness != -1f
     }
 
+    private fun SafeContext.handlePreProcessing(): Boolean {
+        breakInfos
+            .filterNotNull()
+            .filter { it.type != RedundantSecondary && it.updatedThisTick }
+            .let { infos ->
+                rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak }
+                    ?.let { info ->
+                        val rotation = info.context.rotation
+                        rotation.submit(false)
+                    }
+
+                if (breakInfos.none { it != null && it.type != RedundantSecondary }) {
+                    swapped = false
+                    swappedStack = player.mainHandStack
+                    return true
+                }
+
+                infos.forEach {
+                    it.updatePreProcessing(player, world)
+                }
+                infos.firstOrNull()?.let { info ->
+                    infos.firstOrNull { it.shouldSwap && it.shouldProgress }?.let { last ->
+                        if (!info.context.requestSwap(info.request, max(info.minSwapTicks, last.minSwapTicks))) {
+                            swapped = false
+                            return false
+                        }
+                        swappedStack = info.swapStack
+                        swapped = true
+                        info.serverBreakTicks++
+                        return true
+                    }
+                }
+            }
+
+        return true
+    }
+
     /**
      * Attempts to break as many [BreakContext]'s as possible from the [instantBreaks] collection within this tick.
      *
@@ -478,11 +435,12 @@ object BreakManager : RequestHandler(
 
             if (!canAccept(ctx)) continue
 
-            if (request.build.breaking.swapMode.isEnabled() && !ctx.requestSwap(request)) return false
             rotationRequest = if (request.config.rotateForBreak) ctx.rotation.submit(false) else null
             if (!rotated || tickStage !in request.config.breakStageMask) return false
 
             val breakInfo = initNewBreak(ctx, request) ?: return false
+            if (!handlePreProcessing()) return false
+
             updateBreakProgress(breakInfo)
             iterator.remove()
         }
@@ -530,7 +488,7 @@ object BreakManager : RequestHandler(
             }
 
             if (!primaryInfo.breaking) {
-                secondaryBreak = breakInfo.apply { type = BreakType.Secondary }
+                secondaryBreak = breakInfo.apply { type = Secondary }
                 return secondaryBreak
             }
 
@@ -544,6 +502,48 @@ object BreakManager : RequestHandler(
         return primaryBreak
     }
 
+    private fun SafeContext.simulateAbandoned() {
+        // Cancelled but double breaking so requires break manager to continue the simulation
+        breakInfos
+            .asSequence()
+            .filterNotNull()
+            .filter { it.abandoned && it.type != RedundantSecondary }
+            .forEach { info ->
+                with (info.request) {
+                    info.context.blockPos
+                        .toStructure(TargetState.Empty)
+                        .toBlueprint()
+                        .simulate(player.eyePos, interact, rotation, inventory, build)
+                        .asSequence()
+                        .filterIsInstance()
+                        .filter { canAccept(it.context) }
+                        .sorted()
+                        .let { sim ->
+                            info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
+                        }
+                }
+            }
+    }
+
+    private fun SafeContext.checkForCancels() {
+        breakInfos
+            .filterNotNull()
+            .asSequence()
+            .filter { !it.updatedThisTick && tickStage in it.breakConfig.breakStageMask }
+            .forEach { info ->
+                if (info.type == RedundantSecondary && !info.progressedThisTick) {
+                    val cachedState = info.context.cachedState
+                    if (cachedState.isEmpty || cachedState.isAir) {
+                        info.nullify()
+                        return@forEach
+                    }
+                    info.progressedThisTick = true
+                    info.breakingTicks++
+                }
+                else info.cancelBreak()
+            }
+    }
+
     /**
      * Begins the post-break logic sequence for the given [info].
      *
@@ -586,13 +586,38 @@ object BreakManager : RequestHandler(
         info.nullify()
     }
 
+    private fun BreakInfo.updatePreProcessing(player: ClientPlayerEntity, world: BlockView) {
+        shouldProgress = !progressedThisTick &&
+                tickStage in breakConfig.breakStageMask &&
+                (rotated || type != Primary)
+
+        if (updatedPreProcessingThisTick) return
+        updatedPreProcessingThisTick = true
+
+        couldReBreak = ReBreakManager.couldReBreak(this, player, world)
+        shouldSwap = shouldSwap(player, world)
+
+        val cachedState = context.cachedState
+        swapStack = player.inventory.getStack(context.hotbarIndex)
+
+        val breakAmount = cachedState.calcBreakDelta(player, world, context.blockPos, breakConfig, swapStack) * (breakingTicks + 1)
+        val breakAmountNoEfficiency = cachedState.calcBreakDelta(player, world, context.blockPos, breakConfig, swapStack, ignoreEfficiency = true) * (breakingTicks + 1)
+
+        minSwapTicks = if (breakAmount >= getBreakThreshold() || couldReBreak) {
+            val min = if (breakAmountNoEfficiency >= getBreakThreshold()) 0
+            else 1
+            serverBreakTicks++
+            min
+        } else 0
+    }
+
     /**
      * Makes the [BreakInfo] a secondary if not already.
      */
     private fun BreakInfo.makeSecondary() {
         if (secondaryBreak === this) return
         secondaryBreak = this.apply {
-            type = BreakType.Secondary
+            type = Secondary
         }
         primaryBreak = null
     }
@@ -609,15 +634,15 @@ object BreakManager : RequestHandler(
      */
     private fun BreakInfo.cancelBreak() =
         runSafe {
-            if (isRedundant || abandoned) return@runSafe
-            if (isPrimary) {
+            if (type == RedundantSecondary || abandoned) return@runSafe
+            if (type == Primary) {
                 nullify()
                 setBreakingTextureStage(player, world, -1)
                 abortBreakPacket(world, interaction)
                 request.onCancel?.invoke(context.blockPos)
-            } else if (isSecondary) {
+            } else if (type == Secondary) {
                 if (breakConfig.unsafeCancels) {
-                    type = BreakType.RedundantSecondary
+                    type = RedundantSecondary
                     setBreakingTextureStage(player, world, -1)
                     request.onCancel?.invoke(context.blockPos)
                 } else {
@@ -661,7 +686,7 @@ object BreakManager : RequestHandler(
                 return false
             }
             val swing = config.swing
-            if (swing.isEnabled() && (swing != BreakConfig.SwingMode.End || info.isReBreaking)) {
+            if (swing.isEnabled() && (swing != BreakConfig.SwingMode.End || info.type == ReBreak)) {
                 swingHand(config.swingType, Hand.MAIN_HAND)
             }
             return true
@@ -723,8 +748,8 @@ object BreakManager : RequestHandler(
         }
 
         val swing = config.swing
-        if (overBreakThreshold && info.serverBreakTicks >= info.breakConfig.fudgeFactor) {
-            if (info.isPrimary) {
+        if (overBreakThreshold && (!swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor)) {
+            if (info.type == Primary) {
                 onBlockBreak(info)
                 info.stopBreakPacket(world, interaction)
             } else {
@@ -749,7 +774,7 @@ object BreakManager : RequestHandler(
     private fun SafeContext.startBreaking(info: BreakInfo): Boolean {
         val ctx = info.context
 
-        if (info.couldReBreak.value == true) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
+        if (info.couldReBreak) when (val reBreakResult = ReBreakManager.handleUpdate(info.context, info.request)) {
             is ReBreakResult.StillBreaking -> {
                 primaryBreak = reBreakResult.breakInfo.apply {
                     type = Primary
@@ -795,7 +820,8 @@ object BreakManager : RequestHandler(
 
         val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig)
         info.vanillaInstantBreakable = breakDelta >= 1
-        if (notEmpty && breakDelta >= info.getBreakThreshold()) {
+        val serverSwapped = !swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor
+        if (notEmpty && (breakDelta >= info.getBreakThreshold() && serverSwapped)) {
             onBlockBreak(info)
             if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay
         } else {
@@ -815,7 +841,7 @@ object BreakManager : RequestHandler(
 
         info.startBreakPacket(world, interaction)
 
-        if (info.isSecondary || (!info.vanillaInstantBreakable && breakDelta >= info.breakConfig.breakThreshold)) {
+        if (info.type == Secondary || (!info.vanillaInstantBreakable && breakDelta >= info.breakConfig.breakThreshold)) {
             info.stopBreakPacket(world, interaction)
         }
 
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
index dfa02fef6..1ced45580 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt
@@ -83,7 +83,7 @@ object BrokenBlockHandler : PostActionHandler() {
                         return@listen
                     }
 
-                    if (pending.isReBreaking) {
+                    if (pending.type == BreakType.ReBreak) {
                         pending.context.cachedState = event.newState
                     } else {
                         this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}")
@@ -93,7 +93,7 @@ object BrokenBlockHandler : PostActionHandler() {
                 }
 
                 if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak
-                    || (pending.isReBreaking && !pending.breakConfig.reBreak)
+                    || (pending.type == BreakType.ReBreak && !pending.breakConfig.reBreak)
                     ) {
                     destroyBlock(pending)
                 }
diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
index f5f98113c..9782f528c 100644
--- a/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
+++ b/src/main/kotlin/com/lambda/interaction/request/breaking/ReBreakManager.kt
@@ -44,10 +44,6 @@ object ReBreakManager {
             }
         }
 
-        listen(priority = Int.MIN_VALUE) {
-            reBreak?.tickStats()
-        }
-
         listenUnsafe(priority = Int.MIN_VALUE) {
             reBreak = null
         }
diff --git a/src/main/kotlin/com/lambda/util/OneSetPerTick.kt b/src/main/kotlin/com/lambda/util/OneSetPerTick.kt
new file mode 100644
index 000000000..157db9a27
--- /dev/null
+++ b/src/main/kotlin/com/lambda/util/OneSetPerTick.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2025 Lambda
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see .
+ */
+
+package com.lambda.util
+
+import com.lambda.event.events.TickEvent
+import com.lambda.event.listener.SafeListener.Companion.listen
+import kotlin.reflect.KProperty
+
+class OneSetPerTick(
+    private var value: T,
+    private val throwOnLimitBreach: Boolean = false,
+    private val resetAfterTick: Boolean = false
+) {
+    val defaultValue = value
+
+    var setThisTick = false
+        private set
+
+    var destroyed = false
+        private set
+
+    init {
+        instances.add(this)
+    }
+
+    @Suppress("Unused")
+    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
+        if (destroyed) throw IllegalStateException("Value accessed after being destroyed")
+        return value ?: throw UninitializedPropertyAccessException()
+    }
+
+    @Suppress("Unused")
+    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) = set(newValue)
+
+    fun set(newValue: T) {
+        if (destroyed) throw IllegalStateException("Value set after being destroyed")
+        if (setThisTick && newValue != value) {
+            if (throwOnLimitBreach) throw IllegalStateException("Value already written this tick")
+            return
+        }
+        setThisTick = true
+        value = newValue
+    }
+
+    private fun reset() {
+        value = defaultValue
+    }
+
+    fun destroy() {
+        destroyed = true
+        instances.remove(this)
+    }
+
+    companion object {
+        private val instances = linkedSetOf>()
+
+        init {
+            listen(priority = Int.MIN_VALUE) {
+                instances.forEach {
+                    it.setThisTick = false
+                    if (it.resetAfterTick) it.reset()
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

From 8734884e5191454584f8e98a4f2ebb0b3748a160 Mon Sep 17 00:00:00 2001
From: Constructor 
Date: Wed, 13 Aug 2025 00:25:38 +0200
Subject: [PATCH 364/364] Fixed grouping

---
 .../com/lambda/config/AbstractSetting.kt      |  4 +
 .../com/lambda/config/groups/BreakSettings.kt | 84 +++++++++----------
 .../com/lambda/config/groups/BuildSettings.kt | 22 ++---
 .../lambda/config/groups/InteractSettings.kt  | 10 +--
 .../com/lambda/config/groups/PlaceSettings.kt | 22 ++---
 .../lambda/module/modules/combat/KillAura.kt  |  2 +-
 .../module/modules/player/PacketMine.kt       | 39 ++++-----
 .../lambda/module/modules/player/Scaffold.kt  |  2 +-
 8 files changed, 93 insertions(+), 92 deletions(-)

diff --git a/src/main/kotlin/com/lambda/config/AbstractSetting.kt b/src/main/kotlin/com/lambda/config/AbstractSetting.kt
index 81d2d9c42..7f585cb73 100644
--- a/src/main/kotlin/com/lambda/config/AbstractSetting.kt
+++ b/src/main/kotlin/com/lambda/config/AbstractSetting.kt
@@ -150,6 +150,10 @@ abstract class AbstractSetting(
         listeners.add(ValueListener(false, block))
     }
 
+    fun group(path: List, vararg continuation: NamedEnum) = apply {
+        groups.add(path + continuation)
+    }
+
     fun group(vararg path: NamedEnum) = apply {
         groups.add(path.toList())
     }
diff --git a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
index 549d20d88..2959904e9 100644
--- a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt
@@ -31,8 +31,8 @@ import java.awt.Color
 
 class BreakSettings(
     c: Configurable,
-    baseGroup: NamedEnum,
-    vis: () -> Boolean = { true }
+    groupPath: List = emptyList(),
+    vis: () -> Boolean = { true },
 ) : BreakConfig {
     enum class Group(override val displayName: String) : NamedEnum {
         General("General"),
@@ -40,68 +40,68 @@ class BreakSettings(
     }
 
     // General
-    override val breakMode by c.setting("Break Mode", BreakMode.Packet, visibility = vis).group(baseGroup, Group.General)
-    override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed", visibility = vis).group(baseGroup, Group.General)
-    override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", visibility = vis).group(baseGroup, Group.General)
+    override val breakMode by c.setting("Break Mode", BreakMode.Packet, visibility = vis).group(groupPath, Group.General)
+    override val sorter by c.setting("Sorter", SortMode.Closest, "The order in which breaks are performed", visibility = vis).group(groupPath, Group.General)
+    override val reBreak by c.setting("ReBreak", true, "Re-breaks blocks after they've been broken once", visibility = vis).group(groupPath, Group.General)
 
     // Double break
-    override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis).group(baseGroup, Group.General)
-    override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes", visibility = vis).group(baseGroup, Group.General)
+    override val doubleBreak by c.setting("Double Break", true, "Allows breaking two blocks at once", visibility = vis).group(groupPath, Group.General)
+    override val unsafeCancels by c.setting("Unsafe Cancels", true, "Allows cancelling block breaking even if the server might continue breaking sever side, potentially causing unexpected state changes", visibility = vis).group(groupPath, Group.General)
 
     // Fixes / Delays
-    override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis).group(baseGroup, Group.General)
-    override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis).group(baseGroup, Group.General)
+    override val breakThreshold by c.setting("Break Threshold", 0.70f, 0.1f..1.0f, 0.01f, "The break amount at which the block is considered broken", visibility = vis).group(groupPath, Group.General)
+    override val fudgeFactor by c.setting("Fudge Factor", 2, 0..5, 1, "The amount of ticks to give double, aka secondary breaks extra for the server to recognise the break", visibility = vis).group(groupPath, Group.General)
 //    override val desyncFix by c.setting("Desync Fix", false, "Predicts if the players breaking will be slowed next tick as block break packets are processed using the players next position") { vis() && page == Page.General }
-    override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis).group(baseGroup, Group.General)
+    override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " ticks", visibility = vis).group(groupPath, Group.General)
 
     // Timing
-    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), description = "The sub-tick timing at which break actions can be performed", visibility = vis).group(baseGroup, Group.General)
+    override val breakStageMask by c.setting("Break Stage Mask", setOf(TickEvent.Input.Post, TickEvent.Player.Post), description = "The sub-tick timing at which break actions can be performed", visibility = vis).group(groupPath, Group.General)
 
     // Swap
-    override val swapMode by c.setting("Swap Mode", BreakConfig.SwapMode.End, "Decides when to swap to the best suited tool when breaking a block", visibility = vis).group(baseGroup, Group.General)
+    override val swapMode by c.setting("Swap Mode", BreakConfig.SwapMode.End, "Decides when to swap to the best suited tool when breaking a block", visibility = vis).group(groupPath, Group.General)
 
     // Swing
-    override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis).group(baseGroup, Group.General)
-    override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None }.group(baseGroup, Group.General)
+    override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand", visibility = vis).group(groupPath, Group.General)
+    override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing != SwingMode.None }.group(groupPath, Group.General)
 
     // Rotate
-    override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", visibility = vis).group(baseGroup, Group.General)
+    override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", visibility = vis).group(groupPath, Group.General)
 
     // Pending / Post
-    override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking", visibility = vis).group(baseGroup, Group.General)
-    override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick", visibility = vis).group(baseGroup, Group.General)
-    override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks", visibility = vis).group(baseGroup, Group.General)
+    override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking", visibility = vis).group(groupPath, Group.General)
+    override val breaksPerTick by c.setting("Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick", visibility = vis).group(groupPath, Group.General)
+    override val maxPendingBreaks by c.setting("Max Pending Breaks", 15, 1..30, 1, "The maximum amount of pending breaks", visibility = vis).group(groupPath, Group.General)
 
     // Block
-    override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill", visibility = vis).group(baseGroup, Group.General)
-    override val avoidSupporting by c.setting("Avoid Supporting", true, "Avoids breaking the block supporting the player", visibility = vis).group(baseGroup, Group.General)
-    override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", visibility = vis).group(baseGroup, Group.General)
-    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, description = "Blocks that wont be broken", visibility = vis).group(baseGroup, Group.General)
+    override val avoidLiquids by c.setting("Avoid Liquids", true, "Avoids breaking blocks that would cause liquid to spill", visibility = vis).group(groupPath, Group.General)
+    override val avoidSupporting by c.setting("Avoid Supporting", true, "Avoids breaking the block supporting the player", visibility = vis).group(groupPath, Group.General)
+    override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", visibility = vis).group(groupPath, Group.General)
+    override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, description = "Blocks that wont be broken", visibility = vis).group(groupPath, Group.General)
 
     // Tool
-    override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis).group(baseGroup, Group.General)
-    override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis).group(baseGroup, Group.General)
-    override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis).group(baseGroup, Group.General)
-    override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe }.group(baseGroup, Group.General)
+    override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis).group(groupPath, Group.General)
+    override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis).group(groupPath, Group.General)
+    override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis).group(groupPath, Group.General)
+    override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe }.group(groupPath, Group.General)
 
     // Cosmetics
-    override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds", visibility = vis).group(baseGroup, Group.Cosmetic)
-    override val particles by c.setting("Particles", true, "Renders the breaking particles", visibility = vis).group(baseGroup, Group.Cosmetic)
-    override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages", visibility = vis).group(baseGroup, Group.Cosmetic)
+    override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds", visibility = vis).group(groupPath, Group.Cosmetic)
+    override val particles by c.setting("Particles", true, "Renders the breaking particles", visibility = vis).group(groupPath, Group.Cosmetic)
+    override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages", visibility = vis).group(groupPath, Group.Cosmetic)
     // Modes
-    override val renders by c.setting("Renders", true, "Enables the render settings for breaking progress", visibility = vis).group(baseGroup, Group.Cosmetic)
-    override val animation by c.setting("Animation", AnimationMode.Out, "The style of animation used for the box") { vis() && renders }.group(baseGroup, Group.Cosmetic)
+    override val renders by c.setting("Renders", true, "Enables the render settings for breaking progress", visibility = vis).group(groupPath, Group.Cosmetic)
+    override val animation by c.setting("Animation", AnimationMode.Out, "The style of animation used for the box") { vis() && renders }.group(groupPath, Group.Cosmetic)
     // Fill
-    override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && renders }.group(baseGroup, Group.Cosmetic)
-    override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && renders && fill }.group(baseGroup, Group.Cosmetic)
-    override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill") { vis() && renders && !dynamicFillColor && fill }.group(baseGroup, Group.Cosmetic)
-    override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill at the start of breaking") { vis()  && renders && dynamicFillColor && fill }.group(baseGroup, Group.Cosmetic)
-    override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60).brighter(), "The color of the fill at the end of breaking") { vis() && renders && dynamicFillColor && fill }.group(baseGroup, Group.Cosmetic)
+    override val fill by c.setting("Fill", true, "Renders the sides of the box to display break progress") { vis() && renders }.group(groupPath, Group.Cosmetic)
+    override val dynamicFillColor by c.setting("Dynamic Colour", true, "Enables fill color interpolation from start to finish for fill when breaking a block") { vis() && renders && fill }.group(groupPath, Group.Cosmetic)
+    override val staticFillColor by c.setting("Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill") { vis() && renders && !dynamicFillColor && fill }.group(groupPath, Group.Cosmetic)
+    override val startFillColor by c.setting("Start Fill Color", Color(255, 0, 0, 60).brighter(), "The color of the fill at the start of breaking") { vis()  && renders && dynamicFillColor && fill }.group(groupPath, Group.Cosmetic)
+    override val endFillColor by c.setting("End Fill Color", Color(0, 255, 0, 60).brighter(), "The color of the fill at the end of breaking") { vis() && renders && dynamicFillColor && fill }.group(groupPath, Group.Cosmetic)
     // Outline
-    override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && renders }.group(baseGroup, Group.Cosmetic)
-    override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && renders && outline }.group(baseGroup, Group.Cosmetic)
-    override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && renders && outline }.group(baseGroup, Group.Cosmetic)
-    override val staticOutlineColor by c.setting("Outline Color", Color.RED.brighter(), "The Color of the outline at the start of breaking") { vis() && renders && !dynamicOutlineColor && outline }.group(baseGroup, Group.Cosmetic)
-    override val startOutlineColor by c.setting("Start Outline Color", Color.RED.brighter(), "The color of the outline at the start of breaking") { vis() && renders && dynamicOutlineColor && outline }.group(baseGroup, Group.Cosmetic)
-    override val endOutlineColor by c.setting("End Outline Color", Color.GREEN.brighter(), "The color of the outline at the end of breaking") { vis() && renders && dynamicOutlineColor && outline }.group(baseGroup, Group.Cosmetic)
+    override val outline by c.setting("Outline", true, "Renders the lines of the box to display break progress") { vis() && renders }.group(groupPath, Group.Cosmetic)
+    override val outlineWidth by c.setting("Outline Width", 2, 0..5, 1, "The width of the outline") { vis() && renders && outline }.group(groupPath, Group.Cosmetic)
+    override val dynamicOutlineColor by c.setting("Dynamic Outline Color", true, "Enables color interpolation from start to finish for the outline when breaking a block") { vis() && renders && outline }.group(groupPath, Group.Cosmetic)
+    override val staticOutlineColor by c.setting("Outline Color", Color.RED.brighter(), "The Color of the outline at the start of breaking") { vis() && renders && !dynamicOutlineColor && outline }.group(groupPath, Group.Cosmetic)
+    override val startOutlineColor by c.setting("Start Outline Color", Color.RED.brighter(), "The color of the outline at the start of breaking") { vis() && renders && dynamicOutlineColor && outline }.group(groupPath, Group.Cosmetic)
+    override val endOutlineColor by c.setting("End Outline Color", Color.GREEN.brighter(), "The color of the outline at the end of breaking") { vis() && renders && dynamicOutlineColor && outline }.group(groupPath, Group.Cosmetic)
 }
diff --git a/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
index ea53c91d5..0f673d10f 100644
--- a/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt
@@ -24,8 +24,8 @@ import com.lambda.interaction.request.placing.PlaceConfig
 
 class BuildSettings(
     c: Configurable,
-    baseGroup: NamedEnum,
-    vis: () -> Boolean = { true }
+    vararg groupPath: NamedEnum,
+    vis: () -> Boolean = { true },
 ) : BuildConfig {
     enum class Group(override val displayName: String) : NamedEnum {
         General("General"),
@@ -35,24 +35,24 @@ class BuildSettings(
     }
 
     // General
-    override val pathing by c.setting("Pathing", true, "Path to blocks", vis).group(baseGroup, Group.General)
-    override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks", vis).group(baseGroup, Group.General)
-    override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis).group(baseGroup, Group.General)
-    override val interactionsPerTick by c.setting("Interactions Per Tick", 5, 1..30, 1, "The amount of interactions that can happen per tick", visibility = vis).group(baseGroup, Group.General)
-    override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response", visibility = vis).group(baseGroup, Group.General)
+    override val pathing by c.setting("Pathing", true, "Path to blocks", vis).group(*groupPath, Group.General)
+    override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks", vis).group(*groupPath, Group.General)
+    override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis).group(*groupPath, Group.General)
+    override val interactionsPerTick by c.setting("Interactions Per Tick", 5, 1..30, 1, "The amount of interactions that can happen per tick", visibility = vis).group(*groupPath, Group.General)
+    override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response", visibility = vis).group(*groupPath, Group.General)
 
     // Breaking
-    override val breaking = BreakSettings(c, Group.Break, vis)
+    override val breaking = BreakSettings(c, groupPath.toList() + Group.Break, vis)
 
     // Placing
-    override val placing = PlaceSettings(c, Group.Place, vis)
+    override val placing = PlaceSettings(c, groupPath.toList() + Group.Place, vis)
 
     //Interacting
-    override val interacting = InteractSettings(c, Group.Interact, vis)
+    override val interacting = InteractSettings(c, groupPath.toList() + Group.Interact, vis)
 
     override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") {
         vis() && (placing.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None
                 || breaking.breakConfirmation != BreakConfirmationMode.None
                 || interacting.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.None)
-    }.group(baseGroup, Group.Break, Group.Place, Group.Interact)
+    }.group(*groupPath, Group.Break).group(*groupPath, Group.Place).group(*groupPath, Group.Interact)
 }
diff --git a/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt b/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
index 1281453e6..2cc98d016 100644
--- a/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/InteractSettings.kt
@@ -23,11 +23,11 @@ import com.lambda.util.NamedEnum
 
 class InteractSettings(
     c: Configurable,
-    baseGroup: NamedEnum,
+    groupPath: List = emptyList(),
     vis: () -> Boolean =  { true }
 ) : InteractConfig {
-    override val rotate by c.setting("Rotate For Interact", true, "Rotates the player to look at the block when interacting", visibility = vis).group(baseGroup)
-    override val swingHand by c.setting("Swing On Interact", true, "Swings the players hand after interacting", visibility = vis).group(baseGroup)
-    override val interactSwingType by c.setting("Interact Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swingHand }.group(baseGroup)
-    override val interactConfirmationMode by c.setting("Interact Confirmation Mode", InteractionConfig.InteractConfirmationMode.InteractThenAwait, "The style of confirmation for interactions", visibility = vis).group(baseGroup)
+    override val rotate by c.setting("Rotate For Interact", true, "Rotates the player to look at the block when interacting", visibility = vis).group(groupPath)
+    override val swingHand by c.setting("Swing On Interact", true, "Swings the players hand after interacting", visibility = vis).group(groupPath)
+    override val interactSwingType by c.setting("Interact Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swingHand }.group(groupPath)
+    override val interactConfirmationMode by c.setting("Interact Confirmation Mode", InteractionConfig.InteractConfirmationMode.InteractThenAwait, "The style of confirmation for interactions", visibility = vis).group(groupPath)
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt b/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
index 1c3ac09c7..139186442 100644
--- a/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
+++ b/src/main/kotlin/com/lambda/config/groups/PlaceSettings.kt
@@ -26,17 +26,17 @@ import com.lambda.util.NamedEnum
 
 class PlaceSettings(
     c: Configurable,
-    baseGroup: NamedEnum,
+    groupPath: List = emptyList(),
     vis: () -> Boolean = { true }
 ) : PlaceConfig {
-    override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", visibility = vis).group(baseGroup)
-    override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces", visibility = vis).group(baseGroup)
-    override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() }.group(baseGroup)
-    override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), description = "The sub-tick timing at which break actions are performed", visibility = vis).group(baseGroup)
-    override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation", visibility = vis).group(baseGroup)
-    override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements", visibility = vis).group(baseGroup)
-    override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick", visibility = vis).group(baseGroup)
-    override val swing by c.setting("Swing On Place", true, "Swings the players hand when placing", visibility = vis).group(baseGroup)
-    override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing }.group(baseGroup)
-    override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds", visibility = vis).group(baseGroup)
+    override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", visibility = vis).group(groupPath)
+    override val airPlace by c.setting("Air Place", AirPlaceMode.None, "Allows for placing blocks without adjacent faces", visibility = vis).group(groupPath)
+    override val axisRotateSetting by c.setting("Axis Rotate", true, "Overrides the Rotate For Place setting and rotates the player on each axis to air place rotational blocks") { vis() && airPlace.isEnabled() }.group(groupPath)
+    override val placeStageMask by c.setting("Place Sequence Mode", setOf(TickEvent.Pre, TickEvent.Input.Pre, TickEvent.Player.Post), description = "The sub-tick timing at which break actions are performed", visibility = vis).group(groupPath)
+    override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation", visibility = vis).group(groupPath)
+    override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements", visibility = vis).group(groupPath)
+    override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick", visibility = vis).group(groupPath)
+    override val swing by c.setting("Swing On Place", true, "Swings the players hand when placing", visibility = vis).group(groupPath)
+    override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing }.group(groupPath)
+    override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds", visibility = vis).group(groupPath)
 }
diff --git a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
index 0be24141e..f7043ba24 100644
--- a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
+++ b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
@@ -51,7 +51,7 @@ object KillAura : Module(
 ) {
     // Interact
     private val interactionSettings = InteractionSettings(this, Group.Interaction, InteractionMask.Entity)
-    private val interactSettings = InteractSettings(this, Group.Interact)
+    private val interactSettings = InteractSettings(this, listOf(Group.Interact))
     private val swap by setting("Swap", true, "Swap to the item with the highest damage")
     private val attackMode by setting("Attack Mode", AttackMode.Cooldown).group(Group.Interact)
     private val cooldownOffset by setting("Cooldown Offset", 0, -5..5, 1) { attackMode == AttackMode.Cooldown }.group(Group.Interact)
diff --git a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
index a11a87bf4..8c5c697b3 100644
--- a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
+++ b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt
@@ -55,17 +55,21 @@ object PacketMine : Module(
     tag = ModuleTag.PLAYER
 ) {
     private enum class Group(override val displayName: String) : NamedEnum {
-        Build("General"),
+        General("General"),
+        Build("Build"),
         Rotation("Rotation"),
         Interaction("Interaction"),
-        Inventory("Interact"),
-        Hotbar("Hotbar")
+        Inventory("Inventory"),
+        Hotbar("Hotbar"),
+        Render("Render")
     }
 
-    private enum class PacketMineGroup(override val displayName: String) : NamedEnum {
-        General("General"),
-        Cosmetic("Cosmetic")
-    }
+    private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak }.group(Group.General)
+    private val breakRadius by setting("Break Radius", 0, 0..5, 1, "Selects and breaks all blocks within the break radius of the selected block").group(Group.General)
+    private val flatten by setting("Flatten", true, "Wont allow breaking extra blocks under your players position") { breakRadius > 0 }.group(Group.General)
+    private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once").group(Group.General)
+        .onValueChange { _, to -> if (!to) queuePositions.clear() }
+    private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue }.group(Group.General)
 
     private val build = BuildSettings(this, Group.Build)
     private val breakConfig = build.breaking
@@ -74,20 +78,13 @@ object PacketMine : Module(
     private val inventory = InventorySettings(this, Group.Inventory)
     private val hotbar = HotbarSettings(this, Group.Hotbar)
 
-    private val reBreakMode by setting("ReBreak Mode", ReBreakMode.Manual, "The method used to re-break blocks after they've been broken once") { breakConfig.reBreak }.group(BuildSettings.Group.Break, PacketMineGroup.General)
-    private val breakRadius by setting("Break Radius", 0, 0..5, 1, "Selects and breaks all blocks within the break radius of the selected block").group(BuildSettings.Group.Break, PacketMineGroup.General)
-    private val flatten by setting("Flatten", true, "Wont allow breaking extra blocks under your players position") { breakRadius > 0 }.group(BuildSettings.Group.Break, PacketMineGroup.General)
-    private val queue by setting("Queue", false, "Queues blocks to break so you can select multiple at once").group(BuildSettings.Group.Break, PacketMineGroup.General)
-        .onValueChange { _, to -> if (!to) queuePositions.clear() }
-    private val queueOrder by setting("Queue Order", QueueOrder.Standard, "Which end of the queue to break blocks from") { queue }.group(BuildSettings.Group.Break, PacketMineGroup.General)
-    private val renderQueue by setting("Render Queue", true, "Adds renders to signify what block positions are queued").group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-    private val renderSize by setting("Render Size", 0.3f, 0.01f..1f, 0.01f, "The scale of the queue renders") { renderQueue }.group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-    private val renderMode by setting("Render Mode", RenderMode.State, "The style of the queue renders") { renderQueue }.group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-    private val dynamicColor by setting("Dynamic Color", true, "Interpolates the color between start and end") { renderQueue }.group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-    private val staticColor by setting("Color", Color(255, 0, 0, 60).brighter()) { renderQueue && !dynamicColor }.group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-    private val startColor by setting("Start Color", Color(255, 255, 0, 60).brighter(), "The color of the start (closest to breaking) of the queue") { renderQueue && dynamicColor }.group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-    private val endColor by setting("End Color", Color(255, 0, 0, 60).brighter(), "The color of the end (farthest from breaking) of the queue") { renderQueue && dynamicColor }.group(BuildSettings.Group.Break, PacketMineGroup.Cosmetic)
-
+    private val renderQueue by setting("Render Queue", true, "Adds renders to signify what block positions are queued").group(Group.Render)
+    private val renderSize by setting("Render Size", 0.3f, 0.01f..1f, 0.01f, "The scale of the queue renders") { renderQueue }.group(Group.Render)
+    private val renderMode by setting("Render Mode", RenderMode.State, "The style of the queue renders") { renderQueue }.group(Group.Render)
+    private val dynamicColor by setting("Dynamic Color", true, "Interpolates the color between start and end") { renderQueue }.group(Group.Render)
+    private val staticColor by setting("Color", Color(255, 0, 0, 60).brighter()) { renderQueue && !dynamicColor }.group(Group.Render)
+    private val startColor by setting("Start Color", Color(255, 255, 0, 60).brighter(), "The color of the start (closest to breaking) of the queue") { renderQueue && dynamicColor }.group(Group.Render)
+    private val endColor by setting("End Color", Color(255, 0, 0, 60).brighter(), "The color of the end (farthest from breaking) of the queue") { renderQueue && dynamicColor }.group(Group.Render)
 
     private val pendingInteractions = ConcurrentLinkedQueue()
 
diff --git a/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
index 51e632a8d..a32cd7642 100644
--- a/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
+++ b/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt
@@ -87,7 +87,7 @@ object Scaffold : Module(
     private val optimalPitch by setting("Optimal Pitch", 81.0, 70.0..85.0, 0.05).group(Group.Rotation)
 
     private val interactionConfig = InteractionSettings(this, Group.Interaction, InteractionMask.Block)
-    private val interactConfig = InteractSettings(this, Group.Interact)
+    private val interactConfig = InteractSettings(this, listOf(Group.Interact))
 
     // Placement
     private var placeInfo: PlaceInfo? = null