@@ -50,18 +50,25 @@ import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary
5050import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak
5151import com.lambda.interaction.request.breaking.BreakInfo.BreakType.RedundantSecondary
5252import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Secondary
53+ import com.lambda.interaction.request.breaking.BreakManager.abandonedBreak
5354import com.lambda.interaction.request.breaking.BreakManager.activeInfos
5455import com.lambda.interaction.request.breaking.BreakManager.activeRequest
5556import com.lambda.interaction.request.breaking.BreakManager.breakInfos
5657import com.lambda.interaction.request.breaking.BreakManager.breaks
5758import com.lambda.interaction.request.breaking.BreakManager.canAccept
5859import 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
5962import com.lambda.interaction.request.breaking.BreakManager.initNewBreak
6063import 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
6166import com.lambda.interaction.request.breaking.BreakManager.processNewBreak
6267import com.lambda.interaction.request.breaking.BreakManager.processRequest
68+ import com.lambda.interaction.request.breaking.BreakManager.rotationRequest
6369import com.lambda.interaction.request.breaking.BreakManager.simulateAbandoned
6470import com.lambda.interaction.request.breaking.BreakManager.updateBreakProgress
71+ import com.lambda.interaction.request.breaking.BreakManager.updatePreProcessing
6572import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock
6673import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions
6774import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs
@@ -96,6 +103,14 @@ import net.minecraft.util.math.Box
96103import kotlin.math.max
97104import 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+ */
99114object 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