From 09b96debff7a920c313c0b5cefe112267710ecde Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 18 Aug 2025 03:28:51 +0100 Subject: [PATCH 1/8] move almost all swap data and logic into the SwapInfo class, and add new serverSwap setting. this seems to be on par with 1.20.4 consistency but there are improvements to be made regarding speed --- .../com/lambda/config/groups/BreakSettings.kt | 5 +- .../request/breaking/BreakConfig.kt | 3 +- .../interaction/request/breaking/BreakInfo.kt | 35 ++------ .../request/breaking/BreakManager.kt | 59 ++++-------- .../request/breaking/RebreakManager.kt | 4 +- .../interaction/request/breaking/SwapInfo.kt | 90 +++++++++++++++++++ 6 files changed, 123 insertions(+), 73 deletions(-) create mode 100644 src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt diff --git a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index eb2ddfdbc..d90b8ad4a 100644 --- a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -50,9 +50,10 @@ 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", 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 fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The number of ticks to add to the break time, usually to account for server lag", visibility = vis).group(groupPath, Group.General) + override val serverSwapTicks by c.setting("Server Swap", 1, 0..5, 1, "The number of ticks to give the server time to recognize the player attributes on the swapped item", " tick(s)", 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(groupPath, Group.General) + override val breakDelay by c.setting("Break Delay", 0, 0..6, 1, "The delay between breaking blocks", " tick(s)", 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(groupPath, Group.General) diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 7d95dfc26..275944380 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -28,13 +28,14 @@ import java.awt.Color interface BreakConfig : RequestConfig { val breakMode: BreakMode val sorter: SortMode - val breakThreshold: Float val rebreak: Boolean val doubleBreak: Boolean val unsafeCancels: Boolean + val breakThreshold: Float val fudgeFactor: Int + val serverSwapTicks: Int //ToDo: Needs a more advanced player simulation implementation to predict the next ticks onGround / submerged status // abstract val desyncFix: Boolean val breakDelay: Int 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 efa8f4a90..fe3ae90ec 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/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.interaction.request.breaking.BreakInfo.BreakType.Primary import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.Describable import com.lambda.util.NamedEnum @@ -31,7 +32,6 @@ 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.BlockView data class BreakInfo( override var context: BreakContext, @@ -44,10 +44,9 @@ data class BreakInfo( // Pre Processing var shouldProgress = false - var couldReBreak by OneSetPerTick(value = RebreakManager.RebreakPotential.None, throwOnLimitBreach = true) - var shouldSwap by OneSetPerTick(value = false, throwOnLimitBreach = true) + var rebreakPotential by OneSetPerTick(value = RebreakManager.RebreakPotential.None, throwOnLimitBreach = true) + var swapInfo by OneSetPerTick(value = SwapInfo.EMPTY, throwOnLimitBreach = true) var swapStack: ItemStack by OneSetPerTick(ItemStack.EMPTY, true) - var minSwapTicks by OneSetPerTick(0, true) // BreakInfo Specific var updatedThisTick by OneSetPerTick(false, resetAfterTick = true).apply { set(true) } @@ -60,7 +59,7 @@ data class BreakInfo( var breakingTicks by OneSetPerTick(0, true) var soundsCooldown by OneSetPerTick(0f, true) var vanillaInstantBreakable = false - val rebreakable get() = !vanillaInstantBreakable && type == BreakType.Primary + val rebreakable get() = !vanillaInstantBreakable && type == Primary enum class BreakType( override val displayName: String, @@ -70,12 +69,6 @@ data class BreakInfo( Secondary("Secondary", "A second block broken at the same time (when double‑break is enabled)."), RedundantSecondary("Redundant Secondary", "A previously started secondary break that’s now ignored/monitored only (no new actions)."), Rebreak("Rebreak", "A previously broken block which new breaks in the same position can compound progression on. Often rebreaking instantly."); - - fun getBreakThreshold(breakConfig: BreakConfig) = - when (this) { - Primary -> breakConfig.breakThreshold - else -> 1.0f - } } // Post Processing @@ -113,20 +106,6 @@ data class BreakInfo( item = null } - fun shouldSwap(player: ClientPlayerEntity, world: BlockView): Boolean { - val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, swapStack) - val breakProgress = breakDelta * (breakingTicks + 1) - return if (couldReBreak == RebreakManager.RebreakPotential.Instant) - breakConfig.swapMode.isEnabled() - else 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, @@ -144,7 +123,11 @@ data class BreakInfo( return if (progress > 0.0f) (progress * 10.0f).toInt().coerceAtMost(9) else -1 } - fun getBreakThreshold() = type.getBreakThreshold(breakConfig) + fun getBreakThreshold() = + when (type) { + Primary -> breakConfig.breakThreshold + else -> 1.0f + } fun startBreakPacket(world: ClientWorld, interaction: ClientPlayerInteractionManager) = breakPacket(Action.START_DESTROY_BLOCK, world, interaction) 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 8765cf276..4be4c5f2d 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -64,6 +64,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.breaking.SwapInfo.Companion.getSwapInfo import com.lambda.interaction.request.interacting.InteractionManager import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotating.RotationRequest @@ -137,15 +138,15 @@ object BreakManager : RequestHandler( private var rotationRequest: RotationRequest? = null private val rotated get() = rotationRequest?.done != false - var swappedThisTick = false - var heldTicks = 0 - var swappedStack: ItemStack = ItemStack.EMPTY + var currentStack: ItemStack = ItemStack.EMPTY set(value) { if (value != field) heldTicks = 0 swappedThisTick = true field = value } + var heldTicks = 0 + var swappedThisTick = false private var breakCooldown = 0 var breaksThisTick = 0 private var maxBreaksThisTick = 0 @@ -172,7 +173,7 @@ object BreakManager : RequestHandler( listen(priority = Int.MIN_VALUE) { if (!swappedThisTick) { - swappedStack = player.mainHandStack + currentStack = player.mainHandStack } swappedThisTick = false heldTicks++ @@ -416,12 +417,12 @@ object BreakManager : RequestHandler( infos.forEach { it.updatePreProcessing(player, world) } infos.firstOrNull()?.let { info -> - infos.lastOrNull { it.shouldSwap && it.shouldProgress }?.let { last -> - val minSwapTicks = max(info.minSwapTicks, last.minSwapTicks) + infos.lastOrNull { it.swapInfo.swap && it.shouldProgress }?.let { last -> + val minSwapTicks = max(info.swapInfo.minKeepTicks, last.swapInfo.minKeepTicks) if (!info.context.requestSwap(info.request, minSwapTicks)) return false if (minSwapTicks > 0) - swappedStack = info.swapStack + currentStack = info.swapStack } } } @@ -598,31 +599,8 @@ object BreakManager : RequestHandler( updatedPreProcessingThisTick = true swapStack = player.inventory.getStack(context.hotbarIndex) - couldReBreak = RebreakManager.couldRebreak(this, player, world) - shouldSwap = shouldSwap(player, world) - - val cachedState = context.cachedState - - val breakTicks = (breakingTicks + 1 - breakConfig.fudgeFactor).coerceAtLeast(1) - val breakAmount = cachedState.calcBreakDelta( - player, - world, - context.blockPos, - breakConfig, - swapStack - ) * breakTicks - val breakAmountNoEfficiency = cachedState.calcBreakDelta( - player, - world, - context.blockPos, - breakConfig, - swapStack, - ignoreEfficiency = true - ) * breakTicks - - minSwapTicks = if ((breakAmount >= getBreakThreshold() || couldReBreak == RebreakManager.RebreakPotential.Instant) && - (breakAmountNoEfficiency < getBreakThreshold() || type == Secondary)) 1 - else 0 + rebreakPotential = RebreakManager.getRebreakPotential(this, player, world) + swapInfo = getSwapInfo(this, player, world) } /** @@ -732,8 +710,6 @@ object BreakManager : RequestHandler( config ) * (info.breakingTicks - config.fudgeFactor) - val overBreakThreshold = progress >= info.getBreakThreshold() - if (config.sounds) { if (info.soundsCooldown % 4.0f == 0.0f) { val blockSoundGroup = blockState.soundGroup @@ -760,7 +736,7 @@ object BreakManager : RequestHandler( } val swing = config.swing - if (overBreakThreshold && heldTicks + 1 >= info.breakConfig.fudgeFactor) { + if (progress >= info.getBreakThreshold() && info.swapInfo.canCompleteBreak) { if (info.type == Primary) { onBlockBreak(info) info.stopBreakPacket(world, interaction) @@ -786,7 +762,7 @@ object BreakManager : RequestHandler( private fun SafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context - if (info.couldReBreak.isPossible()) { + if (info.rebreakPotential.isPossible()) { when (val rebreakResult = RebreakManager.handleUpdate(info.context, info.request)) { is RebreakResult.StillBreaking -> { primaryBreak = rebreakResult.breakInfo.apply { @@ -827,14 +803,13 @@ object BreakManager : RequestHandler( lastPosStarted = ctx.blockPos val blockState = blockState(ctx.blockPos) - val notEmpty = blockState.isNotEmpty - if (notEmpty && info.breakingTicks == 0) { + if (info.breakingTicks == 0) { blockState.onBlockBreakStart(world, ctx.blockPos, player) } - val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig) - info.vanillaInstantBreakable = breakDelta >= 1 - if (notEmpty && breakDelta >= info.getBreakThreshold() && heldTicks + 1 >= info.breakConfig.fudgeFactor) { + val progress = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig) + info.vanillaInstantBreakable = progress >= 1 && info.swapInfo.canCompleteBreak + if (progress >= info.getBreakThreshold() && info.swapInfo.canCompleteBreak) { onBlockBreak(info) if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay } else { @@ -854,7 +829,7 @@ object BreakManager : RequestHandler( info.startBreakPacket(world, interaction) - if (info.type == Secondary || (!info.vanillaInstantBreakable && breakDelta >= info.breakConfig.breakThreshold)) { + if (info.type == Secondary || (!info.vanillaInstantBreakable && progress >= info.breakConfig.breakThreshold)) { info.stopBreakPacket(world, interaction) } 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 368214676..9f3ab7220 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt @@ -64,7 +64,7 @@ object RebreakManager { rebreak = null } - fun couldRebreak(info: BreakInfo, player: ClientPlayerEntity, world: BlockView) = + fun getRebreakPotential(info: BreakInfo, player: ClientPlayerEntity, world: BlockView) = rebreak?.let { reBreak -> val stack = if (info.breakConfig.swapMode.isEnabled()) info.swapStack @@ -109,6 +109,6 @@ object RebreakManager { PartialProgress, None; - fun isPossible() = this == Instant || this == PartialProgress + fun isPossible() = this != None } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt new file mode 100644 index 000000000..5c28b23d0 --- /dev/null +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -0,0 +1,90 @@ +/* + * 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.request.breaking.BreakInfo.BreakType.Primary +import com.lambda.interaction.request.breaking.BreakManager.currentStack +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.item.ItemStack +import net.minecraft.world.BlockView + +data class SwapInfo( + val breakConfig: BreakConfig = TaskFlowModule.build.breaking, + val swap: Boolean = false, + val minKeepTicks: Int = 0, +) { + val canCompleteBreak + get() = BreakManager.heldTicks >= breakConfig.serverSwapTicks + + companion object { + val EMPTY = SwapInfo() + + fun getSwapInfo( + info: BreakInfo, + player: ClientPlayerEntity, + world: BlockView + ): SwapInfo = with(info) { + val breakDelta = context.cachedState + .calcItemBlockBreakingDelta(player, world, context.blockPos, swapStack) + val breakDeltaNoEfficiency = context.cachedState + .calcItemBlockBreakingDelta(player, world, context.blockPos, swapStack, ignoreEfficiency = true) + val breakTicks = (if (rebreakPotential.isPossible()) RebreakManager.rebreak?.breakingTicks + ?: throw IllegalStateException("Rebreak was null when rebreak was considered possible") + else breakingTicks).let { + // Plus one as this is calculated before this ticks progress is calculated and the breakingTicks are incremented + (it + 1) - breakConfig.fudgeFactor + } + val threshold = getBreakThreshold() + + val minKeepTicks = run { + val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks) + val withinPrimarySwapRange = swapTickProgress >= threshold + if (type == Primary) { + val withoutEfficiency = breakDeltaNoEfficiency * breakTicks >= threshold + if (withinPrimarySwapRange && !withoutEfficiency && swapStack.heldTicks < breakConfig.serverSwapTicks) 1 + else 0 + } else { + val withinSecondarySwapRange = withinPrimarySwapRange || + (breakDelta * breakTicks >= threshold && breakDelta * (breakTicks - 1) < threshold) + if (withinSecondarySwapRange) 1 else 0 + } + } + + val swapAtEnd = breakDelta * breakTicks >= threshold || minKeepTicks > 0 + + val swap = if (rebreakPotential == RebreakManager.RebreakPotential.Instant) + breakConfig.swapMode.isEnabled() + else when (breakConfig.swapMode) { + BreakConfig.SwapMode.None -> false + BreakConfig.SwapMode.Start -> !breaking + BreakConfig.SwapMode.End -> swapAtEnd + BreakConfig.SwapMode.StartAndEnd -> !breaking || swapAtEnd + BreakConfig.SwapMode.Constant -> true + } + + return SwapInfo(breakConfig, swap, minKeepTicks) + } + + private val ItemStack.heldTicks + get() = if (currentStack == this) + BreakManager.heldTicks + else 0 + } +} \ No newline at end of file From a60fe87eb89959db9b30e868814681a95b0e225c Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 18 Aug 2025 04:42:30 +0100 Subject: [PATCH 2/8] min server swap ticks to 2 for secondary breaks allowing for faster primary breaks as to not drag them down --- .../interaction/request/breaking/SwapInfo.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt index 5c28b23d0..0b4f44e1f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -26,15 +26,18 @@ import net.minecraft.item.ItemStack import net.minecraft.world.BlockView data class SwapInfo( + val type: BreakInfo.BreakType, val breakConfig: BreakConfig = TaskFlowModule.build.breaking, val swap: Boolean = false, val minKeepTicks: Int = 0, ) { val canCompleteBreak - get() = BreakManager.heldTicks >= breakConfig.serverSwapTicks + get() = BreakManager.heldTicks >= if (type == Primary) + breakConfig.serverSwapTicks + else breakConfig.serverSwapTicks.coerceAtLeast(2) companion object { - val EMPTY = SwapInfo() + val EMPTY = SwapInfo(Primary) fun getSwapInfo( info: BreakInfo, @@ -46,7 +49,7 @@ data class SwapInfo( val breakDeltaNoEfficiency = context.cachedState .calcItemBlockBreakingDelta(player, world, context.blockPos, swapStack, ignoreEfficiency = true) val breakTicks = (if (rebreakPotential.isPossible()) RebreakManager.rebreak?.breakingTicks - ?: throw IllegalStateException("Rebreak was null when rebreak was considered possible") + ?: throw IllegalStateException("Rebreak BreakInfo was null when rebreak was considered possible") else breakingTicks).let { // Plus one as this is calculated before this ticks progress is calculated and the breakingTicks are incremented (it + 1) - breakConfig.fudgeFactor @@ -54,14 +57,14 @@ data class SwapInfo( val threshold = getBreakThreshold() val minKeepTicks = run { - val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks) - val withinPrimarySwapRange = swapTickProgress >= threshold if (type == Primary) { + val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks) val withoutEfficiency = breakDeltaNoEfficiency * breakTicks >= threshold - if (withinPrimarySwapRange && !withoutEfficiency && swapStack.heldTicks < breakConfig.serverSwapTicks) 1 + if (swapTickProgress >= threshold && !withoutEfficiency && swapStack.heldTicks < breakConfig.serverSwapTicks) 1 else 0 } else { - val withinSecondarySwapRange = withinPrimarySwapRange || + val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks.coerceAtLeast(2) - 1) + val withinSecondarySwapRange = swapTickProgress >= threshold || (breakDelta * breakTicks >= threshold && breakDelta * (breakTicks - 1) < threshold) if (withinSecondarySwapRange) 1 else 0 } @@ -79,7 +82,7 @@ data class SwapInfo( BreakConfig.SwapMode.Constant -> true } - return SwapInfo(breakConfig, swap, minKeepTicks) + return SwapInfo(info.type, breakConfig, swap, minKeepTicks) } private val ItemStack.heldTicks From a3a8bd817301d905f8199e6c17f0e3efb4060011 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 18 Aug 2025 15:36:07 +0100 Subject: [PATCH 3/8] idk what i was thinking at 5 am --- .../com/lambda/config/groups/BreakSettings.kt | 2 +- .../interaction/request/breaking/SwapInfo.kt | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index d90b8ad4a..0b82986e4 100644 --- a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -51,7 +51,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", visibility = vis).group(groupPath, Group.General) override val fudgeFactor by c.setting("Fudge Factor", 1, 0..5, 1, "The number of ticks to add to the break time, usually to account for server lag", visibility = vis).group(groupPath, Group.General) - override val serverSwapTicks by c.setting("Server Swap", 1, 0..5, 1, "The number of ticks to give the server time to recognize the player attributes on the swapped item", " tick(s)", visibility = vis).group(groupPath, Group.General) + override val serverSwapTicks by c.setting("Server Swap", 2, 0..5, 1, "The number of ticks to give the server time to recognize the player attributes on the swapped item", " tick(s)", 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", " tick(s)", visibility = vis).group(groupPath, Group.General) diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt index 0b4f44e1f..e02fb6e93 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -32,8 +32,7 @@ data class SwapInfo( val minKeepTicks: Int = 0, ) { val canCompleteBreak - get() = BreakManager.heldTicks >= if (type == Primary) - breakConfig.serverSwapTicks + get() = (BreakManager.heldTicks + 1) >= if (type == Primary) breakConfig.serverSwapTicks else breakConfig.serverSwapTicks.coerceAtLeast(2) companion object { @@ -58,15 +57,16 @@ data class SwapInfo( val minKeepTicks = run { if (type == Primary) { - val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks) + val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks - 1) val withoutEfficiency = breakDeltaNoEfficiency * breakTicks >= threshold - if (swapTickProgress >= threshold && !withoutEfficiency && swapStack.heldTicks < breakConfig.serverSwapTicks) 1 + if (swapTickProgress >= threshold && + !withoutEfficiency) 1 else 0 } else { - val swapTickProgress = breakDelta * (breakTicks + breakConfig.serverSwapTicks.coerceAtLeast(2) - 1) - val withinSecondarySwapRange = swapTickProgress >= threshold || - (breakDelta * breakTicks >= threshold && breakDelta * (breakTicks - 1) < threshold) - if (withinSecondarySwapRange) 1 else 0 + val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(2) + val swapTickProgress = breakDelta * (breakTicks + serverSwapTicks - 1) + if (swapTickProgress >= threshold && swapStack.heldTicks < serverSwapTicks) 1 + else 0 } } From a979cb2d985ba3fdca745f2a09960371f1eccf21 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 18 Aug 2025 21:43:28 +0100 Subject: [PATCH 4/8] catch rebreak logic up to the rest and raise minimum secondary swap ticks to 3 --- .../com/lambda/interaction/request/breaking/BreakInfo.kt | 4 +++- .../lambda/interaction/request/breaking/BreakManager.kt | 1 + .../lambda/interaction/request/breaking/RebreakManager.kt | 5 +++-- .../com/lambda/interaction/request/breaking/SwapInfo.kt | 7 ++++--- 4 files changed, 11 insertions(+), 6 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 fe3ae90ec..c76542d78 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.request.breaking import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.ActionInfo import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary +import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta import com.lambda.util.Describable import com.lambda.util.NamedEnum @@ -125,7 +126,8 @@ data class BreakInfo( fun getBreakThreshold() = when (type) { - Primary -> breakConfig.breakThreshold + Primary, + Rebreak-> breakConfig.breakThreshold else -> 1.0f } 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 4be4c5f2d..95da9d4b7 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -772,6 +772,7 @@ object BreakManager : RequestHandler( } primaryBreak?.let { primary -> + if (!handlePreProcessing()) return false updateBreakProgress(primary) } return true 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 9f3ab7220..be5edcb56 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt @@ -35,7 +35,7 @@ object RebreakManager { var rebreak: BreakInfo? = null init { - listen(priority = Int.MIN_VALUE) { + listen(priority = Int.MIN_VALUE + 1) { rebreak?.run { if (!progressedThisTick) { breakingTicks++ @@ -89,7 +89,8 @@ object RebreakManager { val context = reBreak.context val breakDelta = context.cachedState.calcBreakDelta(player, world, context.blockPos, reBreak.breakConfig) - return@runSafe if ((reBreak.breakingTicks - reBreak.breakConfig.fudgeFactor) * breakDelta >= reBreak.breakConfig.breakThreshold) { + val breakTicks = reBreak.breakingTicks - reBreak.breakConfig.fudgeFactor + return@runSafe if (breakTicks * breakDelta >= reBreak.getBreakThreshold() && reBreak.swapInfo.canCompleteBreak) { if (reBreak.breakConfig.breakConfirmation != BreakConfig.BreakConfirmationMode.AwaitThenBreak) { destroyBlock(reBreak) } diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt index e02fb6e93..53e2770b4 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request.breaking import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary +import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak import com.lambda.interaction.request.breaking.BreakManager.currentStack import com.lambda.module.modules.client.TaskFlowModule import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta @@ -32,8 +33,8 @@ data class SwapInfo( val minKeepTicks: Int = 0, ) { val canCompleteBreak - get() = (BreakManager.heldTicks + 1) >= if (type == Primary) breakConfig.serverSwapTicks - else breakConfig.serverSwapTicks.coerceAtLeast(2) + get() = (BreakManager.heldTicks + 1) >= if (type == Primary || type == Rebreak) breakConfig.serverSwapTicks + else breakConfig.serverSwapTicks.coerceAtLeast(3) companion object { val EMPTY = SwapInfo(Primary) @@ -63,7 +64,7 @@ data class SwapInfo( !withoutEfficiency) 1 else 0 } else { - val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(2) + val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(3) val swapTickProgress = breakDelta * (breakTicks + serverSwapTicks - 1) if (swapTickProgress >= threshold && swapStack.heldTicks < serverSwapTicks) 1 else 0 From ca55381639bcc931b9ef29fd97676ec2893d0f8f Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 19 Aug 2025 17:27:03 +0100 Subject: [PATCH 5/8] omit population from the repeat loop --- .../com/lambda/interaction/request/breaking/BreakManager.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 1a7170472..917b5342c 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -315,9 +315,12 @@ object BreakManager : RequestHandler( * @see updateBreakProgress */ private fun SafeContext.processRequest(breakRequest: BreakRequest?) { + breakRequest?.let { request -> + if (request.fresh) populateFrom(request) + } + repeat(2) { breakRequest?.let { request -> - if (request.fresh) populateFrom(request) if (performInstantBreaks(request)) { processNewBreaks(request) } From 18ed215ca35f11d0b1515252b4c4dbac147a9630 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 19 Aug 2025 17:30:03 +0100 Subject: [PATCH 6/8] pass all contexts over to the break manager for it to decide which it can take for optimal efficiency and leave stack selection check to the break manager too --- .../construction/simulation/BuildSimulator.kt | 2 -- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 26 ++++++------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 921a0b4cc..ea05b6026 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -38,7 +38,6 @@ 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 @@ -850,7 +849,6 @@ object BuildSimulator { val swapStack = swapCandidates.map { it.matchingStacks(stackSelection) } .asSequence() .flatten() - .filter { BreakManager.currentStackSelection.filterStack(it) } .let { containerStacks -> var bestStack = ItemStack.EMPTY var bestBreakDelta = -1f diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index ccb117983..5b71300df 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -29,7 +29,6 @@ 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 @@ -140,24 +139,14 @@ class BuildTask @Ta5kBuilder constructor( if (atMaxPendingInteractions) return@listen when (bestResult) { is BreakResult.Break -> { - val breakResults = resultsNotBlocked.filterIsInstance() - val requestContexts = arrayListOf() - - if (build.breaking.breaksPerTick > 1) { - breakResults - .filter { it.context.instantBreak } - .take(emptyPendingInteractionSlots) - .let { instantBreakResults -> - requestContexts.addAll(instantBreakResults.map { it.context }) - } - } - - if (requestContexts.isEmpty()) { - requestContexts.addAll(breakResults.map { it.context }) - } + val breakResults = resultsNotBlocked + .filterIsInstance() + .distinctBy { it.blockPos } + .take(emptyPendingInteractionSlots) + .map { it.context } breakRequest( - requestContexts, pendingInteractions, rotation, hotbar, interactionConfig, inventory, build, + breakResults, pendingInteractions, rotation, hotbar, interactionConfig, inventory, build, ) { onStop { breaks++ } onItemDrop?.let { onItemDrop -> @@ -171,8 +160,9 @@ class BuildTask @Ta5kBuilder constructor( .filterIsInstance() .distinctBy { it.blockPos } .take(emptyPendingInteractionSlots) + .map { it.context } - PlaceRequest(placeResults.map { it.context }, build, rotation, hotbar, pendingInteractions) { placements++ }.submit() + PlaceRequest(placeResults, build, rotation, hotbar, pendingInteractions) { placements++ }.submit() } is InteractResult.Interact -> { val interactResults = resultsNotBlocked From 77abd7925c4bad9aec9061df10e484332802fc95 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 19 Aug 2025 19:11:24 +0100 Subject: [PATCH 7/8] min secondary swap ticks to 2 --- .../com/lambda/interaction/request/breaking/SwapInfo.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt index 53e2770b4..c7c132090 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -34,7 +34,7 @@ data class SwapInfo( ) { val canCompleteBreak get() = (BreakManager.heldTicks + 1) >= if (type == Primary || type == Rebreak) breakConfig.serverSwapTicks - else breakConfig.serverSwapTicks.coerceAtLeast(3) + else breakConfig.serverSwapTicks.coerceAtLeast(2) companion object { val EMPTY = SwapInfo(Primary) @@ -64,7 +64,7 @@ data class SwapInfo( !withoutEfficiency) 1 else 0 } else { - val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(3) + val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(2) val swapTickProgress = breakDelta * (breakTicks + serverSwapTicks - 1) if (swapTickProgress >= threshold && swapStack.heldTicks < serverSwapTicks) 1 else 0 From 41e2edae19c29b01769e836feaf03d0781edbbb9 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 19 Aug 2025 19:16:59 +0100 Subject: [PATCH 8/8] back to two... --- .../com/lambda/interaction/request/breaking/SwapInfo.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt index c7c132090..53e2770b4 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -34,7 +34,7 @@ data class SwapInfo( ) { val canCompleteBreak get() = (BreakManager.heldTicks + 1) >= if (type == Primary || type == Rebreak) breakConfig.serverSwapTicks - else breakConfig.serverSwapTicks.coerceAtLeast(2) + else breakConfig.serverSwapTicks.coerceAtLeast(3) companion object { val EMPTY = SwapInfo(Primary) @@ -64,7 +64,7 @@ data class SwapInfo( !withoutEfficiency) 1 else 0 } else { - val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(2) + val serverSwapTicks = breakConfig.serverSwapTicks.coerceAtLeast(3) val swapTickProgress = breakDelta * (breakTicks + serverSwapTicks - 1) if (swapTickProgress >= threshold && swapStack.heldTicks < serverSwapTicks) 1 else 0