Skip to content

fix: hotbar tools, shulker boxes and drops - #353

Open
blocksimus wants to merge 9 commits into
lambda-client:1.21.11from
blocksimus:stack-fixes
Open

blocksimus wants to merge 9 commits into
lambda-client:1.21.11from
blocksimus:stack-fixes

Conversation

@blocksimus

Copy link
Copy Markdown
Contributor

Description

A few things I ran into after the rewrite... feel free to pick them apart.

Hotbar swapping behavior - hotbar gave up the smallest stack first so tools were getting moved out. The default sort by count meant any sort added after it only broke ties. Not as happy with the code here, so if you have any better thoughts...

A shulker box in the hotbar never opened (it tried to move it from the hotbar to the hotbar), nothing could be taken out of an open one so builds kept placing and breaking it, and a box couldn't go back while an identical one was in the hotbar. Opening uses a withContents filter now instead of sortedByBestContentMatch, because acquireStack grabs any matching box already in the hotbar, so a same-looking box with different contents would get opened instead.

Collecting drops only set Baritone's goal when the drop moved, so if another task stopped Baritone on the way it just sat there. Now it sets it again after Baritone's been idle for a second.

Selections kept their default sort by count when mutated, so any sort
added after it only broke ties. A full hotbar then gave up its smallest
stack first, which was usually a lone sword or tool.
A box already in the hotbar never opened, nothing could be taken out
of an open box so builds kept placing and breaking it, and a box
couldn't go back while an identical one was in the hotbar.
Collecting drops only set Baritone's goal when the drop moved, so if
another task stopped Baritone on the way it just waited forever. Now
it sets it again after Baritone has been idle for a second.
@github-project-automation github-project-automation Bot moved this to Backlog in Kanban Oct 22, 2026
@beanbag44
beanbag44 self-requested a review October 22, 2026 09:56
.asSequence()
.filter { it.stack.item == shulkerItem && it.stack.name.string == itemName }
.firstNotNullOfOrNull { it.slot }
if (pickedUp != null && containedIn.slots.any { it.identity() == pickedUp }) return@thenOrNull null

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, maybe we could adjust the container transfer task to allow swapping within the same container, just checking that the slots arent the same? I dont see how that could break any functionality. Then to avoid a needless transfer we could check to see if the original slot the shulker came from is empty and the shulker is already in the correct container before doing the transfer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I shortcut this by not bothering if it was just going back into your inventory/hotbar (not external), unless you think it's important to try get it back where it was previously?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible at least. If the slot isnt empty anymore then dont bother, but i think for the sake of inventory cleanliness its best to try

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, the only skip is the box already being back in its container with its own slot filled.

Comment thread src/main/kotlin/com/lambda/interaction/container/selection/StackSelection.kt Outdated
@blocksimus

Copy link
Copy Markdown
Contributor Author

Thanks for the review! I'll try get another iteration done soon.

Picks boxes by slot, hands back the placed box instead of
keeping it on the container, and only puts boxes back into
chests.
The Pre stage swapped a kept slot back before it was asked again,
so a swap pause never ran out.
private fun SafeContext.checkResetSwap() {
activeRequest?.let { active ->
if (active.keepTicks <= 0) {
if (active.keepTicks == 0 && active.activeRequestAge > 0 && tickStage != requestStage) return

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes an issue I hit with my Nuker's Server Swap at 2. The slot got swapped back at Pre (now in the default mask) before the break asked again, so Nuker stalled.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah i wondered if changing the default would cause issues. Wouldnt we only need the tickStage != requestStage check though? keepTicks is checked to be <= 0 just before, and the active.activeRequestAge > 0 would never prevent the return for keepTicks 0 requests as they start and end on the same event

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, the activeRequestAge > 0 was unnecessary. I think == 0 is still helpful... if a request becomes overdue, it can swap back at any stage, not just its own.

@blocksimus
blocksimus requested a review from beanbag44 October 23, 2026 09:03

@beanbag44 beanbag44 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lemme know if this is too much, i can always help out; i think the pr's have maintainer write perms enabled by default

}.then { pos ->
openContainer(pos).onSuccess {
success(OpenedShulkerBoxContext(pos))
val placed = PlacedShulkerBoxContainer(pos, Block.getBlockFromItem(shulkerItem), null, stacks)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm containers should only really be created in ContainerHandler and ContainerSerializer. Seeing as openContainer accesses the placed shulker, you can probably receive it from ContainerHandler.accessedPlacedContainer. Or actually its likely best just to change the return type of openContainer to T : PlacedContainer with the class taking a type parameter

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See what you think...

private fun SafeContext.checkResetSwap() {
activeRequest?.let { active ->
if (active.keepTicks <= 0) {
if (active.keepTicks == 0 && active.activeRequestAge > 0 && tickStage != requestStage) return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah i wondered if changing the default would cause issues. Wouldnt we only need the tickStage != requestStage check though? keepTicks is checked to be <= 0 just before, and the active.activeRequestAge > 0 would never prevent the return for keepTicks 0 requests as they start and end on the same event

Comment thread src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt
Comment thread src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt Outdated
Comment thread src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt Outdated
.toStructure(TargetState.Stack(slot.stack))
.toBlueprint()
.build(finishOnDone = true, collectDrops = false)
.onPreTick { slot in HotbarContainer.slots && !this.hotbarRequest(slot.index).submit().done }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm something about this feels off lol but i think the whole TargetState style system needs improving, so that can wait for a later date

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does, and I was getting into a mess working through some edge cases... have a look at the change in InteractSim instead.

@blocksimus

Copy link
Copy Markdown
Contributor Author

Lemme know if this is too much, i can always help out; i think the pr's have maintainer write perms enabled by default

No stress, it's been a good exercise in getting to know the codebase, although feel free to make changes as you'd like.

@blocksimus
blocksimus requested a review from beanbag44 October 24, 2026 04:13
A closed box goes back into its own slot when it's free, or stays
where it was picked up if it can't go back.
}

listen<InventoryEvent.FullUpdate> { onContainerUpdate() }
listen<InventoryEvent.FullUpdate>(priority = { Int.MAX_VALUE }) { onContainerUpdate() }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to set a higher priority so ContainerHandler indexes the opened container before OpenContainerTask reads it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants