Skip to content

Commit 9315d3f

Browse files
committed
break manager documentation
1 parent 2550fd1 commit 9315d3f

File tree

1 file changed

+109
-78
lines changed

1 file changed

+109
-78
lines changed

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

Lines changed: 109 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,25 @@ import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary
5050
import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak
5151
import com.lambda.interaction.request.breaking.BreakInfo.BreakType.RedundantSecondary
5252
import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Secondary
53+
import com.lambda.interaction.request.breaking.BreakManager.abandonedBreak
5354
import com.lambda.interaction.request.breaking.BreakManager.activeInfos
5455
import com.lambda.interaction.request.breaking.BreakManager.activeRequest
5556
import com.lambda.interaction.request.breaking.BreakManager.breakInfos
5657
import com.lambda.interaction.request.breaking.BreakManager.breaks
5758
import com.lambda.interaction.request.breaking.BreakManager.canAccept
5859
import com.lambda.interaction.request.breaking.BreakManager.checkForCancels
60+
import com.lambda.interaction.request.breaking.BreakManager.handlePreProcessing
61+
import com.lambda.interaction.request.breaking.BreakManager.hotbarRequest
5962
import com.lambda.interaction.request.breaking.BreakManager.initNewBreak
6063
import com.lambda.interaction.request.breaking.BreakManager.maxBreaksThisTick
64+
import com.lambda.interaction.request.breaking.BreakManager.nullify
65+
import com.lambda.interaction.request.breaking.BreakManager.populateFrom
6166
import com.lambda.interaction.request.breaking.BreakManager.processNewBreak
6267
import com.lambda.interaction.request.breaking.BreakManager.processRequest
68+
import com.lambda.interaction.request.breaking.BreakManager.rotationRequest
6369
import com.lambda.interaction.request.breaking.BreakManager.simulateAbandoned
6470
import com.lambda.interaction.request.breaking.BreakManager.updateBreakProgress
71+
import com.lambda.interaction.request.breaking.BreakManager.updatePreProcessing
6572
import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
6673
import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions
6774
import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs
@@ -96,6 +103,14 @@ import net.minecraft.util.math.Box
96103
import kotlin.math.max
97104
import kotlin.math.min
98105

106+
/**
107+
* This manager is responsible for breaking blocks in the most efficient manner possible. It can be accessed
108+
* from anywhere through a [BreakRequest], although it is not designed in the image of thread safety.
109+
*
110+
* If configured with the right options enabled, this manager can break two blocks simultaneously, even if the two breaks come from
111+
* different requests. Each break will be handled using its own config, and just like the other managers, priority is a first-come, first-served
112+
* style system.
113+
*/
99114
object BreakManager : RequestHandler<BreakRequest>(
100115
0,
101116
TickEvent.Pre,
@@ -291,8 +306,8 @@ object BreakManager : RequestHandler<BreakRequest>(
291306
}
292307

293308
/**
294-
* Attempts to accept and process the request, if there is not already an [activeRequest].
295-
* If the request is processed and all breaks completed, the [activeRequest] is cleared.
309+
* Attempts to accept and process the request, if there is not already an [activeRequest] and the
310+
* [BreakRequest.contexts] collection is not empty.
296311
*
297312
* @see processRequest
298313
*/
@@ -304,30 +319,27 @@ object BreakManager : RequestHandler<BreakRequest>(
304319
}
305320

306321
/**
307-
* If the request is fresh, local variables are populated through the [processRequest] method.
308-
* It then attempts to perform as many breaks within this tick as possible from the [instantBreaks] collection.
309-
* The [breakInfos] are then updated if the dependencies are present, E.G. if the user has rotations enabled,
310-
* or the player needs to swap to a different hotbar slot.
322+
* Handles populating the manager, updating break progresses, and clearing the active request
323+
* when all breaks are complete.
311324
*
312-
* @see performInstantBreaks
325+
* @see populateFrom
313326
* @see processNewBreak
327+
* @see handlePreProcessing
314328
* @see updateBreakProgress
315329
*/
316330
private fun SafeContext.processRequest(request: BreakRequest?) {
317331
if (PlaceManager.activeThisTick || InteractionManager.activeThisTick) return
318332

319333
request?.let { request ->
320334
logger.debug("Processing request", request)
321-
if (request.fresh) request.runSafeAutomated { populateFrom(request) }
335+
if (request.fresh) populateFrom(request)
322336
}
323337

324338
var noNew: Boolean
325339
var noProgression: Boolean
326340

327341
while (true) {
328-
noNew = request?.let {
329-
!request.runSafeAutomated { processNewBreak(request) }
330-
} != false
342+
noNew = request?.let { !processNewBreak(request) } != false
331343

332344
// Reversed so that the breaking order feels natural to the user as the primary break is always the
333345
// last break to be started
@@ -340,9 +352,7 @@ object BreakManager : RequestHandler<BreakRequest>(
340352
if (isEmpty()) true
341353
else {
342354
forEach { breakInfo ->
343-
breakInfo.request.runSafeAutomated {
344-
updateBreakProgress(breakInfo)
345-
}
355+
updateBreakProgress(breakInfo)
346356
}
347357
false
348358
}
@@ -362,13 +372,14 @@ object BreakManager : RequestHandler<BreakRequest>(
362372

363373
/**
364374
* Filters the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches
365-
* in positions. If a match is found, the [BreakInfo] is updated with the new context. Otherwise, the break is cancelled.
366-
* The [instantBreaks] and [breaks] collections are then populated with the new appropriate contexts, and the [maxBreaksThisTick]
375+
* in positions. If a match is found, the [BreakInfo] is updated with the new context.
376+
* The [breaks] collection is then populated with the new appropriate contexts, and the [maxBreaksThisTick]
367377
* value is set.
368378
*
369379
* @see canAccept
380+
* @see BreakInfo.updateInfo
370381
*/
371-
private fun AutomatedSafeContext.populateFrom(request: BreakRequest) {
382+
private fun SafeContext.populateFrom(request: BreakRequest) = request.runSafeAutomated {
372383
logger.debug("Populating from request", request)
373384

374385
// Sanitize the new breaks
@@ -381,24 +392,25 @@ object BreakManager : RequestHandler<BreakRequest>(
381392
breakInfos
382393
.filterNotNull()
383394
.forEach { info ->
384-
newBreaks
385-
.find { ctx -> ctx.blockPos == info.context.blockPos }
386-
?.let { ctx ->
387-
if ((!info.updatedThisTick || info.type == RedundantSecondary) || info.abandoned) {
388-
logger.debug("Updating info", info, ctx)
389-
if (info.type == RedundantSecondary)
390-
info.request.onStart?.invoke(this, info.context.blockPos)
391-
else if (info.abandoned) {
392-
info.abandoned = false
393-
info.request.onStart?.invoke(this, info.context.blockPos)
394-
} else
395-
info.request.onUpdate?.invoke(this, info.context.blockPos)
396-
397-
info.updateInfo(ctx, request)
398-
}
399-
newBreaks.remove(ctx)
400-
return@forEach
395+
val ctx = newBreaks.find { ctx ->
396+
ctx.blockPos == info.context.blockPos
397+
} ?: return@forEach
398+
399+
newBreaks.remove(ctx)
400+
401+
if (info.updatedThisTick && info.type != RedundantSecondary && !info.abandoned) return@forEach
402+
403+
logger.debug("Updating info", info, ctx)
404+
when {
405+
info.type == RedundantSecondary -> info.request.onStart?.invoke(this, info.context.blockPos)
406+
info.abandoned -> {
407+
info.abandoned = false
408+
info.request.onStart?.invoke(this, info.context.blockPos)
401409
}
410+
else -> info.request.onUpdate?.invoke(this, info.context.blockPos)
411+
}
412+
413+
info.updateInfo(ctx, request)
402414
}
403415

404416
breaks = newBreaks
@@ -428,52 +440,58 @@ object BreakManager : RequestHandler<BreakRequest>(
428440
return blockState.isNotEmpty && hardness != 600f && hardness != -1f
429441
}
430442

443+
/**
444+
* Updates the pre-processing for [BreakInfo] elements within [activeInfos] as long as they've been updated this tick.
445+
* This method also populates [rotationRequest] and [hotbarRequest].
446+
*
447+
* @see updatePreProcessing
448+
*/
431449
private fun SafeContext.handlePreProcessing() {
450+
if (activeInfos.isEmpty()) return
451+
432452
activeInfos
433453
.filter { it.updatedThisTick }
434454
.let { infos ->
435-
rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak }
436-
?.let { info ->
437-
val rotation = info.context.rotationRequest
438-
logger.debug("Requesting rotation", rotation)
439-
rotation.submit(false)
440-
}
441-
442-
if (activeInfos.isEmpty()) return
455+
rotationRequest = infos.lastOrNull { info ->
456+
info.breakConfig.rotateForBreak
457+
}?.let { info ->
458+
val rotation = info.context.rotationRequest
459+
logger.debug("Requesting rotation", rotation)
460+
rotation.submit(false)
461+
}
443462

444463
infos.forEach { it.updatePreProcessing() }
445464

446-
infos.firstOrNull()?.let { info ->
447-
infos.lastOrNull { it.swapInfo.swap && it.shouldProgress }?.let { last ->
448-
val minKeepTicks = if (info.swapInfo.longSwap || last.swapInfo.longSwap) 1 else 0
449-
val serverSwapTicks = max(info.breakConfig.serverSwapTicks, last.breakConfig.serverSwapTicks)
450-
hotbarRequest = with(info) {
451-
HotbarRequest(
452-
context.hotbarIndex,
453-
request,
454-
request.hotbarConfig.keepTicks.coerceAtLeast(minKeepTicks),
455-
request.hotbarConfig.swapPause.coerceAtLeast(serverSwapTicks - 1)
456-
).submit(false)
457-
}
458-
logger.debug("Submitted hotbar request", hotbarRequest)
459-
return
460-
}
465+
val first = infos.firstOrNull() ?: return@let
466+
val last = infos.lastOrNull { it.swapInfo.swap && it.shouldProgress } ?: return@let
467+
468+
val minKeepTicks = if (first.swapInfo.longSwap || last.swapInfo.longSwap) 1 else 0
469+
val serverSwapTicks = max(first.breakConfig.serverSwapTicks, last.breakConfig.serverSwapTicks)
470+
471+
hotbarRequest = with(last) {
472+
HotbarRequest(
473+
context.hotbarIndex,
474+
request,
475+
request.hotbarConfig.keepTicks.coerceAtLeast(minKeepTicks),
476+
request.hotbarConfig.swapPause.coerceAtLeast(serverSwapTicks - 1)
477+
).submit(false)
461478
}
479+
480+
logger.debug("Submitted hotbar request", hotbarRequest)
481+
return
462482
}
463483

464484
hotbarRequest = null
465-
466-
return
467485
}
468486

469487
/**
470488
* Attempts to start breaking as many [BreakContext]'s from the [breaks] collection as possible.
471489
*
472-
* @return false if a context cannot be started or the maximum active breaks has been reached.
490+
* @return false if a context cannot be started or the maximum active breaks have been reached.
473491
*
474492
* @see initNewBreak
475493
*/
476-
private fun AutomatedSafeContext.processNewBreak(request: BreakRequest): Boolean {
494+
private fun SafeContext.processNewBreak(request: BreakRequest): Boolean = request.runSafeAutomated {
477495
breaks.forEach { ctx ->
478496
if (breaksThisTick >= maxBreaksThisTick) return false
479497
if (!currentStackSelection.filterStack(player.inventory.getStack(ctx.hotbarIndex))) return@forEach
@@ -488,7 +506,15 @@ object BreakManager : RequestHandler<BreakRequest>(
488506
/**
489507
* Attempts to accept the [requestCtx] into the [breakInfos].
490508
*
491-
* @return the [BreakInfo] or null if the break context wasn't accepted.
509+
* If a primary [BreakInfo] is active, as long as the tick stage is valid, it is transformed
510+
* into a secondary break, so a new primary can be initialized. This means sending a
511+
* [net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action] with action: [net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK]
512+
* packet to the server to start the automated breaking server side.
513+
*
514+
* If there is no way to keep both breaks, and the primary break hasn't been updated yet,
515+
* the primary break is canceled. Otherwise, the break cannot be started.
516+
*
517+
* @return the [BreakInfo], or null, if the break context wasn't accepted.
492518
*/
493519
private fun AutomatedSafeContext.initNewBreak(
494520
requestCtx: BreakContext,
@@ -521,16 +547,18 @@ object BreakManager : RequestHandler<BreakRequest>(
521547
return primaryBreak
522548
}
523549

550+
/**
551+
* Simulates and updates the [abandonedBreak].
552+
*/
524553
private fun SafeContext.simulateAbandoned() {
525-
// Cancelled but double breaking so requires break manager to continue the simulation
554+
// Canceled but double breaking so requires break manager to continue the simulation
526555
val abandonedInfo = abandonedBreak ?: return
527556

528557
abandonedInfo.request.runSafeAutomated {
529558
abandonedInfo.context.blockPos
530559
.toStructure(TargetState.Empty)
531560
.toBlueprint()
532561
.simulate()
533-
.asSequence()
534562
.filterIsInstance<BreakResult.Break>()
535563
.filter { canAccept(it.context) }
536564
.sorted()
@@ -540,10 +568,13 @@ object BreakManager : RequestHandler<BreakRequest>(
540568
}
541569
}
542570

571+
/**
572+
* Checks if any active [BreakInfo]s are not updated this tick, and are within the timeframe of a valid tick stage.
573+
* If so, the [BreakInfo] is either canceled, or progressed if the break is redundant.
574+
*/
543575
private fun SafeContext.checkForCancels() {
544576
breakInfos
545577
.filterNotNull()
546-
.asSequence()
547578
.filter { !it.updatedThisTick && tickStage in it.breakConfig.tickStageMask }
548579
.forEach { info ->
549580
if (info.type == RedundantSecondary && !info.progressedThisTick) {
@@ -575,6 +606,7 @@ object BreakManager : RequestHandler<BreakRequest>(
575606
*
576607
* @see destroyBlock
577608
* @see startPending
609+
* @see nullify
578610
*/
579611
private fun AutomatedSafeContext.onBlockBreak(info: BreakInfo) {
580612
info.request.onStop?.invoke(this, info.context.blockPos)
@@ -619,9 +651,9 @@ object BreakManager : RequestHandler<BreakRequest>(
619651
/**
620652
* Attempts to cancel the break.
621653
*
622-
* Secondary blocks are monitored by the server, and keep breaking regardless of the clients actions.
623-
* This means that the break cannot be completely stopped, instead, it must be monitored as we can't start
624-
* more secondary break infos until the previous has broken or its state has turned to air.
654+
* Secondary blocks are monitored by the server and keep breaking regardless of the clients' actions.
655+
* This means that the break cannot be completely stopped. Instead, it must be monitored as we can't start
656+
* another secondary [BreakInfo] until the previous has broken or its state has become empty.
625657
*
626658
* If the user has [BreakConfig.unsafeCancels] enabled, the info is made redundant, and mostly ignored.
627659
* If not, the break continues.
@@ -652,23 +684,18 @@ object BreakManager : RequestHandler<BreakRequest>(
652684
}
653685
}
654686

655-
/**
656-
* Nullifies the break. If the block is not broken, the [BreakInfo.internalOnCancel] callback gets triggered
657-
*/
658687
private fun BreakInfo.nullify() =
659688
when (type) {
660689
Primary, Rebreak -> primaryBreak = null
661690
else -> secondaryBreak = null
662691
}
663692

664693
/**
665-
* A modified version of the vanilla updateBlockBreakingProgress method.
694+
* A modified version of the vanilla [net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress] method.
666695
*
667696
* @return if the update was successful.
668-
*
669-
* @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress
670697
*/
671-
private fun AutomatedSafeContext.updateBreakProgress(info: BreakInfo) {
698+
private fun SafeContext.updateBreakProgress(info: BreakInfo): Unit = info.request.runSafeAutomated {
672699
val ctx = info.context
673700

674701
info.progressedThisTick = true
@@ -756,11 +783,9 @@ object BreakManager : RequestHandler<BreakRequest>(
756783
}
757784

758785
/**
759-
* A modified version of the minecraft attackBlock method.
786+
* A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock] method.
760787
*
761788
* @return if the block started breaking successfully.
762-
*
763-
* @see net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock
764789
*/
765790
private fun AutomatedSafeContext.startBreaking(info: BreakInfo): Boolean {
766791
val ctx = info.context
@@ -852,6 +877,9 @@ object BreakManager : RequestHandler<BreakRequest>(
852877
return true
853878
}
854879

880+
/**
881+
* Wrapper method for calculating block-breaking delta.
882+
*/
855883
context(automatedSafeContext: AutomatedSafeContext)
856884
fun BlockState.calcBreakDelta(
857885
pos: BlockPos,
@@ -884,6 +912,9 @@ object BreakManager : RequestHandler<BreakRequest>(
884912
return inRange && correctMaterial
885913
}
886914

915+
/**
916+
* Interpolates the give [box] using the [BreakConfig]'s animation mode.
917+
*/
887918
private fun interpolateBox(box: Box, progress: Double, animationMode: BreakConfig.AnimationMode): Box {
888919
val boxCenter = Box(box.center, box.center)
889920
return when (animationMode) {

0 commit comments

Comments
 (0)