From 9315d3f0805a3cb6122e6dafb78a5043ba972e69 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 10 Nov 2025 21:05:32 +0000 Subject: [PATCH 1/8] break manager documentation --- .../request/breaking/BreakManager.kt | 187 ++++++++++-------- 1 file changed, 109 insertions(+), 78 deletions(-) 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 3ef12f5ca..a7e49aeb9 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -50,18 +50,25 @@ import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak import com.lambda.interaction.request.breaking.BreakInfo.BreakType.RedundantSecondary import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Secondary +import com.lambda.interaction.request.breaking.BreakManager.abandonedBreak import com.lambda.interaction.request.breaking.BreakManager.activeInfos import com.lambda.interaction.request.breaking.BreakManager.activeRequest import com.lambda.interaction.request.breaking.BreakManager.breakInfos import com.lambda.interaction.request.breaking.BreakManager.breaks import com.lambda.interaction.request.breaking.BreakManager.canAccept import com.lambda.interaction.request.breaking.BreakManager.checkForCancels +import com.lambda.interaction.request.breaking.BreakManager.handlePreProcessing +import com.lambda.interaction.request.breaking.BreakManager.hotbarRequest import com.lambda.interaction.request.breaking.BreakManager.initNewBreak import com.lambda.interaction.request.breaking.BreakManager.maxBreaksThisTick +import com.lambda.interaction.request.breaking.BreakManager.nullify +import com.lambda.interaction.request.breaking.BreakManager.populateFrom import com.lambda.interaction.request.breaking.BreakManager.processNewBreak import com.lambda.interaction.request.breaking.BreakManager.processRequest +import com.lambda.interaction.request.breaking.BreakManager.rotationRequest import com.lambda.interaction.request.breaking.BreakManager.simulateAbandoned import com.lambda.interaction.request.breaking.BreakManager.updateBreakProgress +import com.lambda.interaction.request.breaking.BreakManager.updatePreProcessing import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock import com.lambda.interaction.request.breaking.BrokenBlockHandler.pendingActions import com.lambda.interaction.request.breaking.BrokenBlockHandler.setPendingConfigs @@ -96,6 +103,14 @@ import net.minecraft.util.math.Box import kotlin.math.max import kotlin.math.min +/** + * This manager is responsible for breaking blocks in the most efficient manner possible. It can be accessed + * from anywhere through a [BreakRequest], although it is not designed in the image of thread safety. + * + * If configured with the right options enabled, this manager can break two blocks simultaneously, even if the two breaks come from + * different requests. Each break will be handled using its own config, and just like the other managers, priority is a first-come, first-served + * style system. + */ object BreakManager : RequestHandler( 0, TickEvent.Pre, @@ -291,8 +306,8 @@ object BreakManager : RequestHandler( } /** - * Attempts to accept and process the request, if there is not already an [activeRequest]. - * If the request is processed and all breaks completed, the [activeRequest] is cleared. + * Attempts to accept and process the request, if there is not already an [activeRequest] and the + * [BreakRequest.contexts] collection is not empty. * * @see processRequest */ @@ -304,13 +319,12 @@ object BreakManager : RequestHandler( } /** - * If the request is fresh, local variables are populated through the [processRequest] method. - * It then attempts to perform as many breaks within this tick as possible from the [instantBreaks] collection. - * The [breakInfos] are then updated if the dependencies are present, E.G. if the user has rotations enabled, - * or the player needs to swap to a different hotbar slot. + * Handles populating the manager, updating break progresses, and clearing the active request + * when all breaks are complete. * - * @see performInstantBreaks + * @see populateFrom * @see processNewBreak + * @see handlePreProcessing * @see updateBreakProgress */ private fun SafeContext.processRequest(request: BreakRequest?) { @@ -318,16 +332,14 @@ object BreakManager : RequestHandler( request?.let { request -> logger.debug("Processing request", request) - if (request.fresh) request.runSafeAutomated { populateFrom(request) } + if (request.fresh) populateFrom(request) } var noNew: Boolean var noProgression: Boolean while (true) { - noNew = request?.let { - !request.runSafeAutomated { processNewBreak(request) } - } != false + noNew = request?.let { !processNewBreak(request) } != false // Reversed so that the breaking order feels natural to the user as the primary break is always the // last break to be started @@ -340,9 +352,7 @@ object BreakManager : RequestHandler( if (isEmpty()) true else { forEach { breakInfo -> - breakInfo.request.runSafeAutomated { - updateBreakProgress(breakInfo) - } + updateBreakProgress(breakInfo) } false } @@ -362,13 +372,14 @@ object BreakManager : RequestHandler( /** * Filters the requests [BreakContext]s, and iterates over the [breakInfos] collection looking for matches - * in positions. If a match is found, the [BreakInfo] is updated with the new context. Otherwise, the break is cancelled. - * The [instantBreaks] and [breaks] collections are then populated with the new appropriate contexts, and the [maxBreaksThisTick] + * in positions. If a match is found, the [BreakInfo] is updated with the new context. + * The [breaks] collection is then populated with the new appropriate contexts, and the [maxBreaksThisTick] * value is set. * * @see canAccept + * @see BreakInfo.updateInfo */ - private fun AutomatedSafeContext.populateFrom(request: BreakRequest) { + private fun SafeContext.populateFrom(request: BreakRequest) = request.runSafeAutomated { logger.debug("Populating from request", request) // Sanitize the new breaks @@ -381,24 +392,25 @@ object BreakManager : RequestHandler( breakInfos .filterNotNull() .forEach { info -> - newBreaks - .find { ctx -> ctx.blockPos == info.context.blockPos } - ?.let { ctx -> - if ((!info.updatedThisTick || info.type == RedundantSecondary) || info.abandoned) { - logger.debug("Updating info", info, ctx) - if (info.type == RedundantSecondary) - info.request.onStart?.invoke(this, info.context.blockPos) - else if (info.abandoned) { - info.abandoned = false - info.request.onStart?.invoke(this, info.context.blockPos) - } else - info.request.onUpdate?.invoke(this, info.context.blockPos) - - info.updateInfo(ctx, request) - } - newBreaks.remove(ctx) - return@forEach + val ctx = newBreaks.find { ctx -> + ctx.blockPos == info.context.blockPos + } ?: return@forEach + + newBreaks.remove(ctx) + + if (info.updatedThisTick && info.type != RedundantSecondary && !info.abandoned) return@forEach + + logger.debug("Updating info", info, ctx) + when { + info.type == RedundantSecondary -> info.request.onStart?.invoke(this, info.context.blockPos) + info.abandoned -> { + info.abandoned = false + info.request.onStart?.invoke(this, info.context.blockPos) } + else -> info.request.onUpdate?.invoke(this, info.context.blockPos) + } + + info.updateInfo(ctx, request) } breaks = newBreaks @@ -428,52 +440,58 @@ object BreakManager : RequestHandler( return blockState.isNotEmpty && hardness != 600f && hardness != -1f } + /** + * Updates the pre-processing for [BreakInfo] elements within [activeInfos] as long as they've been updated this tick. + * This method also populates [rotationRequest] and [hotbarRequest]. + * + * @see updatePreProcessing + */ private fun SafeContext.handlePreProcessing() { + if (activeInfos.isEmpty()) return + activeInfos .filter { it.updatedThisTick } .let { infos -> - rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak } - ?.let { info -> - val rotation = info.context.rotationRequest - logger.debug("Requesting rotation", rotation) - rotation.submit(false) - } - - if (activeInfos.isEmpty()) return + rotationRequest = infos.lastOrNull { info -> + info.breakConfig.rotateForBreak + }?.let { info -> + val rotation = info.context.rotationRequest + logger.debug("Requesting rotation", rotation) + rotation.submit(false) + } infos.forEach { it.updatePreProcessing() } - infos.firstOrNull()?.let { info -> - infos.lastOrNull { it.swapInfo.swap && it.shouldProgress }?.let { last -> - val minKeepTicks = if (info.swapInfo.longSwap || last.swapInfo.longSwap) 1 else 0 - val serverSwapTicks = max(info.breakConfig.serverSwapTicks, last.breakConfig.serverSwapTicks) - hotbarRequest = with(info) { - HotbarRequest( - context.hotbarIndex, - request, - request.hotbarConfig.keepTicks.coerceAtLeast(minKeepTicks), - request.hotbarConfig.swapPause.coerceAtLeast(serverSwapTicks - 1) - ).submit(false) - } - logger.debug("Submitted hotbar request", hotbarRequest) - return - } + val first = infos.firstOrNull() ?: return@let + val last = infos.lastOrNull { it.swapInfo.swap && it.shouldProgress } ?: return@let + + val minKeepTicks = if (first.swapInfo.longSwap || last.swapInfo.longSwap) 1 else 0 + val serverSwapTicks = max(first.breakConfig.serverSwapTicks, last.breakConfig.serverSwapTicks) + + hotbarRequest = with(last) { + HotbarRequest( + context.hotbarIndex, + request, + request.hotbarConfig.keepTicks.coerceAtLeast(minKeepTicks), + request.hotbarConfig.swapPause.coerceAtLeast(serverSwapTicks - 1) + ).submit(false) } + + logger.debug("Submitted hotbar request", hotbarRequest) + return } hotbarRequest = null - - return } /** * Attempts to start breaking as many [BreakContext]'s from the [breaks] collection as possible. * - * @return false if a context cannot be started or the maximum active breaks has been reached. + * @return false if a context cannot be started or the maximum active breaks have been reached. * * @see initNewBreak */ - private fun AutomatedSafeContext.processNewBreak(request: BreakRequest): Boolean { + private fun SafeContext.processNewBreak(request: BreakRequest): Boolean = request.runSafeAutomated { breaks.forEach { ctx -> if (breaksThisTick >= maxBreaksThisTick) return false if (!currentStackSelection.filterStack(player.inventory.getStack(ctx.hotbarIndex))) return@forEach @@ -488,7 +506,15 @@ object BreakManager : RequestHandler( /** * Attempts to accept the [requestCtx] into the [breakInfos]. * - * @return the [BreakInfo] or null if the break context wasn't accepted. + * If a primary [BreakInfo] is active, as long as the tick stage is valid, it is transformed + * into a secondary break, so a new primary can be initialized. This means sending a + * [net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action] with action: [net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK] + * packet to the server to start the automated breaking server side. + * + * If there is no way to keep both breaks, and the primary break hasn't been updated yet, + * the primary break is canceled. Otherwise, the break cannot be started. + * + * @return the [BreakInfo], or null, if the break context wasn't accepted. */ private fun AutomatedSafeContext.initNewBreak( requestCtx: BreakContext, @@ -521,8 +547,11 @@ object BreakManager : RequestHandler( return primaryBreak } + /** + * Simulates and updates the [abandonedBreak]. + */ private fun SafeContext.simulateAbandoned() { - // Cancelled but double breaking so requires break manager to continue the simulation + // Canceled but double breaking so requires break manager to continue the simulation val abandonedInfo = abandonedBreak ?: return abandonedInfo.request.runSafeAutomated { @@ -530,7 +559,6 @@ object BreakManager : RequestHandler( .toStructure(TargetState.Empty) .toBlueprint() .simulate() - .asSequence() .filterIsInstance() .filter { canAccept(it.context) } .sorted() @@ -540,10 +568,13 @@ object BreakManager : RequestHandler( } } + /** + * Checks if any active [BreakInfo]s are not updated this tick, and are within the timeframe of a valid tick stage. + * If so, the [BreakInfo] is either canceled, or progressed if the break is redundant. + */ private fun SafeContext.checkForCancels() { breakInfos .filterNotNull() - .asSequence() .filter { !it.updatedThisTick && tickStage in it.breakConfig.tickStageMask } .forEach { info -> if (info.type == RedundantSecondary && !info.progressedThisTick) { @@ -575,6 +606,7 @@ object BreakManager : RequestHandler( * * @see destroyBlock * @see startPending + * @see nullify */ private fun AutomatedSafeContext.onBlockBreak(info: BreakInfo) { info.request.onStop?.invoke(this, info.context.blockPos) @@ -619,9 +651,9 @@ object BreakManager : RequestHandler( /** * Attempts to cancel the break. * - * Secondary blocks are monitored by the server, and keep breaking regardless of the clients actions. - * This means that the break cannot be completely stopped, instead, it must be monitored as we can't start - * more secondary break infos until the previous has broken or its state has turned to air. + * Secondary blocks are monitored by the server and keep breaking regardless of the clients' actions. + * This means that the break cannot be completely stopped. Instead, it must be monitored as we can't start + * another secondary [BreakInfo] until the previous has broken or its state has become empty. * * If the user has [BreakConfig.unsafeCancels] enabled, the info is made redundant, and mostly ignored. * If not, the break continues. @@ -652,9 +684,6 @@ object BreakManager : RequestHandler( } } - /** - * Nullifies the break. If the block is not broken, the [BreakInfo.internalOnCancel] callback gets triggered - */ private fun BreakInfo.nullify() = when (type) { Primary, Rebreak -> primaryBreak = null @@ -662,13 +691,11 @@ object BreakManager : RequestHandler( } /** - * A modified version of the vanilla updateBlockBreakingProgress method. + * A modified version of the vanilla [net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress] method. * * @return if the update was successful. - * - * @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress */ - private fun AutomatedSafeContext.updateBreakProgress(info: BreakInfo) { + private fun SafeContext.updateBreakProgress(info: BreakInfo): Unit = info.request.runSafeAutomated { val ctx = info.context info.progressedThisTick = true @@ -756,11 +783,9 @@ object BreakManager : RequestHandler( } /** - * A modified version of the minecraft attackBlock method. + * A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock] method. * * @return if the block started breaking successfully. - * - * @see net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock */ private fun AutomatedSafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context @@ -852,6 +877,9 @@ object BreakManager : RequestHandler( return true } + /** + * Wrapper method for calculating block-breaking delta. + */ context(automatedSafeContext: AutomatedSafeContext) fun BlockState.calcBreakDelta( pos: BlockPos, @@ -884,6 +912,9 @@ object BreakManager : RequestHandler( return inRange && correctMaterial } + /** + * Interpolates the give [box] using the [BreakConfig]'s animation mode. + */ private fun interpolateBox(box: Box, progress: Double, animationMode: BreakConfig.AnimationMode): Box { val boxCenter = Box(box.center, box.center) return when (animationMode) { From 4a6bd8910cf085d46d65353a0ebca62d2599a88c Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Mon, 10 Nov 2025 22:04:18 +0000 Subject: [PATCH 2/8] other breaking-related documentation --- .../interaction/request/breaking/BreakInfo.kt | 12 ++--- .../request/breaking/BreakRequest.kt | 12 +++++ .../request/breaking/BrokenBlockHandler.kt | 50 ++++++++----------- .../request/breaking/RebreakHandler.kt | 23 +++++++++ .../interaction/request/breaking/SwapInfo.kt | 10 +++- 5 files changed, 71 insertions(+), 36 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 b1edef638..95d93c4e6 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -37,6 +37,9 @@ import net.minecraft.item.ItemStack import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action +/** + * A data class that holds all the information required to process and continue a break. + */ data class BreakInfo( override var context: BreakContext, var type: BreakType, @@ -140,16 +143,13 @@ data class BreakInfo( } context(_: SafeContext) - fun startBreakPacket() = - breakPacket(Action.START_DESTROY_BLOCK) + fun startBreakPacket() = breakPacket(Action.START_DESTROY_BLOCK) context(_: SafeContext) - fun stopBreakPacket() = - breakPacket(Action.STOP_DESTROY_BLOCK) + fun stopBreakPacket() = breakPacket(Action.STOP_DESTROY_BLOCK) context(_: SafeContext) - fun abortBreakPacket() = - breakPacket(Action.ABORT_DESTROY_BLOCK) + fun abortBreakPacket() = breakPacket(Action.ABORT_DESTROY_BLOCK) context(safeContext: SafeContext) private fun breakPacket(action: Action) = diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index ba7795963..e6a0bd3ca 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -24,12 +24,24 @@ import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.LogContext import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request +import com.lambda.interaction.request.breaking.BreakRequest.Companion.breakRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.isEmpty import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos +/** + * Contains the information necessary for initializing and continuing breaks within the [BreakManager]. + * + * The callbacks can be used to keep track of the break progress. + * + * The class has a private constructor to force use of the cleaner [BreakRequestDsl] builder. This is + * accessed through the [breakRequest] method. + * + * @param contexts A collection of [BreakContext]'s gathered from the [com.lambda.interaction.construction.simulation.BuildSimulator]. + * @param pendingInteractions A mutable, concurrent list to store the pending actions. + */ data class BreakRequest private constructor( val contexts: Collection, val pendingInteractions: MutableCollection, diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index b85c565aa..3817cb872 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -42,8 +42,8 @@ import net.minecraft.entity.ItemEntity import net.minecraft.util.math.ChunkSectionPos /** - * This object is designed to handle blocks that have been broken client side, yet are awaiting - * confirmation from the server, and / or an item drop. + * Designed to handle blocks that are deemed broken, yet are awaiting + * confirmation from the server, and/or an item drop. * * @see BreakManager */ @@ -105,7 +105,7 @@ object BrokenBlockHandler : PostActionHandler() { if (pending.breakConfig.breakConfirmation == BreakConfirmationMode.AwaitThenBreak || (pending.type == BreakInfo.BreakType.Rebreak && !pending.breakConfig.rebreak) - ) { + ) { destroyBlock(pending) } pending.internalOnBreak() @@ -121,44 +121,38 @@ object BrokenBlockHandler : PostActionHandler() { listen(priority = Int.MIN_VALUE) { if (it.entity !is ItemEntity) return@listen - run { + val pending = pendingActions.firstOrNull { info -> matchesBlockItem(info, it.entity) } ?: rebreak?.let { info -> - return@run if (matchesBlockItem(info, it.entity)) info - else null - } - }?.let { pending -> - pending.internalOnItemDrop(it.entity) - if (pending.callbacksCompleted) { - pending.stopPending() - if (lastPosStarted == pending.context.blockPos) { - RebreakHandler.offerRebreak(pending) - } + if (matchesBlockItem(info, it.entity)) info + else return@listen + } ?: return@listen + + pending.internalOnItemDrop(it.entity) + if (pending.callbacksCompleted) { + pending.stopPending() + if (lastPosStarted == pending.context.blockPos) { + RebreakHandler.offerRebreak(pending) } - return@listen } } } /** - * A modified version of the minecraft breakBlock method. + * A modified version of the minecraft [net.minecraft.client.world.ClientWorld.breakBlock] method. * - * Performs the actions required to display break particles, sounds, texture overlay, etc. - * based on the users settings. - * - * @return if the blocks state was set or not. - * - * @see net.minecraft.client.world.ClientWorld.breakBlock + * Performs the actions required to display breaking particles, sounds, texture overlay, etc. + * based on the user's settings. */ - fun SafeContext.destroyBlock(info: BreakInfo): Boolean { + fun SafeContext.destroyBlock(info: BreakInfo) { val ctx = info.context - if (player.isBlockBreakingRestricted(world, ctx.blockPos, gamemode)) return false - if (!player.mainHandStack.canMine(ctx.cachedState, world, ctx.blockPos, player)) return false + if (player.isBlockBreakingRestricted(world, ctx.blockPos, gamemode)) return + if (!player.mainHandStack.canMine(ctx.cachedState, world, ctx.blockPos, player)) return val block = ctx.cachedState.block - if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return false - if (ctx.cachedState.isEmpty) return false + if (block is OperatorBlock && !player.isCreativeLevelTwoOp) return + if (ctx.cachedState.isEmpty) return block.onBreak(world, ctx.blockPos, ctx.cachedState, player) val fluidState = fluidState(ctx.blockPos) @@ -166,7 +160,5 @@ object BrokenBlockHandler : PostActionHandler() { if (setState) block.onBroken(world, ctx.blockPos, ctx.cachedState) if (info.breakConfig.breakingTexture) info.setBreakingTextureStage(player, world, -1) - - return setState } } diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakHandler.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakHandler.kt index f6f9af58d..ccec4316e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakHandler.kt @@ -25,10 +25,15 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.breaking.BreakManager.calcBreakDelta import com.lambda.interaction.request.breaking.BrokenBlockHandler.destroyBlock +import com.lambda.interaction.request.breaking.RebreakHandler.rebreak import com.lambda.threading.runSafeAutomated import com.lambda.util.player.swingHand import net.minecraft.util.Hand +/** + * Designed to track the latest primary-broken [BreakInfo] in order to exploit a flaw in Minecraft's code that allows + * the user to break any block placed in said position using the progress from the previously broken block. + */ object RebreakHandler { var rebreak: BreakInfo? = null @@ -47,6 +52,10 @@ object RebreakHandler { } } + /** + * Tests to see if the [BreakInfo] can be accepted. If not, nothing happens. Otherwise, + * the [rebreak] is set, and the [BreakRequest.onReBreakStart] callback is invoked. + */ context(safeContext: SafeContext) fun offerRebreak(info: BreakInfo) { if (!info.rebreakable) return @@ -63,6 +72,15 @@ object RebreakHandler { rebreak = null } + /** + * [RebreakPotential.None] if it cannot be rebroken at all. + * + * [RebreakPotential.PartialProgress] if some progress would be added to the break. + * + * [RebreakPotential.Instant] if the block can be instantly rebroken. + * + * @return In what way this block can be rebroken. + */ context(_: SafeContext) fun BreakInfo.getRebreakPotential() = request.runSafeAutomated { rebreak?.let { reBreak -> @@ -81,6 +99,11 @@ object RebreakHandler { } ?: RebreakPotential.None } + /** + * Updates the current [rebreak] with a fresh [BreakContext], and attempts to rebreak the block if possible. + * + * @return A [RebreakResult] to indicate how the update has been processed. + */ context(_: SafeContext) fun handleUpdate(ctx: BreakContext, breakRequest: BreakRequest) = breakRequest.runSafeAutomated { val reBreak = this@RebreakHandler.rebreak ?: return@runSafeAutomated RebreakResult.Ignored 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 9ffd890be..c313245cf 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -27,6 +27,9 @@ import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Secondary import com.lambda.interaction.request.breaking.BreakManager.calcBreakDelta import com.lambda.threading.runSafeAutomated +/** + * A simple data class to store info about when the [BreakManager] should swap tool. + */ data class SwapInfo( private val type: BreakInfo.BreakType, private val automated: Automated, @@ -43,13 +46,18 @@ data class SwapInfo( companion object { val EMPTY = SwapInfo(Primary, AutomationConfig) + /** + * Calculates the contents and returns a [SwapInfo]. + * + * + */ context(_: SafeContext) fun BreakInfo.getSwapInfo() = request.runSafeAutomated { val breakDelta = context.cachedState.calcBreakDelta(context.blockPos, swapStack) val threshold = getBreakThreshold() - // Plus one as this is calculated before this ticks progress is calculated and the breakingTicks are incremented + // Plus one as this is calculated before this ticks' progress is calculated and the breakingTicks are incremented val breakTicks = (if (rebreakPotential.isPossible()) RebreakHandler.rebreak?.breakingTicks ?: throw IllegalStateException("Rebreak BreakInfo was null when rebreak was considered possible") else breakingTicks) + 1 - breakConfig.fudgeFactor From dccb0a8596ae2c17ef296d913842b9ffe7903497 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Tue, 11 Nov 2025 16:11:01 +0000 Subject: [PATCH 3/8] hotbar, interaction, inventory docs --- .../lambda/interaction/request/ActionInfo.kt | 4 +++ .../lambda/interaction/request/DebugLogger.kt | 3 ++ .../interaction/request/PositionBlocking.kt | 5 ++++ .../interaction/request/PostActionHandler.kt | 3 ++ .../com/lambda/interaction/request/Request.kt | 9 +++++- .../interaction/request/RequestConfig.kt | 20 ------------- .../interaction/request/RequestHandler.kt | 2 +- .../request/breaking/BreakConfig.kt | 3 +- .../request/breaking/BreakManager.kt | 4 +-- .../request/breaking/BreakRequest.kt | 6 ++-- .../request/hotbar/HotbarConfig.kt | 3 +- .../request/hotbar/HotbarManager.kt | 30 +++++++++++++++++++ .../request/hotbar/HotbarRequest.kt | 4 +-- .../request/interacting/InteractConfig.kt | 3 +- .../request/interacting/InteractRequest.kt | 4 +-- .../interacting/InteractedBlockHandler.kt | 5 ++++ .../request/interacting/InteractionManager.kt | 29 ++++++++++++++++-- .../request/inventory/InventoryAction.kt | 5 ++++ .../request/inventory/InventoryConfig.kt | 3 +- .../request/inventory/InventoryManager.kt | 26 ++++++++++++++-- .../request/inventory/InventoryRequest.kt | 17 +++++++---- .../request/placing/PlaceConfig.kt | 3 +- .../request/placing/PlaceRequest.kt | 4 +-- .../request/rotating/RotationConfig.kt | 4 +-- .../request/rotating/RotationRequest.kt | 4 +-- 25 files changed, 146 insertions(+), 57 deletions(-) delete mode 100644 src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt diff --git a/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt b/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt index 4c64ae909..4eb5b43ac 100644 --- a/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/ActionInfo.kt @@ -19,6 +19,10 @@ package com.lambda.interaction.request import com.lambda.interaction.construction.context.BuildContext +/** + * A simple interface to provide a basic object to hold key information that managers might need if information + * must persist longer than the request. + */ interface ActionInfo { val context: BuildContext val pendingInteractionsList: MutableCollection diff --git a/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt b/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt index 1bd34af3a..f841484b9 100644 --- a/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt +++ b/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt @@ -35,6 +35,9 @@ import imgui.flag.ImGuiWindowFlags import java.awt.Color import java.util.* +/** + * A simple logger that can be used to display information about what is happening within the managers. + */ class DebugLogger( val name: String ) { diff --git a/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt b/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt index ef3cd6a77..5086495a1 100644 --- a/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt +++ b/src/main/kotlin/com/lambda/interaction/request/PositionBlocking.kt @@ -19,6 +19,11 @@ package com.lambda.interaction.request import net.minecraft.util.math.BlockPos +/** + * Provides a [blockedPositions] list to inform other managers what positions should not be processed. + * + * Reasons a position could be blocked include pending interactions and or active interactions. + */ interface PositionBlocking { val blockedPositions: List } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt b/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt index e8afb77fe..5c4a8ee75 100644 --- a/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/PostActionHandler.kt @@ -25,6 +25,9 @@ import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.breaking.BrokenBlockHandler import com.lambda.util.collections.LimitedDecayQueue +/** + * A simple interface for handlers of actions that need some sort of server response after being executed. + */ abstract class PostActionHandler { abstract val pendingActions: LimitedDecayQueue diff --git a/src/main/kotlin/com/lambda/interaction/request/Request.kt b/src/main/kotlin/com/lambda/interaction/request/Request.kt index 5e319ae6c..3c09b2d0e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/Request.kt +++ b/src/main/kotlin/com/lambda/interaction/request/Request.kt @@ -19,8 +19,15 @@ package com.lambda.interaction.request import com.lambda.context.Automated +/** + * A simple format to ensure basic requirements and information when requesting a manager. + * + * @property requestId Used for tracking how many requests there have been and what number this request is. + * @property fresh If this request is new. + * @property done If this request has been completed. + */ abstract class Request : Automated { - abstract val requestID: Int + abstract val requestId: Int var fresh = true abstract val done: Boolean diff --git a/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt b/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt deleted file mode 100644 index e756864d8..000000000 --- a/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 - -interface RequestConfig \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index e7f77a209..48141cf1e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -55,7 +55,7 @@ abstract class RequestHandler( var queuedRequest: R? = null; protected set /** - * Represents if the handler performed any external actions within this tick + * Represents if the handler performed any actions within this tick */ var activeThisTick = false; protected set 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 3db058511..b01f1d31a 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -19,13 +19,12 @@ package com.lambda.interaction.request.breaking import com.lambda.config.groups.BuildConfig import com.lambda.event.Event -import com.lambda.interaction.request.RequestConfig import com.lambda.util.Describable import com.lambda.util.NamedEnum import net.minecraft.block.Block import java.awt.Color -interface BreakConfig : RequestConfig { +interface BreakConfig { val breakMode: BreakMode val sorter: SortMode val rebreak: Boolean 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 a7e49aeb9..f5f2f3bbf 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -104,8 +104,8 @@ import kotlin.math.max import kotlin.math.min /** - * This manager is responsible for breaking blocks in the most efficient manner possible. It can be accessed - * from anywhere through a [BreakRequest], although it is not designed in the image of thread safety. + * Manager responsible for breaking blocks in the most efficient manner possible. It can be accessed + * from anywhere through a [BreakRequest]. * * If configured with the right options enabled, this manager can break two blocks simultaneously, even if the two breaks come from * different requests. Each break will be handled using its own config, and just like the other managers, priority is a first-come, first-served diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index e6a0bd3ca..09eb4d05d 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -36,7 +36,7 @@ import net.minecraft.util.math.BlockPos * * The callbacks can be used to keep track of the break progress. * - * The class has a private constructor to force use of the cleaner [BreakRequestDsl] builder. This is + * A private constructor is used to force use of the cleaner [BreakRequestDsl] builder. This is * accessed through the [breakRequest] method. * * @param contexts A collection of [BreakContext]'s gathered from the [com.lambda.interaction.construction.simulation.BuildSimulator]. @@ -47,7 +47,7 @@ data class BreakRequest private constructor( val pendingInteractions: MutableCollection, private val automated: Automated ) : Request(), LogContext, Automated by automated { - override val requestID = ++requestCount + override val requestId = ++requestCount var onStart: (SafeContext.(BlockPos) -> Unit)? = null var onUpdate: (SafeContext.(BlockPos) -> Unit)? = null @@ -65,7 +65,7 @@ data class BreakRequest private constructor( override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { group("Break Request") { - value("Request ID", requestID) + value("Request ID", requestId) value("Contexts", contexts.size) group("Callbacks") { value("onStart", onStart != null) diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt index 43a2c806d..4c6646d8f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -18,14 +18,13 @@ package com.lambda.interaction.request.hotbar import com.lambda.event.Event -import com.lambda.interaction.request.RequestConfig /** * Abstract base class for configuring hotbar slot switch behavior. * * @param priority The priority of this configuration. */ -interface HotbarConfig : RequestConfig { +interface HotbarConfig { /** * The number of ticks to keep the current hotbar selection active. diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 6380f64db..3e79485e3 100644 --- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -28,11 +28,22 @@ import com.lambda.interaction.request.Logger import com.lambda.interaction.request.ManagerUtils.newStage import com.lambda.interaction.request.ManagerUtils.newTick import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.hotbar.HotbarManager.activeRequest import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap +import com.lambda.interaction.request.hotbar.HotbarManager.maxSwapsThisTick +import com.lambda.interaction.request.hotbar.HotbarManager.setActiveRequest import com.lambda.module.hud.ManagerDebugLoggers.hotbarManagerLogger import com.lambda.threading.runSafe import net.minecraft.item.ItemStack +/** + * Manager responsible for handling the current selected hotbar index. It can be accessed from anywhere through a + * [HotbarRequest] + * + * "Silent swapping" is a feature of this manager. If requested with a [HotbarRequest] that has [HotbarRequest.keepTicks] + * set to 0, assuming the request is accepted, the manager will only swap for the duration of the current [tickStage]. + * After which, the manager will end the request and swap back to the player's selected slot. + */ object HotbarManager : RequestHandler( 1, TickEvent.Pre, @@ -83,6 +94,14 @@ object HotbarManager : RequestHandler( return "Loaded Hotbar Manager" } + /** + * Attempts to accept the request and process it. If the [activeRequest] is not null, the new [request] matches hotbar index, + * and the new request has an equal or longer [HotbarRequest.keepTicks] than the current request, the new request is accepted. + * Otherwise, if the [activeRequest] is null, or is from an old request, assuming the swap doesn't exceed [maxSwapsThisTick], + * the request is accepted. + * + * @see setActiveRequest + */ override fun AutomatedSafeContext.handleRequest(request: HotbarRequest) { logger.debug("Handling request:", request) @@ -104,6 +123,10 @@ object HotbarManager : RequestHandler( setActiveRequest(request) } + /** + * Sets the [activeRequest]. This also calls [net.minecraft.client.network.ClientPlayerInteractionManager.syncSelectedSlot] to + * update the server now to keep predictability. + */ private fun AutomatedSafeContext.setActiveRequest(request: HotbarRequest) { maxSwapsThisTick = hotbarConfig.swapsPerTick if (request.slot != serverSlot) { @@ -116,6 +139,13 @@ object HotbarManager : RequestHandler( logger.success("Set active request", request) } + /** + * Called after every [tickStage] closes. This method checks if the current [activeRequest] should be stopped. + * This action is counted as another swap, so the conditions for a regular swap must be met. If the requests + * [HotbarConfig.sequenceStageMask] does not contain the current tick stage, no actions can be performed. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.syncSelectedSlot + */ private fun SafeContext.checkResetSwap() { activeRequest?.let { active -> val canStopSwap = swapsThisTick < maxSwapsThisTick diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index 49acf4d7c..d6f74e8ec 100644 --- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -28,7 +28,7 @@ class HotbarRequest( var keepTicks: Int = automated.hotbarConfig.keepTicks, var swapPause: Int = automated.hotbarConfig.swapPause ) : Request(), LogContext, Automated by automated { - override val requestID = ++requestCount + override val requestId = ++requestCount var activeRequestAge = 0 var swapPauseAge = 0 @@ -41,7 +41,7 @@ class HotbarRequest( override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { group("Hotbar Request") { - value("Request ID", requestID) + value("Request ID", requestId) value("Slot", slot) value("Keep Ticks", keepTicks) value("Swap Pause", swapPause) diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt index b563f5fdc..fa473159f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractConfig.kt @@ -19,11 +19,10 @@ package com.lambda.interaction.request.interacting import com.lambda.config.groups.BuildConfig import com.lambda.event.Event -import com.lambda.interaction.request.RequestConfig import com.lambda.util.Describable import com.lambda.util.NamedEnum -interface InteractConfig : RequestConfig { +interface InteractConfig { val rotate: Boolean val swingHand: Boolean val tickStageMask: Set diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt index dbb74e138..3f06ff57e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt @@ -33,7 +33,7 @@ data class InteractRequest( private val automated: Automated, val onInteract: ((BlockPos) -> Unit)? ) : Request(), LogContext, Automated by automated { - override val requestID = ++requestCount + override val requestId = ++requestCount override val done: Boolean get() = contexts.all { mc.world?.getBlockState(it.blockPos)?.matches(it.expectedState) == true } @@ -43,7 +43,7 @@ data class InteractRequest( override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { group("Interact Request") { - value("Request ID", requestID) + value("Request ID", requestId) value("Contexts", contexts.size) } } diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt index 582bf1541..b959d6215 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractedBlockHandler.kt @@ -28,6 +28,11 @@ import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +/** + * Designed to handle interactions pending a response from the server. + * + * @see InteractionManager + */ object InteractedBlockHandler : PostActionHandler() { override val pendingActions = LimitedDecayQueue( AutomationConfig.buildConfig.maxPendingInteractions, diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt index 476bd6758..9f8bce255 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -35,6 +35,10 @@ import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.interacting.InteractedBlockHandler.pendingActions import com.lambda.interaction.request.interacting.InteractedBlockHandler.setPendingConfigs import com.lambda.interaction.request.interacting.InteractedBlockHandler.startPending +import com.lambda.interaction.request.interacting.InteractionManager.activeRequest +import com.lambda.interaction.request.interacting.InteractionManager.maxInteractionsThisTick +import com.lambda.interaction.request.interacting.InteractionManager.populateFrom +import com.lambda.interaction.request.interacting.InteractionManager.potentialInteractions import com.lambda.interaction.request.interacting.InteractionManager.processRequest import com.lambda.interaction.request.placing.PlaceManager import com.lambda.module.hud.ManagerDebugLoggers.interactionManagerLogger @@ -44,6 +48,10 @@ import com.lambda.util.player.swingHand import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket import net.minecraft.util.Hand +/** + * Manager responsible for handling block interactions other than placing. It can be accessed from anywhere + * through an [InteractRequest] + */ object InteractionManager : RequestHandler( 0, TickEvent.Pre, @@ -51,9 +59,9 @@ object InteractionManager : RequestHandler( TickEvent.Input.Post, TickEvent.Player.Post, onOpen = { - if (InteractionManager.potentialInteractions.isNotEmpty()) + if (potentialInteractions.isNotEmpty()) InteractionManager.logger.newStage(InteractionManager.tickStage) - InteractionManager.activeRequest?.let { it.runSafeAutomated { processRequest(it) } } + activeRequest?.let { it.runSafeAutomated { processRequest(it) } } } ), PositionBlocking, Logger { private var activeRequest: InteractRequest? = null @@ -90,6 +98,12 @@ object InteractionManager : RequestHandler( return "Loaded Interaction Manager" } + /** + * Attempts to accept and process the request, if there is not already an [activeRequest] and the request's [InteractRequest.contexts] + * collection is not empty. + * + * @see processRequest + */ override fun AutomatedSafeContext.handleRequest(request: InteractRequest) { if (activeRequest != null || request.contexts.isEmpty()) return @@ -98,6 +112,11 @@ object InteractionManager : RequestHandler( if (interactionsThisTick > 0) activeThisTick = true } + /** + * Handles populating the manager and performing interactions. + * + * @see populateFrom + */ fun AutomatedSafeContext.processRequest(request: InteractRequest) { if (BreakManager.activeThisTick || PlaceManager.activeThisTick) return @@ -138,6 +157,12 @@ object InteractionManager : RequestHandler( } } + /** + * Populates the [potentialInteractions] collection, and sets the configurations + * + * @see setPendingConfigs + * @see maxInteractionsThisTick + */ private fun Automated.populateFrom(request: InteractRequest) { logger.debug("Populating from request", request) setPendingConfigs() diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryAction.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryAction.kt index 5297cc62f..db527337e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryAction.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryAction.kt @@ -19,6 +19,11 @@ package com.lambda.interaction.request.inventory import com.lambda.context.SafeContext +/** + * Represents a type of action. Inventory actions are strictly inventory-related. + * Other actions can be external actions that happen some time within the sequence of inventory actions + * to avoid having to use multiple requests. + */ sealed interface InventoryAction { val action: SafeContext.() -> Unit diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt index fcfe09b06..2d03d437c 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryConfig.kt @@ -21,12 +21,11 @@ import com.lambda.event.events.TickEvent import com.lambda.interaction.material.ContainerSelection import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.request.RequestConfig import com.lambda.util.Describable import com.lambda.util.NamedEnum import net.minecraft.block.Block -interface InventoryConfig : RequestConfig { +interface InventoryConfig { val actionsPerSecond: Int val tickStageMask: Set val disposables: Set diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt index df97b9888..2c4310e50 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -27,6 +27,8 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.Logger import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.inventory.InventoryManager.actions +import com.lambda.interaction.request.inventory.InventoryManager.alteredSlots import com.lambda.interaction.request.placing.PlaceManager import com.lambda.module.hud.ManagerDebugLoggers.inventoryManagerLogger import com.lambda.threading.runSafe @@ -41,6 +43,10 @@ import net.minecraft.screen.PlayerScreenHandler import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot +/** + * Manager designed to handle inventory actions. One of the key features being the inventory change detection to + * avoid accepting old information from the server in cases where ping is high. This helps to prevent desync. + */ object InventoryManager : RequestHandler( 1, TickEvent.Pre, @@ -57,7 +63,7 @@ object InventoryManager : RequestHandler( private var screenHandler: ScreenHandler? = null set(value) { - if (value != null && field !== value) + if (value != null && field?.syncId != value.syncId) slots = getStacks(value.slots) field = value } @@ -85,6 +91,13 @@ object InventoryManager : RequestHandler( return "Loaded Inventory Manager" } + /** + * Attempts to accept the request and perform the actions. If, for example, the tick stage isn't valid, or + * not all the actions can be performed and [InventoryRequest.settleForLess] is set to false, the request is rejected. + * All checks aside from tick stage are ignored if the request has [InventoryRequest.mustPerform] set to true. + * This is typically used in dangerous situations where typical rules are worth breaking. For example, if the player + * needs to equip a totem of undying. + */ override fun AutomatedSafeContext.handleRequest(request: InventoryRequest) { val inventoryActionCount = request.actions.count { it is InventoryAction.Inventory } if (inventoryActionCount >= request.inventoryConfig.actionsPerSecond - actionsThisSecond && @@ -115,6 +128,9 @@ object InventoryManager : RequestHandler( if (actionsThisTick > 0) activeThisTick = true } + /** + * Populates the [actions] collection and sets some configurations. + */ private fun populateFrom(request: InventoryRequest) { PlaceManager.logger.debug("Populating from request", request) actions = request.actions.toMutableList() @@ -122,8 +138,14 @@ object InventoryManager : RequestHandler( alteredSlots.setDecayTime(AutomationConfig.desyncTimeout * 50L) } + /** + * Detects changes in item stacks between now and the last time slots were cached and + * adds them all to the [alteredSlots] collection where they can be compared to + * incoming [InventoryS2CPacket] and [ScreenHandlerSlotUpdateS2CPacket] packets to decide whether to + * block certain updates. + */ private fun SafeContext.indexInventoryChanges() { - if (player.currentScreenHandler !== screenHandler) return + if (player.currentScreenHandler.syncId != screenHandler?.syncId) return val changes = screenHandler?.slots ?.filter { !it.stack.equal(slots[it.id]) } ?.map { Pair(it.id, Pair(slots[it.id], it.stack.copy())) } diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt index 8893ee390..4e9518bfe 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt @@ -30,9 +30,13 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -@DslMarker -private annotation class InvRequestDsl - +/** + * A private constructor is used to enforce use of the [InvRequestDsl] builder. + * + * @property actions A list of inventory actions to be performed in the request. + * @property settleForLess A flag indicating whether to settle for partial completion of the request. + * @property mustPerform A flag indicating whether the request must be performed regardless of conditions as long as the tick stage is valid. + */ class InventoryRequest private constructor( val actions: List, val settleForLess: Boolean, @@ -40,7 +44,7 @@ class InventoryRequest private constructor( automated: Automated, val onComplete: (SafeContext.() -> Unit)? ) : Request(), LogContext, Automated by automated { - override val requestID = ++requestCount + override val requestId = ++requestCount override var done = false override fun submit(queueIfClosed: Boolean) = @@ -48,11 +52,14 @@ class InventoryRequest private constructor( override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { group("Inventory Request") { - value("Request ID", requestID) + value("Request ID", requestId) value("Action Count", actions.size) } } + @DslMarker + private annotation class InvRequestDsl + @InvRequestDsl class InvRequestBuilder(val settleForLess: Boolean, val mustPerform: Boolean) { val actions = mutableListOf() diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt index 15cf19b4e..a76a35899 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceConfig.kt @@ -19,11 +19,10 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig import com.lambda.event.Event -import com.lambda.interaction.request.RequestConfig import com.lambda.util.Describable import com.lambda.util.NamedEnum -interface PlaceConfig : RequestConfig { +interface PlaceConfig { val rotateForPlace: Boolean val airPlace: AirPlaceMode val axisRotateSetting: Boolean diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index c5576a1c3..ad41ef7c1 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -35,7 +35,7 @@ data class PlaceRequest( private val automated: Automated, val onPlace: (SafeContext.(BlockPos) -> Unit)? = null ) : Request(), LogContext, Automated by automated { - override val requestID = ++requestCount + override val requestId = ++requestCount override val done: Boolean get() = runSafe { @@ -47,7 +47,7 @@ data class PlaceRequest( override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { group("PlaceRequest") { - value("Request ID", requestID) + value("Request ID", requestId) value("Contexts", contexts.size) } } diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt index cb223b421..355fa7f79 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationConfig.kt @@ -17,9 +17,7 @@ package com.lambda.interaction.request.rotating -import com.lambda.interaction.request.RequestConfig - -interface RotationConfig : RequestConfig { +interface RotationConfig { /** * - [RotationMode.Silent] Spoofing server-side rotation. * - [RotationMode.Sync] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt index dc977e8b8..6a5bb9b93 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt @@ -33,7 +33,7 @@ data class RotationRequest( var decayTicks: Int = automated.rotationConfig.decayTicks, val speedMultiplier: Double = 1.0 ) : Request(), LogContext, Automated by automated { - override val requestID = ++requestCount + override val requestId = ++requestCount var age = 0 @@ -45,7 +45,7 @@ data class RotationRequest( override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { group("Rotation Request") { - value("Request ID", requestID) + value("Request ID", requestId) value("Rotation Mode", rotationMode) value("Turn Speed", turnSpeed) value("Keep Ticks", keepTicks) From 0ca4d9e8319db8a356cf8d354b20b71914132bd6 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Nov 2025 02:02:42 +0000 Subject: [PATCH 4/8] rotation manager and place manager docs --- .../request/breaking/BrokenBlockHandler.kt | 2 +- .../request/placing/PlaceManager.kt | 33 ++++++++----------- .../request/placing/PlacedBlockHandler.kt | 3 ++ .../request/rotating/RotationManager.kt | 32 ++++++++++++++++-- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 3817cb872..1083fbe62 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -43,7 +43,7 @@ import net.minecraft.util.math.ChunkSectionPos /** * Designed to handle blocks that are deemed broken, yet are awaiting - * confirmation from the server, and/or an item drop. + * confirmation from the server and/or an item drop. * * @see BreakManager */ diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index ed3de1265..8e03ee3a7 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -69,6 +69,9 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.GameMode import kotlin.math.min +/** + * Manager designed to place blocks. + */ object PlaceManager : RequestHandler( 0, TickEvent.Pre, @@ -135,11 +138,10 @@ object PlaceManager : RequestHandler( } /** - * If the request is fresh, local variables are populated through the [processRequest] method. - * It then attempts to perform as many placements within this tick as possible from the [potentialPlacements] collection. - * - * If all the [maxPlacementsThisTick] limit is reached and the user has rotations enabled, it will start rotating to - * the next predicted placement in the list for optimal speed. + * Returns immediately if [BreakManager] or [InteractionManager] have been active this tick. + * Otherwise, for fresh requests, [populateFrom] is called to fill the [potentialPlacements] collection. + * It then attempts to perform as many placements as possible from the [potentialPlacements] collection within + * the [maxPlacementsThisTick] limit. * * @see populateFrom * @see placeBlock @@ -182,7 +184,7 @@ object PlaceManager : RequestHandler( /** * Filters the [request]'s [PlaceContext]s, placing them into the [potentialPlacements] collection, and - * setting the maxPlacementsThisTick value. + * setting other configurations. * * @see isPosBlocked */ @@ -205,9 +207,8 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft interactBlock method, renamed to better suit its usage. - * - * @see net.minecraft.client.network.ClientPlayerInteractionManager.interactBlock + * A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.interactBlock] method, + * renamed to better suit its usage. */ private fun AutomatedSafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() @@ -224,9 +225,7 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft interactBlockInternal method. - * - * @see net.minecraft.client.network.ClientPlayerInteractionManager.interactBlockInternal + * A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.interactBlockInternal] method. */ private fun AutomatedSafeContext.interactBlockInternal( placeContext: PlaceContext, @@ -267,9 +266,7 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft useOnBlock method. - * - * @see net.minecraft.item.Item.useOnBlock + * A modified version of the minecraft [net.minecraft.item.Item.useOnBlock] method. */ private fun AutomatedSafeContext.useOnBlock( placeContext: PlaceContext, @@ -297,9 +294,7 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft place method. - * - * @see net.minecraft.item.BlockItem.place + * A modified version of the minecraft [net.minecraft.item.BlockItem.place] method. */ private fun AutomatedSafeContext.place( placeContext: PlaceContext, @@ -396,7 +391,7 @@ object PlaceManager : RequestHandler( } /** - * Plays the block placement sound at a given position. + * Plays the block placement sound at a given [pos]. */ fun SafeContext.placeSound(state: BlockState, pos: BlockPos) { val blockSoundGroup = state.soundGroup diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt index 8e3a7ca1d..e3fc0c82f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -29,6 +29,9 @@ import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue +/** + * Designed to handle placements pending a response from the server. + */ object PlacedBlockHandler : PostActionHandler() { override val pendingActions = LimitedDecayQueue( AutomationConfig.buildConfig.maxPendingInteractions, diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt index 15fcbfc4a..f05a6dc34 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt @@ -33,6 +33,10 @@ import com.lambda.interaction.BaritoneManager import com.lambda.interaction.request.Logger import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotating.Rotation.Companion.slerp +import com.lambda.interaction.request.rotating.RotationManager.activeRequest +import com.lambda.interaction.request.rotating.RotationManager.activeRotation +import com.lambda.interaction.request.rotating.RotationManager.serverRotation +import com.lambda.interaction.request.rotating.RotationManager.updateActiveRotation import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.hud.ManagerDebugLoggers.rotationManagerLogger import com.lambda.threading.runGameScheduled @@ -53,6 +57,9 @@ import kotlin.math.atan2 import kotlin.math.cos import kotlin.math.sin +/** + * Manager designed to rotate the player and adjust movement input to match the camera's direction. + */ object RotationManager : RequestHandler( 1, TickEvent.Pre, @@ -62,8 +69,7 @@ object RotationManager : RequestHandler( ), Logger { var activeRotation = Rotation.ZERO var serverRotation = Rotation.ZERO - @JvmStatic - var prevServerRotation = Rotation.ZERO + @JvmStatic var prevServerRotation = Rotation.ZERO var baritoneRequest: RotationRequest? = null var activeRequest: RotationRequest? = null @@ -138,6 +144,12 @@ object RotationManager : RequestHandler( return "Loaded Rotation Manager" } + /** + * If the [activeRequest] is from an older tick or null, and the [request]'s target rotation is not null, + * the request is accepted and set as the [activeRequest]. The [activeRotation] is then updated. + * + * @see updateActiveRotation + */ override fun AutomatedSafeContext.handleRequest(request: RotationRequest) { activeRequest?.let { if (it.age <= 0) return } if (request.target.targetRotation.value != null) { @@ -148,6 +160,11 @@ object RotationManager : RequestHandler( } } + /** + * If the rotation has not been changed this tick, the [activeRequest]'s target rotation is updated, and + * likewise the [activeRotation]. The [activeRequest] is then updated, ticking the [RotationRequest.keepTicks] + * and [RotationRequest.decayTicks]. + */ @JvmStatic fun processRotations() = runSafe { if (activeRequest != null) activeThisTick = true @@ -171,6 +188,11 @@ object RotationManager : RequestHandler( } } + /** + * Calculates and sets the optimal movement input for moving in the direction the player is facing. This + * is not the direction the rotation manager is rotated towards, but the underlying player rotation, typically + * also the camera's rotation. + */ @JvmStatic fun redirectStrafeInputs(input: Input) = runSafe { if (activeRequest == baritoneRequest) return@runSafe @@ -263,11 +285,15 @@ object RotationManager : RequestHandler( } } + /** + * Updates the [activeRotation]. If [activeRequest] is null, the player's rotation is used. + * Otherwise, the [serverRotation] is interpolated towards the [RotationRequest.target] rotation. + */ private fun SafeContext.updateActiveRotation() { activeRotation = activeRequest?.let { request -> val rotationTo = if (request.keepTicks >= 0) request.target.targetRotation.value - ?: activeRotation // same context gets used again && the rotation is null this tick + ?: activeRotation // the same context gets used again && the rotation is null this tick else player.rotation val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier From 3676ccf7f7c63b8fb8182c28c103a5e94a67680d Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Nov 2025 17:25:45 +0000 Subject: [PATCH 5/8] build sim docs --- .../construction/context/BuildContext.kt | 3 ++ .../processing/PlacementProcessor.kt | 5 +++ .../construction/processing/ProcessingStep.kt | 22 ------------ .../processing/ProcessorRegistry.kt | 11 ++++++ .../construction/processing/TaskPlanner.kt | 20 ----------- .../construction/result/Contextual.kt | 3 ++ .../construction/result/Dependent.kt | 3 ++ .../construction/result/Drawable.kt | 3 ++ .../construction/result/Navigable.kt | 3 ++ .../construction/result/Resolvable.kt | 3 ++ .../result/results/InteractResult.kt | 4 +++ .../result/results/PostSimResult.kt | 3 ++ .../construction/simulation/BuildSimulator.kt | 15 ++++++++ .../construction/simulation/ISimInfo.kt | 12 +++++++ .../construction/simulation/Sim.kt | 36 ++++++++++++++++++- .../simulation/checks/BasicChecker.kt | 3 ++ .../simulation/checks/BreakSim.kt | 3 ++ .../simulation/checks/PlaceSim.kt | 3 ++ .../simulation/checks/PostProcessingSim.kt | 3 ++ .../rotating/visibilty/VisibilityChecker.kt | 13 ++++--- 20 files changed, 121 insertions(+), 50 deletions(-) delete mode 100644 src/main/kotlin/com/lambda/interaction/construction/processing/ProcessingStep.kt delete mode 100644 src/main/kotlin/com/lambda/interaction/construction/processing/TaskPlanner.kt diff --git a/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 2cd5893b1..b232fd3ec 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -25,6 +25,9 @@ import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos +/** + * Holds the necessary information for managers to perform actions. + */ abstract class BuildContext : Comparable, Drawable, Automated { abstract val hitResult: BlockHitResult abstract val rotationRequest: RotationRequest diff --git a/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt b/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt index d1880aedc..5fa00bfa2 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/processing/PlacementProcessor.kt @@ -20,6 +20,11 @@ package com.lambda.interaction.construction.processing import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos +/** + * The class all pre-processors must extend to provide the structure. Preprocessors are used to + * optimize how blocks are simulated. Some blocks might only be placeable on certain sides, so it is + * unnecessary to scan all of them, for example. + */ abstract class PlacementProcessor { abstract fun acceptsState(state: BlockState): Boolean abstract fun preProcess(state: BlockState, pos: BlockPos, accumulator: PreProcessingInfoAccumulator) diff --git a/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessingStep.kt b/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessingStep.kt deleted file mode 100644 index 569726982..000000000 --- a/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessingStep.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.construction.processing - -import com.lambda.task.Task - -abstract class ProcessingStep : Task() diff --git a/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt b/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt index 1d219c4d6..83dd717b3 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt @@ -29,6 +29,10 @@ object ProcessorRegistry : Loadable { private val processors = getInstances() private val processorCache = Collections.synchronizedMap(mutableMapOf()) + /** + * List of properties that can be processed after the block is placed. This is often used to ignore these properties + * when placing blocks, as sometimes they can only be set to the right state after placement. + */ val postProcessedProperties = setOf( Properties.EXTENDED, Properties.EYE, @@ -106,6 +110,13 @@ object ProcessorRegistry : Loadable { override fun load() = "Loaded ${processors.size} pre processors" + /** + * [PreProcessingInfo]'s are cached to avoid duplicate computations as block states are immutable. + * + * @return A [PreProcessingInfo] object containing information about the block state. This method runs through + * each pre-processor checking if the state can be accepted. If so, the state is passed through the pre-processor + * which can call the functions within the [PreProcessingInfoAccumulator] DSL to modify the information. + */ fun TargetState.getProcessingInfo(pos: BlockPos): PreProcessingInfo? = if (this !is TargetState.State) PreProcessingInfo.DEFAULT else { diff --git a/src/main/kotlin/com/lambda/interaction/construction/processing/TaskPlanner.kt b/src/main/kotlin/com/lambda/interaction/construction/processing/TaskPlanner.kt deleted file mode 100644 index 85b393520..000000000 --- a/src/main/kotlin/com/lambda/interaction/construction/processing/TaskPlanner.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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.construction.processing - -object TaskPlanner diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/Contextual.kt b/src/main/kotlin/com/lambda/interaction/construction/result/Contextual.kt index a95f01cb5..849a251f6 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/Contextual.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/Contextual.kt @@ -20,6 +20,9 @@ package com.lambda.interaction.construction.result import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.request.hotbar.HotbarManager +/** + * Represents a result holding a [BuildContext]. + */ interface Contextual : ComparableResult { val context: BuildContext diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/Dependent.kt b/src/main/kotlin/com/lambda/interaction/construction/result/Dependent.kt index d688892bc..3d61ac5b2 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/Dependent.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/Dependent.kt @@ -17,6 +17,9 @@ package com.lambda.interaction.construction.result +/** + * Represents a [BuildResult] that depends on another [BuildResult]. + */ interface Dependent { val dependency: BuildResult val lastDependency: BuildResult diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt b/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt index 84b71901e..ba9e05615 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt @@ -19,6 +19,9 @@ package com.lambda.interaction.construction.result import com.lambda.graphics.renderer.esp.ShapeBuilder +/** + * Represents a [BuildResult] that can be rendered in-game. + */ interface Drawable { fun ShapeBuilder.buildRenderer() } diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt b/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt index 89404fa38..fc5483c2c 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt @@ -19,6 +19,9 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.Goal +/** + * Represents a [BuildResult] with a pathing goal. + */ interface Navigable { val goal: Goal } diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt b/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt index 951256cb0..bbf96023c 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt @@ -20,6 +20,9 @@ package com.lambda.interaction.construction.result import com.lambda.context.Automated import com.lambda.task.Task +/** + * Represents a [BuildResult] with a resolvable [Task] + */ interface Resolvable { context(automated: Automated) fun resolve(): Task<*> diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/results/InteractResult.kt b/src/main/kotlin/com/lambda/interaction/construction/result/results/InteractResult.kt index 67a6012ad..b074036d2 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/results/InteractResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/results/InteractResult.kt @@ -30,6 +30,10 @@ import net.minecraft.util.math.BlockPos sealed class InteractResult : BuildResult() { override val name: String get() = "${this::class.simpleName} at ${pos.toShortString()}" + /** + * Represents a successful interaction. All checks have been passed. + * @param context The context of the interaction. + */ data class Interact( override val pos: BlockPos, override val context: InteractionContext diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/results/PostSimResult.kt b/src/main/kotlin/com/lambda/interaction/construction/result/results/PostSimResult.kt index 2f016a0f7..c607af354 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/results/PostSimResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/results/PostSimResult.kt @@ -24,6 +24,9 @@ import net.minecraft.util.math.BlockPos sealed class PostSimResult : BuildResult() { override val name: String get() = "${this::class.simpleName} at ${pos.toShortString()}" + /** + * No result can be found for the given position. + */ data class NoMatch( override val pos: BlockPos, ) : PostSimResult() { 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 78612b403..e088d651a 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -34,7 +34,22 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.supervisorScope import net.minecraft.util.math.Vec3d +/** + * The public API for simulating a blueprint. + */ object BuildSimulator : Sim() { + /** + * Iterates over the blueprint and performs the best suited simulation. Each simulation adds [BuildResult]s to + * the provided concurrent set. This method uses coroutines to perform the simulations in parallel. The results + * will likely not be returned in the same order they were simulated due to the parallel nature of the simulations. + * + * @return A set of [BuildResult]s + * + * @see ISimInfo.sim + * @see simPostProcessing + * @see simPlacement + * @see simBreak + */ context(automatedSafeContext: AutomatedSafeContext) fun Blueprint.simulate( pov: Vec3d = automatedSafeContext.player.eyePos diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/ISimInfo.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/ISimInfo.kt index 3a460ecd4..ca5e1bc58 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/ISimInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/ISimInfo.kt @@ -29,6 +29,10 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import java.util.* +/** + * An interface representing all the information required to simulate a state. All simulators must present their public api + * as an extension of the [SimInfo] class to allow easy access through the DSL style sim builder. + */ interface ISimInfo : Automated { val pos: BlockPos val state: BlockState @@ -39,6 +43,9 @@ interface ISimInfo : Automated { val dependencyStack: Stack> companion object { + /** + * Creates a [SimInfo], checks its basic requirements, and runs the [simBuilder] block. + */ @SimDsl context(_: BuildSimulator) suspend fun AutomatedSafeContext.sim( @@ -56,6 +63,11 @@ interface ISimInfo : Automated { ).takeIf { it.hasBasicRequirements() }?.run { simBuilder() } } + /** + * Creates a new [SimInfo] using the current [ISimInfo]'s [dependencyStack] and [concurrentResults], + * checks its basic requirements, and runs the [simBuilder] block. As simulations tend to make use of + * concurrency, a new stack is created and the dependencies from the previous stack are added. + */ @SimDsl context(_: AutomatedSafeContext) suspend fun ISimInfo.sim( diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt index 1f47b9b8c..b65ffcbd6 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt @@ -41,11 +41,40 @@ import kotlin.math.pow @DslMarker annotation class SimDsl +/** + * A class designed to simulate transforming a BlockState at a specified BlockPos to + * a TargetState. + * + * Some [Sim]s might need to call other sims as an intermediary between the current BlockState + * and the TargetState. In this case, we use a dependency system to ensure type safety. + * All sims must only return either [GenericResult]s or typed [BuildResult]s. For example, the BreakSim + * must only return BreakResults. For this reason, each type has its own Dependency result. + * To make sure that the results added are of the correct type, each [Sim] must be called from another [Sim]. + * Assuming the dependency stack has not reached max capacity, the original sim is then added to the dependency stack + * kept within the [SimInfo] object. Each [BuildResult] added is then iterated over the dependency stack, calling + * [dependentUpon] on each one. By the end, the result will be a nested group, with your initial [BuildResult] at + * the very bottom, which is then added to the [ISimInfo.concurrentResults] set. After a sim is completed, the dependency + * is then popped from the stack. + * + * @param T The type of [BuildResult] this sim produces. + * + * @see com.lambda.interaction.construction.result.Dependent + * @see dependentUpon + * @see withDependent + */ @SimDsl abstract class Sim : Results { + /** + * Can be overridden to return a typed Dependent result with the initial [buildResult] nested inside. + * + * @see com.lambda.interaction.construction.result.Dependent + */ @SimDsl open fun dependentUpon(buildResult: BuildResult): BuildResult = buildResult + /** + * Pushes and pops the [dependent] onto and off of the dependency stack unless the [maxSimDependencies] is reached. + */ protected suspend fun ISimInfo.withDependent(dependent: Sim<*>, block: suspend () -> Unit) { // +1 because the build sim counts as a dependent if (dependencyStack.size >= maxSimDependencies + 1) return @@ -54,6 +83,11 @@ abstract class Sim : Results { dependencyStack.pop() } + /** + * Scans a [voxelShape] on the given [sides] at the [pos] from the [pov]. + * + * @return A set of [CheckedHit] + */ suspend fun ISimInfo.scanShape( pov: Vec3d, voxelShape: VoxelShape, @@ -91,7 +125,7 @@ abstract class Sim : Results { } if (hit.blockPos != pos || hit.side != side) return@scanSurfaces - val checked = CheckedHit(hit, newRotation, buildConfig.interactReach) + val checked = CheckedHit(hit, newRotation) validHits.add(checked) } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BasicChecker.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BasicChecker.kt index ec61a518c..55a7662a6 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BasicChecker.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BasicChecker.kt @@ -29,6 +29,9 @@ import com.lambda.util.world.WorldUtils.isLoaded import net.minecraft.block.OperatorBlock object BasicChecker : Results { + /** + * A sequence of basic checks to make sure that the block is worth simulating. + */ @SimDsl context(automatedSafeContext: AutomatedSafeContext) fun SimInfo.hasBasicRequirements(): Boolean = with(automatedSafeContext) { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt index 9df0d56dd..60dc03273 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt @@ -73,6 +73,9 @@ class BreakSim private constructor(simInfo: ISimInfo) BreakResult.Dependency(pos, buildResult) companion object { + /** + * Public [SimDsl] API. + */ @SimDsl context(automatedSafeContext: AutomatedSafeContext, dependent: Sim<*>) suspend fun SimInfo.simBreak() = diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt index d916a3b6e..f0a0fbb69 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt @@ -76,6 +76,9 @@ class PlaceSim private constructor(simInfo: ISimInfo) PlaceResult.Dependency(pos, buildResult) companion object { + /** + * Public [SimDsl] API. + */ context(automatedSafeContext: AutomatedSafeContext, dependent: Sim<*>) @SimDsl suspend fun SimInfo.simPlacement() = diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt index 38ee34933..282d7c432 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt @@ -52,6 +52,9 @@ class PostProcessingSim private constructor(simInfo: ISimInfo) InteractResult.Dependency(pos, buildResult) companion object { + /** + * Public [SimDsl] API. + */ context(automatedSafeContext: AutomatedSafeContext, dependent: Sim<*>) @SimDsl suspend fun SimInfo.simPostProcessing() = diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt index 684f4650a..000af3d3f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/VisibilityChecker.kt @@ -67,7 +67,7 @@ object VisibilityChecker { if (boxes.any { it.contains(eye) }) { currentRotation.rayCast(reach, eye)?.let { hit -> - return CheckedHit(hit, currentRotation, reach) + return CheckedHit(hit, currentRotation) } } @@ -111,7 +111,7 @@ object VisibilityChecker { val mask = if (buildConfig.strictRayCast) InteractionMask.Both else targetType val hit = newRotation.rayCast(reach, eye, mask = mask) ?: return@scanSurfaces - val checked = CheckedHit(hit, newRotation, reach) + val checked = CheckedHit(hit, newRotation) if (!checked.verify()) return@scanSurfaces add(checked) @@ -142,7 +142,7 @@ object VisibilityChecker { val mask = if (buildConfig.strictRayCast || entity == null) InteractionMask.Both else targetType val hit = newRotation.rayCast(reach, eye, mask = mask) ?: return@scanSurfaces - val checked = CheckedHit(hit, newRotation, reach) + val checked = CheckedHit(hit, newRotation) if (!checked.verify()) return@scanSurfaces add(checked) @@ -151,11 +151,11 @@ object VisibilityChecker { } /** - * Scans the surfaces of a given box, optionally excluding specific sides, + * Scans the surfaces of a given box on the [sides] specified * and executes a callback for each point calculated based on the scanning parameters. * * @param box The 3D box whose surfaces will be scanned. - * @param excludedSides A set of directions representing the sides of the box to exclude from the scan (default is an empty set). + * @param sides A set of sides to scan * @param resolution The number of intervals into which each dimension is divided for scanning (default is 5). * @param scan Configuration specifying the axis and mode of the scan (default is `SurfaceScan.DEFAULT`). * @param check A callback function that performs an action for each surface point, receiving the direction of the surface and the current 3D vector. @@ -291,7 +291,6 @@ object VisibilityChecker { class CheckedHit( val hit: HitResult, - val targetRotation: Rotation, - val reach: Double + val targetRotation: Rotation ) } From 675bd7754249a81dd35474959d0f13df711203d8 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Nov 2025 17:34:26 +0000 Subject: [PATCH 6/8] improvements --- .../interaction/request/breaking/BreakManager.kt | 12 +++++++++--- .../interaction/request/breaking/BreakRequest.kt | 4 +++- .../request/breaking/BrokenBlockHandler.kt | 4 +++- .../interaction/request/hotbar/HotbarManager.kt | 4 +++- .../request/inventory/InventoryManager.kt | 4 +++- .../interaction/request/placing/PlaceManager.kt | 16 ++++++++++++---- 6 files changed, 33 insertions(+), 11 deletions(-) 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 f5f2f3bbf..23e59cb33 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -508,13 +508,15 @@ object BreakManager : RequestHandler( * * If a primary [BreakInfo] is active, as long as the tick stage is valid, it is transformed * into a secondary break, so a new primary can be initialized. This means sending a - * [net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action] with action: [net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK] + * PlayerActionC2SPacket with action: STOP_DESTROY_BLOCK * packet to the server to start the automated breaking server side. * * If there is no way to keep both breaks, and the primary break hasn't been updated yet, * the primary break is canceled. Otherwise, the break cannot be started. * * @return the [BreakInfo], or null, if the break context wasn't accepted. + * + * @see net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket */ private fun AutomatedSafeContext.initNewBreak( requestCtx: BreakContext, @@ -691,9 +693,11 @@ object BreakManager : RequestHandler( } /** - * A modified version of the vanilla [net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress] method. + * A modified version of the vanilla updateBlockBreakingProgress method. * * @return if the update was successful. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.updateBlockBreakingProgress */ private fun SafeContext.updateBreakProgress(info: BreakInfo): Unit = info.request.runSafeAutomated { val ctx = info.context @@ -783,9 +787,11 @@ object BreakManager : RequestHandler( } /** - * A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock] method. + * A modified version of the minecraft attackBlock method. * * @return if the block started breaking successfully. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.attackBlock */ private fun AutomatedSafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 09eb4d05d..c86c5d254 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -39,8 +39,10 @@ import net.minecraft.util.math.BlockPos * A private constructor is used to force use of the cleaner [BreakRequestDsl] builder. This is * accessed through the [breakRequest] method. * - * @param contexts A collection of [BreakContext]'s gathered from the [com.lambda.interaction.construction.simulation.BuildSimulator]. + * @param contexts A collection of [BreakContext]'s gathered from the BuildSimulator. * @param pendingInteractions A mutable, concurrent list to store the pending actions. + * + * @see com.lambda.interaction.construction.simulation.BuildSimulator */ data class BreakRequest private constructor( val contexts: Collection, diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 1083fbe62..65b14b745 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -139,10 +139,12 @@ object BrokenBlockHandler : PostActionHandler() { } /** - * A modified version of the minecraft [net.minecraft.client.world.ClientWorld.breakBlock] method. + * A modified version of the minecraft breakBlock method. * * Performs the actions required to display breaking particles, sounds, texture overlay, etc. * based on the user's settings. + * + * @see net.minecraft.client.world.ClientWorld.breakBlock */ fun SafeContext.destroyBlock(info: BreakInfo) { val ctx = info.context diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index 3e79485e3..d601b85ce 100644 --- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -124,8 +124,10 @@ object HotbarManager : RequestHandler( } /** - * Sets the [activeRequest]. This also calls [net.minecraft.client.network.ClientPlayerInteractionManager.syncSelectedSlot] to + * Sets the [activeRequest]. This also calls syncSelectedSlot to * update the server now to keep predictability. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.syncSelectedSlot */ private fun AutomatedSafeContext.setActiveRequest(request: HotbarRequest) { maxSwapsThisTick = hotbarConfig.swapsPerTick diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt index 2c4310e50..ce62479a9 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -157,7 +157,9 @@ object InventoryManager : RequestHandler( private fun getStacks(slots: Collection) = slots.map { it.stack.copy() } /** - * A modified version of the [net.minecraft.client.network.ClientPlayNetworkHandler.onInventory] method + * A modified version of the minecraft onInventory method + * + * @see net.minecraft.client.network.ClientPlayNetworkHandler.onInventory */ @JvmStatic fun onInventoryUpdate(packet: InventoryS2CPacket, original: Operation){ diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 8e03ee3a7..685b96f4e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -207,8 +207,10 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.interactBlock] method, + * A modified version of the minecraft interactBlock method, * renamed to better suit its usage. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.interactBlock */ private fun AutomatedSafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() @@ -225,7 +227,9 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft [net.minecraft.client.network.ClientPlayerInteractionManager.interactBlockInternal] method. + * A modified version of the minecraft interactBlockInternal method. + * + * @see net.minecraft.client.network.ClientPlayerInteractionManager.interactBlockInternal */ private fun AutomatedSafeContext.interactBlockInternal( placeContext: PlaceContext, @@ -266,7 +270,9 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft [net.minecraft.item.Item.useOnBlock] method. + * A modified version of the minecraft useOnBlock method. + * + * @see net.minecraft.item.Item.useOnBlock */ private fun AutomatedSafeContext.useOnBlock( placeContext: PlaceContext, @@ -294,7 +300,9 @@ object PlaceManager : RequestHandler( } /** - * A modified version of the minecraft [net.minecraft.item.BlockItem.place] method. + * A modified version of the minecraft place method. + * + * @see net.minecraft.item.BlockItem.place */ private fun AutomatedSafeContext.place( placeContext: PlaceContext, From ee95c8bf8bd0b0ffe219442cc7a4cc88170a3d47 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Nov 2025 17:48:42 +0000 Subject: [PATCH 7/8] remove empty space --- .../kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt | 2 -- 1 file changed, 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 c313245cf..52b3df455 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -48,8 +48,6 @@ data class SwapInfo( /** * Calculates the contents and returns a [SwapInfo]. - * - * */ context(_: SafeContext) fun BreakInfo.getSwapInfo() = request.runSafeAutomated { From 28122664fb0e4717ae3b99f2096025e866b536c4 Mon Sep 17 00:00:00 2001 From: beanbag44 Date: Wed, 12 Nov 2025 18:07:40 +0000 Subject: [PATCH 8/8] cleanup --- .../interaction/construction/simulation/BuildSimulator.kt | 5 ----- .../com/lambda/interaction/construction/simulation/Sim.kt | 2 -- .../interaction/construction/simulation/checks/BreakSim.kt | 3 --- .../interaction/construction/simulation/checks/PlaceSim.kt | 3 --- .../construction/simulation/checks/PostProcessingSim.kt | 3 --- .../lambda/interaction/request/inventory/InventoryManager.kt | 4 ---- .../com/lambda/interaction/request/placing/PlaceManager.kt | 3 --- .../lambda/interaction/request/placing/PlacedBlockHandler.kt | 3 --- 8 files changed, 26 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 e088d651a..1f0565f6a 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -34,17 +34,12 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.supervisorScope import net.minecraft.util.math.Vec3d -/** - * The public API for simulating a blueprint. - */ object BuildSimulator : Sim() { /** * Iterates over the blueprint and performs the best suited simulation. Each simulation adds [BuildResult]s to * the provided concurrent set. This method uses coroutines to perform the simulations in parallel. The results * will likely not be returned in the same order they were simulated due to the parallel nature of the simulations. * - * @return A set of [BuildResult]s - * * @see ISimInfo.sim * @see simPostProcessing * @see simPlacement diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt index b65ffcbd6..8436871b0 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt @@ -85,8 +85,6 @@ abstract class Sim : Results { /** * Scans a [voxelShape] on the given [sides] at the [pos] from the [pov]. - * - * @return A set of [CheckedHit] */ suspend fun ISimInfo.scanShape( pov: Vec3d, diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt index 60dc03273..9df0d56dd 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt @@ -73,9 +73,6 @@ class BreakSim private constructor(simInfo: ISimInfo) BreakResult.Dependency(pos, buildResult) companion object { - /** - * Public [SimDsl] API. - */ @SimDsl context(automatedSafeContext: AutomatedSafeContext, dependent: Sim<*>) suspend fun SimInfo.simBreak() = diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt index f0a0fbb69..d916a3b6e 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceSim.kt @@ -76,9 +76,6 @@ class PlaceSim private constructor(simInfo: ISimInfo) PlaceResult.Dependency(pos, buildResult) companion object { - /** - * Public [SimDsl] API. - */ context(automatedSafeContext: AutomatedSafeContext, dependent: Sim<*>) @SimDsl suspend fun SimInfo.simPlacement() = diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt index 282d7c432..38ee34933 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingSim.kt @@ -52,9 +52,6 @@ class PostProcessingSim private constructor(simInfo: ISimInfo) InteractResult.Dependency(pos, buildResult) companion object { - /** - * Public [SimDsl] API. - */ context(automatedSafeContext: AutomatedSafeContext, dependent: Sim<*>) @SimDsl suspend fun SimInfo.simPostProcessing() = diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt index ce62479a9..c5c1ceff5 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -27,7 +27,6 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.Logger import com.lambda.interaction.request.RequestHandler -import com.lambda.interaction.request.inventory.InventoryManager.actions import com.lambda.interaction.request.inventory.InventoryManager.alteredSlots import com.lambda.interaction.request.placing.PlaceManager import com.lambda.module.hud.ManagerDebugLoggers.inventoryManagerLogger @@ -128,9 +127,6 @@ object InventoryManager : RequestHandler( if (actionsThisTick > 0) activeThisTick = true } - /** - * Populates the [actions] collection and sets some configurations. - */ private fun populateFrom(request: InventoryRequest) { PlaceManager.logger.debug("Populating from request", request) actions = request.actions.toMutableList() diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 685b96f4e..c5fde073c 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -69,9 +69,6 @@ import net.minecraft.util.math.BlockPos import net.minecraft.world.GameMode import kotlin.math.min -/** - * Manager designed to place blocks. - */ object PlaceManager : RequestHandler( 0, TickEvent.Pre, diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt index e3fc0c82f..8e3a7ca1d 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlacedBlockHandler.kt @@ -29,9 +29,6 @@ import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.collections.LimitedDecayQueue -/** - * Designed to handle placements pending a response from the server. - */ object PlacedBlockHandler : PostActionHandler() { override val pendingActions = LimitedDecayQueue( AutomationConfig.buildConfig.maxPendingInteractions,