From ebdab3f34587db6d86a161d5541bc29581d16704 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Thu, 14 Aug 2025 03:42:59 +0100 Subject: [PATCH] fixes the main inconsistency within the break manager on 1.21.5 --- .../interaction/request/breaking/BreakInfo.kt | 4 +- .../request/breaking/BreakManager.kt | 130 ++++++++++-------- .../request/breaking/RebreakManager.kt | 23 +++- 3 files changed, 92 insertions(+), 65 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 1fac827c1..6bc1bfea2 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -44,7 +44,7 @@ data class BreakInfo( // Pre Processing var shouldProgress = false - var couldReBreak by OneSetPerTick(value = false, throwOnLimitBreach = true) + var couldReBreak by OneSetPerTick(value = RebreakManager.RebreakPotential.None, throwOnLimitBreach = true) var shouldSwap by OneSetPerTick(value = false, throwOnLimitBreach = true) var swapStack: ItemStack by OneSetPerTick(ItemStack.EMPTY, true) var minSwapTicks by OneSetPerTick(0, true) @@ -118,7 +118,7 @@ data class BreakInfo( 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) + return if (couldReBreak == RebreakManager.RebreakPotential.Instant) 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 6c549c98e..a500ed918 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -101,39 +101,49 @@ object BreakManager : RequestHandler( onOpen = { processRequest(activeRequest); simulateAbandoned() }, onClose = { checkForCancels() } ), PositionBlocking { + private val breakInfos = arrayOfNulls(2) + + private val activeInfos + get() = breakInfos + .filterNotNull() + .filter { it.type != RedundantSecondary } + 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 val abandonedBreak + get() = breakInfos[1].let { secondary -> + if (secondary?.abandoned == true && secondary.type != RedundantSecondary) secondary + else null + } + val currentStackSelection - get() = breakInfos + get() = activeInfos .lastOrNull { - it != null && it.type != RedundantSecondary && (it.breakConfig.doubleBreak || it.type == Secondary) + it.breakConfig.doubleBreak || it.type == Secondary }?.context?.itemSelection ?: StackSelection.EVERYTHING.select() - private val pendingBreakCount get() = breakInfos.count { it != null } + pendingActions.size + private val pendingBreakCount get() = activeInfos.count() + pendingActions.size override val blockedPositions - get() = breakInfos.mapNotNull { it?.context?.blockPos } + pendingActions.map { it.context.blockPos } + get() = activeInfos.map { it.context.blockPos } + pendingActions.map { it.context.blockPos } private var activeRequest: BreakRequest? = null 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 swappedThisTick = false var swappedStack: ItemStack = ItemStack.EMPTY set(value) { if (value != field) breakInfos.forEach { it?.serverBreakTicks = 0 } + swappedThisTick = true field = value } private var breakCooldown = 0 @@ -161,6 +171,9 @@ object BreakManager : RequestHandler( super.load() listen(priority = Int.MIN_VALUE) { + if (!swappedThisTick) { + swappedStack = player.mainHandStack + } else swappedThisTick = false if (breakCooldown > 0) { breakCooldown-- } @@ -258,7 +271,8 @@ object BreakManager : RequestHandler( } listenUnsafe(priority = Int.MIN_VALUE) { - breakInfos.forEach { it?.nullify() } + primaryBreak = null + secondaryBreak = null breakCooldown = 0 } @@ -301,9 +315,8 @@ object BreakManager : RequestHandler( // last break to be started run { if (!handlePreProcessing()) return@run - breakInfos - .filterNotNull() - .filter { it.type != RedundantSecondary && it.updatedThisTick } + activeInfos + .filter { it.updatedThisTick } .asReversed() .forEach { info -> if (info.shouldProgress) @@ -315,7 +328,7 @@ object BreakManager : RequestHandler( if (instantBreaks.isEmpty() && breaks.isEmpty()) { activeRequest = null } - if (breaksThisTick > 0 || breakInfos.any { it != null && it.type != RedundantSecondary }) { + if (breaksThisTick > 0 || activeInfos.isNotEmpty()) { activeThisTick = true } } @@ -371,7 +384,7 @@ object BreakManager : RequestHandler( * @return if the break context can be accepted. */ private fun SafeContext.canAccept(newCtx: BreakContext): Boolean { - if (breakInfos.none { it?.context?.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false + if (activeInfos.none { it.context.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false if (!currentStackSelection.filterStack(player.inventory.getStack(newCtx.hotbarIndex))) return false @@ -383,9 +396,8 @@ object BreakManager : RequestHandler( } private fun SafeContext.handlePreProcessing(): Boolean { - breakInfos - .filterNotNull() - .filter { it.type != RedundantSecondary && it.updatedThisTick } + activeInfos + .filter { it.updatedThisTick } .let { infos -> rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak } ?.let { info -> @@ -393,8 +405,7 @@ object BreakManager : RequestHandler( rotation.submit(false) } - if (breakInfos.none { it != null && it.type != RedundantSecondary }) { - swapped = false + if (activeInfos.isEmpty()) { swappedStack = player.mainHandStack return true } @@ -404,14 +415,10 @@ object BreakManager : RequestHandler( } 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 + if (!info.context.requestSwap(info.request, max(info.minSwapTicks, last.minSwapTicks))) return false - } swappedStack = info.swapStack - swapped = true - info.serverBreakTicks++ - return true + if (info.minSwapTicks > 0) info.serverBreakTicks++ } } } @@ -502,25 +509,21 @@ 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.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) - } - } + abandonedBreak?.let { abandonedInfo -> + with (abandonedInfo.request) { + abandonedInfo.context.blockPos + .toStructure(TargetState.Empty) + .toBlueprint() + .simulate(player.eyePos, interact, rotation, inventory, build) + .asSequence() + .filterIsInstance() + .filter { canAccept(it.context) } + .sorted() + .let { sim -> + abandonedInfo.updateInfo(sim.firstOrNull()?.context ?: return) + } } + } } private fun checkForCancels() { @@ -598,14 +601,26 @@ object BreakManager : RequestHandler( 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 + 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 } /** @@ -748,7 +763,7 @@ object BreakManager : RequestHandler( } val swing = config.swing - if (overBreakThreshold && (!swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor)) { + if (overBreakThreshold && (info.serverBreakTicks >= info.breakConfig.fudgeFactor || info.minSwapTicks < 1)) { if (info.type == Primary) { onBlockBreak(info) info.stopBreakPacket(world, interaction) @@ -774,7 +789,7 @@ object BreakManager : RequestHandler( private fun SafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context - if (info.couldReBreak) { + if (info.couldReBreak.isPossible()) { when (val rebreakResult = RebreakManager.handleUpdate(info.context, info.request)) { is RebreakResult.StillBreaking -> { primaryBreak = rebreakResult.breakInfo.apply { @@ -822,8 +837,7 @@ object BreakManager : RequestHandler( val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig) info.vanillaInstantBreakable = breakDelta >= 1 - val serverSwapped = !swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor - if (notEmpty && (breakDelta >= info.getBreakThreshold() && serverSwapped)) { + if (notEmpty && (breakDelta >= info.getBreakThreshold() && (info.serverBreakTicks >= info.breakConfig.fudgeFactor || info.minSwapTicks < 1))) { onBlockBreak(info) if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay } else { 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 bd32b6104..79be43605 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) { rebreak?.run { if (!progressedThisTick) { breakingTicks++ @@ -70,11 +70,16 @@ object RebreakManager { player.inventory.getStack(info.context.hotbarIndex) else player.mainHandStack val breakDelta = info.context.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, stack) - reBreak.breakConfig.rebreak && + val possible = reBreak.breakConfig.rebreak && info.context.blockPos == reBreak.context.blockPos && - !reBreak.updatedThisTick && - ((reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold) - } == true + !reBreak.updatedThisTick + val instant = (reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold + when { + possible && instant -> RebreakPotential.Instant + possible -> RebreakPotential.PartialProgress + else -> RebreakPotential.None + } + } ?: RebreakPotential.None fun handleUpdate(ctx: BreakContext, breakRequest: BreakRequest) = runSafe { @@ -98,4 +103,12 @@ object RebreakManager { RebreakResult.StillBreaking(reBreak) } } + + enum class RebreakPotential { + Instant, + PartialProgress, + None; + + fun isPossible() = this == Instant || this == PartialProgress + } } \ No newline at end of file