Skip to content

Commit 661ab1a

Browse files
authored
Fix: Fixes the main compatibility issue in the break manager after the 1.21.5 update (#135)
Properly propagates and tracks values to make sure blocks are broken within the correct time frame, accounting for the server players attribute update delays
1 parent a7383ab commit 661ab1a

File tree

3 files changed

+86
-68
lines changed

3 files changed

+86
-68
lines changed

src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ data class BreakInfo(
4444

4545
// Pre Processing
4646
var shouldProgress = false
47-
var couldReBreak by OneSetPerTick(value = false, throwOnLimitBreach = true)
47+
var couldReBreak by OneSetPerTick(value = RebreakManager.RebreakPotential.None, throwOnLimitBreach = true)
4848
var shouldSwap by OneSetPerTick(value = false, throwOnLimitBreach = true)
4949
var swapStack: ItemStack by OneSetPerTick(ItemStack.EMPTY, true)
5050
var minSwapTicks by OneSetPerTick(0, true)
@@ -118,7 +118,7 @@ data class BreakInfo(
118118
val item = player.inventory.getStack(context.hotbarIndex)
119119
val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item)
120120
val breakProgress = breakDelta * (breakingTicks + 1)
121-
return if (couldReBreak)
121+
return if (couldReBreak == RebreakManager.RebreakPotential.Instant)
122122
breakConfig.swapMode.isEnabled()
123123
else when (breakConfig.swapMode) {
124124
BreakConfig.SwapMode.None -> false

src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt

Lines changed: 65 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -101,39 +101,49 @@ object BreakManager : RequestHandler<BreakRequest>(
101101
onOpen = { processRequest(activeRequest); simulateAbandoned() },
102102
onClose = { checkForCancels() }
103103
), PositionBlocking {
104+
private val breakInfos = arrayOfNulls<BreakInfo>(2)
105+
106+
private val activeInfos
107+
get() = breakInfos
108+
.filterNotNull()
109+
.filter { it.type != RedundantSecondary }
110+
104111
private var primaryBreak: BreakInfo?
105112
get() = breakInfos[0]
106113
set(value) { breakInfos[0] = value }
114+
107115
private var secondaryBreak: BreakInfo?
108116
get() = breakInfos[1]
109117
set(value) { breakInfos[1] = value }
110-
private val breakInfos = arrayOfNulls<BreakInfo>(2)
118+
119+
private val abandonedBreak
120+
get() = breakInfos[1].let { secondary ->
121+
if (secondary?.abandoned == true && secondary.type != RedundantSecondary) secondary
122+
else null
123+
}
124+
111125
val currentStackSelection
112-
get() = breakInfos
126+
get() = activeInfos
113127
.lastOrNull {
114-
it != null && it.type != RedundantSecondary && (it.breakConfig.doubleBreak || it.type == Secondary)
128+
it.breakConfig.doubleBreak || it.type == Secondary
115129
}?.context?.itemSelection
116130
?: StackSelection.EVERYTHING.select()
117131

118-
private val pendingBreakCount get() = breakInfos.count { it != null } + pendingActions.size
132+
private val pendingBreakCount get() = activeInfos.count() + pendingActions.size
119133
override val blockedPositions
120-
get() = breakInfos.mapNotNull { it?.context?.blockPos } + pendingActions.map { it.context.blockPos }
134+
get() = activeInfos.map { it.context.blockPos } + pendingActions.map { it.context.blockPos }
121135

122136
private var activeRequest: BreakRequest? = null
123137

124138
private var rotationRequest: RotationRequest? = null
125139
private val rotated get() = rotationRequest?.done != false
126140

127-
private var swapped = false
128-
set(value) {
129-
field = value
130-
if (!value)
131-
breakInfos.forEach { it?.serverBreakTicks = 0 }
132-
}
141+
var swappedThisTick = false
133142
var swappedStack: ItemStack = ItemStack.EMPTY
134143
set(value) {
135144
if (value != field)
136145
breakInfos.forEach { it?.serverBreakTicks = 0 }
146+
swappedThisTick = true
137147
field = value
138148
}
139149
private var breakCooldown = 0
@@ -161,6 +171,9 @@ object BreakManager : RequestHandler<BreakRequest>(
161171
super.load()
162172

163173
listen<TickEvent.Post>(priority = Int.MIN_VALUE) {
174+
if (!swappedThisTick) {
175+
swappedStack = player.mainHandStack
176+
} else swappedThisTick = false
164177
if (breakCooldown > 0) {
165178
breakCooldown--
166179
}
@@ -257,7 +270,7 @@ object BreakManager : RequestHandler<BreakRequest>(
257270

258271
info.context.cachedState.getOutlineShape(world, info.context.blockPos).boundingBoxes.map {
259272
it.offset(info.context.blockPos)
260-
}.forEach boxes@{ box ->
273+
}.forEach boxes@ { box ->
261274
val interpolated = interpolateBox(box, interpolatedProgress, info.breakConfig)
262275
if (config.fill) event.renderer.buildFilled(interpolated, fillColor)
263276
if (config.outline) event.renderer.buildOutline(interpolated, outlineColor)
@@ -266,7 +279,8 @@ object BreakManager : RequestHandler<BreakRequest>(
266279
}
267280

268281
listenUnsafe<ConnectionEvent.Connect.Pre>(priority = Int.MIN_VALUE) {
269-
breakInfos.forEach { it?.nullify() }
282+
primaryBreak = null
283+
secondaryBreak = null
270284
breakCooldown = 0
271285
}
272286

@@ -309,9 +323,8 @@ object BreakManager : RequestHandler<BreakRequest>(
309323
// last break to be started
310324
run {
311325
if (!handlePreProcessing()) return@run
312-
breakInfos
313-
.filterNotNull()
314-
.filter { it.type != RedundantSecondary && it.updatedThisTick }
326+
activeInfos
327+
.filter { it.updatedThisTick }
315328
.asReversed()
316329
.forEach { info ->
317330
if (info.shouldProgress)
@@ -323,7 +336,7 @@ object BreakManager : RequestHandler<BreakRequest>(
323336
if (instantBreaks.isEmpty() && breaks.isEmpty()) {
324337
activeRequest = null
325338
}
326-
if (breaksThisTick > 0 || breakInfos.any { it != null && it.type != RedundantSecondary }) {
339+
if (breaksThisTick > 0 || activeInfos.isNotEmpty()) {
327340
activeThisTick = true
328341
}
329342
}
@@ -379,7 +392,7 @@ object BreakManager : RequestHandler<BreakRequest>(
379392
* @return if the break context can be accepted.
380393
*/
381394
private fun SafeContext.canAccept(newCtx: BreakContext): Boolean {
382-
if (breakInfos.none { it?.context?.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false
395+
if (activeInfos.none { it.context.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false
383396

384397
if (!currentStackSelection.filterStack(player.inventory.getStack(newCtx.hotbarIndex)))
385398
return false
@@ -391,18 +404,16 @@ object BreakManager : RequestHandler<BreakRequest>(
391404
}
392405

393406
private fun SafeContext.handlePreProcessing(): Boolean {
394-
breakInfos
395-
.filterNotNull()
396-
.filter { it.type != RedundantSecondary && it.updatedThisTick }
407+
activeInfos
408+
.filter { it.updatedThisTick }
397409
.let { infos ->
398410
rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak }
399411
?.let { info ->
400412
val rotation = info.context.rotation
401413
rotation.submit(false)
402414
}
403415

404-
if (breakInfos.none { it != null && it.type != RedundantSecondary }) {
405-
swapped = false
416+
if (activeInfos.isEmpty()) {
406417
swappedStack = player.mainHandStack
407418
return true
408419
}
@@ -412,14 +423,10 @@ object BreakManager : RequestHandler<BreakRequest>(
412423
}
413424
infos.firstOrNull()?.let { info ->
414425
infos.firstOrNull { it.shouldSwap && it.shouldProgress }?.let { last ->
415-
if (!info.context.requestSwap(info.request, max(info.minSwapTicks, last.minSwapTicks))) {
416-
swapped = false
426+
if (!info.context.requestSwap(info.request, max(info.minSwapTicks, last.minSwapTicks)))
417427
return false
418-
}
419428
swappedStack = info.swapStack
420-
swapped = true
421-
info.serverBreakTicks++
422-
return true
429+
if (info.minSwapTicks > 0) info.serverBreakTicks++
423430
}
424431
}
425432
}
@@ -510,25 +517,21 @@ object BreakManager : RequestHandler<BreakRequest>(
510517

511518
private fun SafeContext.simulateAbandoned() {
512519
// Cancelled but double breaking so requires break manager to continue the simulation
513-
breakInfos
514-
.asSequence()
515-
.filterNotNull()
516-
.filter { it.abandoned && it.type != RedundantSecondary }
517-
.forEach { info ->
518-
with(info.request) {
519-
info.context.blockPos
520-
.toStructure(TargetState.Empty)
521-
.toBlueprint()
522-
.simulate(player.eyePos, interact, rotation, inventory, build)
523-
.asSequence()
524-
.filterIsInstance<BreakResult.Break>()
525-
.filter { canAccept(it.context) }
526-
.sorted()
527-
.let { sim ->
528-
info.updateInfo(sim.firstOrNull()?.context ?: return@forEach)
529-
}
530-
}
520+
abandonedBreak?.let { abandonedInfo ->
521+
with (abandonedInfo.request) {
522+
abandonedInfo.context.blockPos
523+
.toStructure(TargetState.Empty)
524+
.toBlueprint()
525+
.simulate(player.eyePos, interact, rotation, inventory, build)
526+
.asSequence()
527+
.filterIsInstance<BreakResult.Break>()
528+
.filter { canAccept(it.context) }
529+
.sorted()
530+
.let { sim ->
531+
abandonedInfo.updateInfo(sim.firstOrNull()?.context ?: return)
532+
}
531533
}
534+
}
532535
}
533536

534537
private fun checkForCancels() {
@@ -605,22 +608,26 @@ object BreakManager : RequestHandler<BreakRequest>(
605608
val cachedState = context.cachedState
606609
swapStack = player.inventory.getStack(context.hotbarIndex)
607610

608-
val breakAmount =
609-
cachedState.calcBreakDelta(player, world, context.blockPos, breakConfig, swapStack) * (breakingTicks + 1)
611+
val breakTicks = (breakingTicks + 1 - breakConfig.fudgeFactor).coerceAtLeast(1)
612+
val breakAmount = cachedState.calcBreakDelta(
613+
player,
614+
world,
615+
context.blockPos,
616+
breakConfig,
617+
swapStack
618+
) * breakTicks
610619
val breakAmountNoEfficiency = cachedState.calcBreakDelta(
611620
player,
612621
world,
613622
context.blockPos,
614623
breakConfig,
615624
swapStack,
616625
ignoreEfficiency = true
617-
) * (breakingTicks + 1)
626+
) * breakTicks
618627

619-
minSwapTicks = if (breakAmount >= getBreakThreshold() || couldReBreak) {
620-
val min = if (breakAmountNoEfficiency >= getBreakThreshold()) 0 else 1
621-
serverBreakTicks++
622-
min
623-
} else 0
628+
minSwapTicks = if ((breakAmount >= getBreakThreshold() || couldReBreak == RebreakManager.RebreakPotential.Instant) &&
629+
(breakAmountNoEfficiency < getBreakThreshold() || type == Secondary)) 1
630+
else 0
624631
}
625632

626633
/**
@@ -763,7 +770,7 @@ object BreakManager : RequestHandler<BreakRequest>(
763770
}
764771

765772
val swing = config.swing
766-
if (overBreakThreshold && (!swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor)) {
773+
if (overBreakThreshold && (info.serverBreakTicks >= info.breakConfig.fudgeFactor || info.minSwapTicks < 1)) {
767774
if (info.type == Primary) {
768775
onBlockBreak(info)
769776
info.stopBreakPacket(world, interaction)
@@ -789,7 +796,7 @@ object BreakManager : RequestHandler<BreakRequest>(
789796
private fun SafeContext.startBreaking(info: BreakInfo): Boolean {
790797
val ctx = info.context
791798

792-
if (info.couldReBreak) {
799+
if (info.couldReBreak.isPossible()) {
793800
when (val rebreakResult = RebreakManager.handleUpdate(info.context, info.request)) {
794801
is RebreakResult.StillBreaking -> {
795802
primaryBreak = rebreakResult.breakInfo.apply {
@@ -837,8 +844,7 @@ object BreakManager : RequestHandler<BreakRequest>(
837844

838845
val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig)
839846
info.vanillaInstantBreakable = breakDelta >= 1
840-
val serverSwapped = !swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor
841-
if (notEmpty && (breakDelta >= info.getBreakThreshold() && serverSwapped)) {
847+
if (notEmpty && (breakDelta >= info.getBreakThreshold() && (info.serverBreakTicks >= info.breakConfig.fudgeFactor || info.minSwapTicks < 1))) {
842848
onBlockBreak(info)
843849
if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay
844850
} else {

src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ object RebreakManager {
3535
var rebreak: BreakInfo? = null
3636

3737
init {
38-
listen<TickEvent.Pre>(priority = Int.MIN_VALUE) {
38+
listen<TickEvent.Post>(priority = Int.MIN_VALUE) {
3939
rebreak?.run {
4040
if (!progressedThisTick) {
4141
breakingTicks++
@@ -69,13 +69,17 @@ object RebreakManager {
6969
val stack = if (info.breakConfig.swapMode.isEnabled())
7070
player.inventory.getStack(info.context.hotbarIndex)
7171
else player.mainHandStack
72-
val breakDelta =
73-
info.context.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, stack)
74-
reBreak.breakConfig.rebreak &&
72+
val breakDelta = info.context.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, stack)
73+
val possible = reBreak.breakConfig.rebreak &&
7574
info.context.blockPos == reBreak.context.blockPos &&
76-
!reBreak.updatedThisTick &&
77-
((reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold)
78-
} == true
75+
!reBreak.updatedThisTick
76+
val instant = (reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold
77+
when {
78+
possible && instant -> RebreakPotential.Instant
79+
possible -> RebreakPotential.PartialProgress
80+
else -> RebreakPotential.None
81+
}
82+
} ?: RebreakPotential.None
7983

8084
fun handleUpdate(ctx: BreakContext, breakRequest: BreakRequest) =
8185
runSafe {
@@ -99,4 +103,12 @@ object RebreakManager {
99103
RebreakResult.StillBreaking(reBreak)
100104
}
101105
}
106+
107+
enum class RebreakPotential {
108+
Instant,
109+
PartialProgress,
110+
None;
111+
112+
fun isPossible() = this == Instant || this == PartialProgress
113+
}
102114
}

0 commit comments

Comments
 (0)