From 54cac3f87cd3b3e3e5124fe762e201d6224fbedd Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:26:39 +0000 Subject: [PATCH 1/4] initial rework --- .../command/commands/TransferCommand.kt | 60 +++------ .../lambda/config/groups/InventorySettings.kt | 9 +- .../simulation/checks/BreakSim.kt | 7 +- .../simulation/checks/InteractSim.kt | 4 +- .../simulation/result/Resolvable.kt | 7 +- .../simulation/result/results/BreakResult.kt | 18 +-- .../result/results/GenericResult.kt | 15 +-- .../construction/verify/TargetState.kt | 4 +- .../managers/inventory/InventoryConfig.kt | 18 +-- .../material/ContainerSelection.kt | 8 +- .../material/container/ContainerManager.kt | 40 ++++-- .../ExternalContainer.kt} | 28 ++--- .../material/container/MaterialContainer.kt | 115 +++++++++--------- .../container/containers/ChestContainer.kt | 49 +++----- .../container/containers/CreativeContainer.kt | 86 +++---------- .../containers/EnderChestContainer.kt | 66 ++++------ .../container/containers/HotbarContainer.kt | 36 ++---- .../containers/InventoryContainer.kt | 12 +- .../container/containers/MainHandContainer.kt | 66 ++-------- .../container/containers/OffHandContainer.kt | 20 +-- .../containers/ShulkerBoxContainer.kt | 78 +++--------- .../container/containers/StashContainer.kt | 8 +- .../material/transfer/InventoryChanges.kt | 99 --------------- .../material/transfer/SlotTransfer.kt | 101 --------------- .../material/transfer/TransferResult.kt | 75 ------------ .../module/modules/combat/CrystalAura.kt | 16 ++- .../module/modules/debug/ContainerTest.kt | 2 +- .../module/modules/player/InventoryTweaks.kt | 12 +- .../com/lambda/module/modules/player/Nuker.kt | 6 +- .../lambda/module/modules/player/Printer.kt | 1 - .../lambda/module/modules/player/Scaffold.kt | 1 - .../module/modules/player/StackReplenish.kt | 1 - .../lambda/module/modules/player/ToolSaver.kt | 6 +- ...uireMaterial.kt => AcquireMaterialTask.kt} | 20 +-- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 16 +-- .../task/tasks/ContainerTransferTask.kt | 79 ++++++++++++ .../kotlin/com/lambda/task/tasks/EatTask.kt | 5 +- ...{OpenContainer.kt => OpenContainerTask.kt} | 2 +- ...laceContainer.kt => PlaceContainerTask.kt} | 9 +- .../com/lambda/util/extension/Screen.kt | 6 +- .../com/lambda/util/item/ItemStackUtils.kt | 4 + .../kotlin/com/lambda/util/item/ItemUtils.kt | 29 +++-- .../com/lambda/util/player/SlotUtils.kt | 25 ++-- 43 files changed, 434 insertions(+), 835 deletions(-) rename src/main/kotlin/com/lambda/interaction/material/{ContainerTask.kt => container/ExternalContainer.kt} (57%) delete mode 100644 src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt delete mode 100644 src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt delete mode 100644 src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt rename src/main/kotlin/com/lambda/task/tasks/{AcquireMaterial.kt => AcquireMaterialTask.kt} (69%) create mode 100644 src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt rename src/main/kotlin/com/lambda/task/tasks/{OpenContainer.kt => OpenContainerTask.kt} (98%) rename src/main/kotlin/com/lambda/task/tasks/{PlaceContainer.kt => PlaceContainerTask.kt} (94%) diff --git a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index 6172879df..cc61edea1 100644 --- a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -32,9 +32,9 @@ import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.findContainersWithMaterial import com.lambda.interaction.material.container.ContainerManager.findContainersWithSpace -import com.lambda.interaction.material.transfer.TransferResult -import com.lambda.task.RootTask.run -import com.lambda.threading.runSafe +import com.lambda.task.RootTask +import com.lambda.task.Task +import com.lambda.threading.runSafeAutomated import com.lambda.util.Communication.info import com.lambda.util.extension.CommandBuilder @@ -43,7 +43,7 @@ object TransferCommand : LambdaCommand( usage = "transfer ", description = "Transfer items from anywhere to anywhere", ) { - private var lastContainerTransfer: TransferResult.ContainerTransfer? = null + private var lastContainerTransfer: Task<*>? = null override fun CommandBuilder.create() { required(itemStack("stack", registry)) { stack -> @@ -54,12 +54,10 @@ object TransferCommand : LambdaCommand( val selection = selectStack(count) { isItem(stack(ctx).value().item) } - with(AutomationConfig.Companion.DEFAULT) { - runSafe { - selection.findContainersWithMaterial().forEachIndexed { i, container -> - builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) - } - } + AutomationConfig.Companion.DEFAULT.runSafeAutomated { + selection.findContainersWithMaterial().forEachIndexed { i, container -> + builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) + } } builder.buildFuture() } @@ -68,11 +66,9 @@ object TransferCommand : LambdaCommand( val selection = selectStack(amount(ctx).value()) { isItem(stack(ctx).value().item) } - with(AutomationConfig.Companion.DEFAULT) { - runSafe { - findContainersWithSpace(selection).forEachIndexed { i, container -> - builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) - } + AutomationConfig.Companion.DEFAULT.runSafeAutomated { + findContainersWithSpace(selection).forEachIndexed { i, container -> + builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) } } builder.buildFuture() @@ -81,35 +77,17 @@ object TransferCommand : LambdaCommand( val selection = selectStack(amount().value()) { isItem(stack().value().item) } - val fromContainer = ContainerManager.containers().find { - it.name == from().value().split(".").last().trim() - } ?: return@executeWithResult failure("From container not found") + AutomationConfig.Companion.DEFAULT.runSafeAutomated { + val fromContainer = ContainerManager.containers().find { + it.name == from().value().split(".").last().trim() + } ?: return@executeWithResult failure("From container not found") - val toContainer = ContainerManager.containers().find { - it.name == to().value().split(".").last().trim() - } ?: return@executeWithResult failure("To container not found") + val toContainer = ContainerManager.containers().find { + it.name == to().value().split(".").last().trim() + } ?: return@executeWithResult failure("To container not found") - with(AutomationConfig.Companion.DEFAULT) { - when (val transaction = fromContainer.transfer(selection, toContainer)) { - is TransferResult.ContainerTransfer -> { - info("${transaction.name} started.") - lastContainerTransfer = transaction - transaction.finally { - info("${transaction.name} completed.") - }.run() - return@executeWithResult success() - } - - is TransferResult.MissingItems -> { - return@executeWithResult failure("Missing items: ${transaction.missing}") - } - - is TransferResult.NoSpace -> { - return@executeWithResult failure("No space in ${toContainer.name}") - } - } + fromContainer.transferByTask(selection, toContainer).execute(RootTask) } - return@executeWithResult success() } } diff --git a/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 33c56aec5..2dc25e38a 100644 --- a/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -41,9 +41,8 @@ class InventorySettings( override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from").group(baseGroup, Group.Container).index() override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to").group(baseGroup, Group.Container).index() - override val immediateAccessOnly by c.setting("Immediate Access Only", false, "Only allow access to inventories that can be accessed immediately").group(baseGroup, Group.Access).index() - override val accessShulkerBoxes by c.setting("Access Shulker Boxes", true, "Allow access to the player's shulker boxes") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() - override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() - override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() - override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() + override val accessShulkerBoxes by c.setting("Access Shulker Boxes", false, "Allow access to the player's shulker boxes").group(baseGroup, Group.Access).index() + override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests").group(baseGroup, Group.Access).index() + override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest").group(baseGroup, Group.Access).index() + override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes").group(baseGroup, Group.Access).index() } \ No newline at end of file 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 5d48ef331..322ef7b19 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 @@ -185,10 +185,9 @@ class BreakSim private constructor(simInfo: SimInfo) } val hotbarCandidates = stackSelection - .findContainersWithMaterial(silentSwapSelection) - .map { it.matchingStacks(stackSelection) } - .flatten() - if (hotbarCandidates.isEmpty()) { + .findContainersWithMaterial(silentSwapSelection) + .flatMap { it.matchingStacks(stackSelection) } + if (hotbarCandidates.isEmpty()) { result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack)) return null } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt index e67862fd5..234a80c45 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt @@ -47,6 +47,7 @@ import com.lambda.util.item.ItemUtils.blockItem import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.minus import com.lambda.util.player.MovementUtils.sneaking +import com.lambda.util.player.SlotUtils.hotbarStacks import com.lambda.util.player.copyPlayer import com.lambda.util.world.raycast.RayCastUtils.blockResult import kotlinx.coroutines.CoroutineScope @@ -223,8 +224,9 @@ class InteractSim private constructor(simInfo: InteractSimInfo) result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack)) return null } + val hotbarStacks = player.hotbarStacks return stackSelection.filterStacks(container.stacks).run { - firstOrNull { it.inventoryIndex == player.inventory.selectedSlot } + firstOrNull { hotbarStacks.indexOf(it) == player.inventory.selectedSlot } ?: firstOrNull() } } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt index ba8f70371..afbff5de1 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt @@ -17,14 +17,13 @@ package com.lambda.interaction.construction.simulation.result -import com.lambda.context.Automated -import com.lambda.context.SafeContext +import com.lambda.context.AutomatedSafeContext import com.lambda.task.Task /** * Represents a [BuildResult] with a resolvable [Task] */ interface Resolvable { - context(automated: Automated, safeContext: SafeContext) - fun resolve(): Task<*>? + context(task: Task<*>, _: AutomatedSafeContext) + fun resolve() } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt index 4356dc53d..30741633e 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt @@ -19,11 +19,9 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.esp.DirectionMask.mask -import com.lambda.graphics.esp.ShapeScope +import com.lambda.context.AutomatedSafeContext import com.lambda.graphics.mc.TransientRegionESP +import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.interaction.construction.simulation.context.BreakContext import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult @@ -34,8 +32,9 @@ import com.lambda.interaction.construction.simulation.result.Navigable import com.lambda.interaction.construction.simulation.result.Rank import com.lambda.interaction.construction.simulation.result.Resolvable import com.lambda.interaction.material.StackSelection.Companion.selectStack -import com.lambda.interaction.material.container.ContainerManager.transfer -import com.lambda.interaction.material.container.containers.MainHandContainer +import com.lambda.interaction.material.container.ContainerManager.transferByTask +import com.lambda.interaction.material.container.containers.HotbarContainer +import com.lambda.task.Task import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos @@ -98,11 +97,12 @@ sealed class BreakResult : BuildResult() { ) : Resolvable, BreakResult() { override val rank = Rank.BreakItemCantMine - context(automated: Automated, safeContext: SafeContext) - override fun resolve() = + context(task: Task<*>, _: AutomatedSafeContext) + override fun resolve() { selectStack { isItem(badItem).not() - }.transfer(MainHandContainer) + }.transferByTask(HotbarContainer) + } override fun compareResult(other: ComparableResult) = when (other) { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt index dc8716f3f..0694c3c6d 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt @@ -18,9 +18,7 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalNear -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.graphics.esp.ShapeScope +import com.lambda.context.AutomatedSafeContext import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult @@ -29,8 +27,9 @@ import com.lambda.interaction.construction.simulation.result.Navigable import com.lambda.interaction.construction.simulation.result.Rank import com.lambda.interaction.construction.simulation.result.Resolvable import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.container.ContainerManager.transfer -import com.lambda.interaction.material.container.containers.MainHandContainer +import com.lambda.interaction.material.container.ContainerManager.transferByTask +import com.lambda.interaction.material.container.containers.HotbarContainer +import com.lambda.task.Task import net.minecraft.client.data.TextureMap.side import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos @@ -98,8 +97,10 @@ sealed class GenericResult : BuildResult() { override val rank = Rank.WrongItem private val color = Color(3, 252, 169, 25) - context(automated: Automated, safeContext: SafeContext) - override fun resolve() = neededSelection.transfer(MainHandContainer) + context(task: Task<*>, _: AutomatedSafeContext) + override fun resolve() { + neededSelection.transferByTask(HotbarContainer)?.execute(task) + } override fun render(esp: TransientRegionESP) { esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { diff --git a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index aa7eaacaf..19c5d0e4e 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -87,7 +87,7 @@ sealed class TargetState() : StateMatcher { override fun getStack(pos: BlockPos) = with(automatedSafeContext) { findDisposable()?.stacks?.firstOrNull { - it.item.block in inventoryConfig.disposables && it.item.block !in replace + it.item in inventoryConfig.disposables && it.item.block !in replace } ?: ItemStack(Items.NETHERRACK) } @@ -114,7 +114,7 @@ sealed class TargetState() : StateMatcher { override fun getStack(pos: BlockPos) = with(automatedSafeContext) { findDisposable()?.stacks?.firstOrNull { - it.item.block in inventoryConfig.disposables + it.item in inventoryConfig.disposables } ?: ItemStack(Items.NETHERRACK) } diff --git a/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt b/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt index a21fd7842..41a879922 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt @@ -18,36 +18,36 @@ package com.lambda.interaction.managers.inventory import com.lambda.config.ISettingGroup +import com.lambda.context.SafeContext 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.util.Describable import com.lambda.util.NamedEnum -import net.minecraft.block.Block +import net.minecraft.item.Item interface InventoryConfig : ISettingGroup { val actionsPerSecond: Int val tickStageMask: Collection - val disposables: Collection + val disposables: Collection val swapWithDisposables: Boolean val providerPriority: Priority val storePriority: Priority - val immediateAccessOnly: Boolean val accessShulkerBoxes: Boolean - val accessEnderChest: Boolean val accessChests: Boolean + val accessEnderChest: Boolean val accessStashes: Boolean val containerSelection: ContainerSelection get() = ContainerSelection.selectContainer { val allowedContainers = buildSet { addAll(MaterialContainer.Rank.entries) - if (!accessShulkerBoxes || immediateAccessOnly) remove(MaterialContainer.Rank.ShulkerBox) - if (!accessEnderChest || immediateAccessOnly) remove(MaterialContainer.Rank.EnderChest) - if (!accessChests || immediateAccessOnly) remove(MaterialContainer.Rank.Chest) - if (!accessStashes || immediateAccessOnly) remove(MaterialContainer.Rank.Stash) + if (!accessShulkerBoxes) remove(MaterialContainer.Rank.ShulkerBox) + if (!accessEnderChest) remove(MaterialContainer.Rank.EnderChest) + if (!accessChests) remove(MaterialContainer.Rank.Chest) + if (!accessStashes) remove(MaterialContainer.Rank.Stash) } ofAnyType(*allowedContainers.toTypedArray()) } @@ -59,6 +59,7 @@ interface InventoryConfig : ISettingGroup { WithMinItems("With Min Items", "Pick containers with the fewest matching items (or least space) first; useful for topping off or clearing leftovers."), WithMaxItems("With Max Items", "Pick containers with the most matching items (or most space) first; ideal for bulk moves with fewer transfers."); + context(_: SafeContext) fun materialComparator(selection: StackSelection) = when (this) { WithMaxItems -> compareBy { it.rank } @@ -70,6 +71,7 @@ interface InventoryConfig : ISettingGroup { .thenBy { it.name } } + context(_: SafeContext) fun spaceComparator(selection: StackSelection) = when (this) { WithMaxItems -> compareBy { it.rank } diff --git a/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt b/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt index 706e67018..88f8a2483 100644 --- a/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt +++ b/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt @@ -38,8 +38,9 @@ class ContainerSelection { * which matches the given StackSelection. */ @ContainerSelectionDsl + context(_: SafeContext) fun matches(stackSelection: StackSelection): (MaterialContainer) -> Boolean = - { container -> container.matchingStacks(stackSelection).isNotEmpty() } + { container -> container.matchingSlots(stackSelection).isNotEmpty() } /** * Returns a function that checks whether a given MaterialContainer matches the criteria @@ -63,11 +64,6 @@ class ContainerSelection { fun noneOfType(vararg types: MaterialContainer.Rank): (MaterialContainer) -> Boolean = { container -> !types.contains(container.rank) } - @ContainerSelectionDsl - context(_: SafeContext) - fun immediateOnly(): (MaterialContainer) -> Boolean = - { container -> !container.isImmediatelyAccessible() } - /** * Returns a function that combines two container predicates using logical AND. */ diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 61bb65f12..c61e016f9 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.material.container import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.events.InventoryEvent @@ -45,7 +46,7 @@ object ContainerManager : Loadable { private val compileContainers = getInstances() private val runtimeContainers = mutableSetOf() - private var lastInteractedBlockEntity: BlockEntity? = null + var lastInteractedBlockEntity: BlockEntity? = null override fun load() = "Loaded ${compileContainers.size} containers" @@ -82,22 +83,37 @@ object ContainerManager : Loadable { } } + context(_: SafeContext) fun containers() = containers.flatMap { setOf(it) + it.shulkerContainer }.sorted() - context(automated: Automated, _: SafeContext) + context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.transfer(destination: MaterialContainer) = - findContainerWithMaterial()?.transfer(this, destination) + with(automatedSafeContext) { + findContainerWithMaterial( + inventoryConfig.containerSelection + )?.transfer(this@transfer, destination) + ?: false + } + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.transferByTask(destination: MaterialContainer) = + with(automatedSafeContext) { + findContainerWithMaterial( + inventoryConfig.containerSelection + )?.transferByTask(this@transferByTask, destination) + } + context(_: SafeContext) fun findContainer( block: (MaterialContainer) -> Boolean, ): MaterialContainer? = containers().find(block) - context(automated: Automated, _: SafeContext) + context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.findContainerWithMaterial( - containerSelection: ContainerSelection = automated.inventoryConfig.containerSelection + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection ): MaterialContainer? = findContainersWithMaterial(containerSelection).firstOrNull() - context(_: Automated, _: SafeContext) + context(_: AutomatedSafeContext) fun findContainerWithSpace(selection: StackSelection): MaterialContainer? = findContainersWithSpace(selection).firstOrNull() @@ -106,24 +122,22 @@ object ContainerManager : Loadable { containerSelection: ContainerSelection = automated.inventoryConfig.containerSelection, ): List = containers() - .filter { !automated.inventoryConfig.immediateAccessOnly || it.isImmediatelyAccessible() } .filter { it.materialAvailable(this) >= count } .filter { containerSelection.matches(it) } .sortedWith(automated.inventoryConfig.providerPriority.materialComparator(this)) - context(automated: Automated, safeContext: SafeContext) + context(automatedSafeContext: AutomatedSafeContext) fun findContainersWithSpace( selection: StackSelection, ): List = containers() - .filter { !automated.inventoryConfig.immediateAccessOnly || it.isImmediatelyAccessible() } .filter { it.spaceAvailable(selection) >= selection.count } - .filter { automated.inventoryConfig.containerSelection.matches(it) } - .sortedWith(automated.inventoryConfig.providerPriority.spaceComparator(selection)) + .filter { automatedSafeContext.inventoryConfig.containerSelection.matches(it) } + .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.spaceComparator(selection)) - context(automated: Automated) + context(automatedSafeContext: AutomatedSafeContext) fun findDisposable() = containers().find { container -> - automated.inventoryConfig.disposables.any { container.materialAvailable(it.asItem().select()) > 0 } + automatedSafeContext.inventoryConfig.disposables.any { container.materialAvailable(it.asItem().select()) > 0 } } class NoContainerFound(selection: StackSelection) : Exception("No container found matching $selection") diff --git a/src/main/kotlin/com/lambda/interaction/material/ContainerTask.kt b/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt similarity index 57% rename from src/main/kotlin/com/lambda/interaction/material/ContainerTask.kt rename to src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt index 630c8d664..e7fa989db 100644 --- a/src/main/kotlin/com/lambda/interaction/material/ContainerTask.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 Lambda + * Copyright 2026 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 @@ -15,26 +15,12 @@ * along with this program. If not, see . */ -package com.lambda.interaction.material +package com.lambda.interaction.material.container -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.context.Automated import com.lambda.task.Task -abstract class ContainerTask : Task() { - private var finish = false - private val delay = 5 - private var currentDelay = 0 - - fun delayedFinish() { - finish = true - } - - init { - listen { - if (finish) { - if (currentDelay++ > delay) success() - } - } - } -} +interface ExternalContainer { + context(_: Automated) + fun access(): Task<*> +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 7279c308d..48613d4b5 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -18,15 +18,14 @@ package com.lambda.interaction.material.container import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.inventory.InventoryRequest +import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial import com.lambda.interaction.material.container.containers.ShulkerBoxContainer -import com.lambda.interaction.material.transfer.TransferResult import com.lambda.task.Task -import com.lambda.util.Communication.logError +import com.lambda.task.tasks.ContainerTransferTask import com.lambda.util.Nameable import com.lambda.util.item.ItemStackUtils.count import com.lambda.util.item.ItemStackUtils.empty @@ -41,16 +40,28 @@ import com.lambda.util.text.highlighted import com.lambda.util.text.literal import com.lambda.util.text.text import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.text.Text // ToDo: Make jsonable to persistently store them abstract class MaterialContainer( val rank: Rank ) : Nameable, Comparable { + context(_: SafeContext) + abstract val slots: List abstract var stacks: List + open val swapMethodPriority = 0 abstract val description: Text + context(automated: Automated) + open val replaceSorter get() = compareByDescending { + it.stack.isEmpty + }.thenByDescending { + it.stack.item in automated.inventoryConfig.disposables + } + @TextDsl + context(_: SafeContext) fun TextBuilder.stock(selection: StackSelection) { literal("\n") literal("Contains ") @@ -66,6 +77,7 @@ abstract class MaterialContainer( highlighted("${selection.optimalStack?.name?.string}") } + context(_: SafeContext) fun description(selection: StackSelection) = buildText { text(description) @@ -75,20 +87,21 @@ abstract class MaterialContainer( override val name: String get() = buildText { text(description) }.string + context(_: SafeContext) val shulkerContainer get() = - stacks.filter { - it.item in ItemUtils.shulkerBoxes - }.map { stack -> + slots.filter { + it.stack.item in ItemUtils.shulkerBoxes + }.map { slot -> ShulkerBoxContainer( - stack.shulkerBoxContents, + slot.stack.shulkerBoxContents, containedIn = this@MaterialContainer, - shulkerStack = stack + shulkerSlot = slot ) }.toSet() - fun update(stacks: List) { - this.stacks = stacks + fun update(slots: List) { + this.stacks = slots } class FailureTask(override val name: String) : Task() { @@ -97,68 +110,52 @@ abstract class MaterialContainer( } } - class AwaitItemTask( - override val name: String, - val selection: StackSelection, - automated: Automated - ) : Task(), Automated by automated { - init { - listen { - if (selection.findContainerWithMaterial() != null) { - success() - } - } + context(_: SafeContext) + open fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + if (fromHere.stack.isEmpty) pickupAndPlace(toSlot.id, fromHere.id) + else { + pickupAndPlace(fromHere.id, toSlot.id) + if (!toSlot.stack.isEmpty) pickup(fromHere.id) } + } - override fun SafeContext.onStart() { - logError(name) + context(automatedSafeContext: AutomatedSafeContext) + fun transfer(stackSelection: StackSelection, destination: MaterialContainer): Boolean = + with(automatedSafeContext) { + val fromSlot = stackSelection.filterSlots(slots).firstOrNull() ?: return false + val toSlot = destination.getReplaceSlot() ?: return false + return inventoryRequest { + transfer(fromSlot, toSlot) + }.submit().done } - } - /** - * Withdraws items from the container to the player's inventory. - */ - @Task.Ta5kBuilder - context(automated: Automated) - open fun withdraw(selection: StackSelection): Task<*>? = null + context(automatedSafeContext: AutomatedSafeContext) + fun transferByTask(stackSelection: StackSelection, destination: MaterialContainer, failIfNoMaterial: Boolean = false) = + ContainerTransferTask(this, destination, stackSelection, automatedSafeContext, failIfNoMaterial) - /** - * Deposits items from the player's inventory into the container. - */ - @Task.Ta5kBuilder - context(automated: Automated) - open fun deposit(selection: StackSelection): Task<*>? = null + protected fun InventoryRequest.InvRequestBuilder.pickupAndPlace(fromId: Int, toId: Int) { + pickup(fromId) + pickup(toId) + } + context(_: SafeContext) open fun matchingStacks(selection: StackSelection) = selection.filterStacks(stacks) + context(_: SafeContext) + open fun matchingSlots(selection: StackSelection) = + selection.filterSlots(slots) + + context(_: SafeContext) open fun materialAvailable(selection: StackSelection) = matchingStacks(selection).count + context(_: SafeContext) open fun spaceAvailable(selection: StackSelection) = matchingStacks(selection).spaceLeft + stacks.empty * selection.stackSize - context(safeContext: SafeContext) - abstract fun isImmediatelyAccessible(): Boolean - - context(automated: Automated) - fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { - val amount = materialAvailable(selection) - if (amount < selection.count) { - return TransferResult.MissingItems(selection.count - amount) - } - -// val space = destination.spaceAvailable(selection) -// if (space == 0) { -// return TransferResult.NoSpace -// } - -// val transferAmount = minOf(amount, space) -// selection.selector = { true } -// selection.count = transferAmount - - return TransferResult.ContainerTransfer(selection, from = this, to = destination, automated) - } + context(_: AutomatedSafeContext) + open fun getReplaceSlot() = slots.sortedWith(replaceSorter).firstOrNull() enum class Rank { MainHand, diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt index 1eadb0b53..424549adb 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt @@ -19,23 +19,31 @@ package com.lambda.interaction.material.container.containers import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.deposit -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.withdraw -import com.lambda.task.tasks.OpenContainer -import com.lambda.util.Communication.info +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal +import net.minecraft.block.entity.ChestBlockEntity import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.util.math.BlockPos data class ChestContainer( override var stacks: List, val blockPos: BlockPos, val containedInStash: StashContainer? = null -) : MaterialContainer(Rank.Chest) { +) : MaterialContainer(Rank.Chest), ExternalContainer { + context(safeContext: SafeContext) + override val slots + get(): List = + if (ContainerManager.lastInteractedBlockEntity is ChestBlockEntity) + safeContext.player.currentScreenHandler.containerSlots + else emptyList() + override val description = buildText { literal("Chest at ") @@ -47,33 +55,6 @@ data class ChestContainer( } } -// override fun prepare() = -// moveIntoEntityRange(blockPos).onSuccess { _, _ -> -//// when { -//// ChestBlock.hasBlockOnTop(world, blockPos) -> breakBlock(blockPos.up()) -//// ChestBlock.hasCatOnTop(world, blockPos) -> kill(cat) -//// } -// if (ChestBlock.isChestBlocked(world, blockPos)) { -// throw ChestBlockedException() -// } -// } - context(automated: Automated) - override fun withdraw(selection: StackSelection) = - OpenContainer(blockPos, automated) - .then { - info("Withdrawing $selection from ${it.type}") - withdraw(it, selection) - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = - OpenContainer(blockPos, automated) - .then { - info("Depositing $selection to ${it.type}") - deposit(it, selection) - } - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false + override fun access() = OpenContainerTask(blockPos, automated) } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt index 398e6f014..60a1207b2 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt @@ -18,91 +18,35 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.Task -import com.lambda.util.item.ItemStackUtils.equal -import com.lambda.util.player.gamemode import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot data object CreativeContainer : MaterialContainer(Rank.Creative) { - override var stacks = emptyList() + context(_: SafeContext) + override val slots: List + get() = emptyList() + override var stacks = emptyList() + + override val swapMethodPriority = 11 override val description = buildText { literal("Creative") } + context(_: SafeContext) + override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { + clickCreativeStack(toHere.stack, from.id) + } + + context(_: SafeContext) override fun materialAvailable(selection: StackSelection): Int = if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 + context(_: SafeContext) override fun spaceAvailable(selection: StackSelection): Int = if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 - - class CreativeDeposit @Ta5kBuilder constructor(val selection: StackSelection, automated: Automated) : Task(), Automated by automated { - override val name: String get() = "Removing $selection from creative inventory" - - init { - listen { - if (!gamemode.isCreative) { - // ToDo: Maybe switch gamemode? - failure(NotInCreativeModeException()) - } - - inventoryRequest { - player.currentScreenHandler?.slots?.let { slots -> - selection.filterSlots(slots).forEach { - clickCreativeStack(ItemStack.EMPTY, it.id) - } - } - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - } - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = CreativeDeposit(selection, automated) - - class CreativeWithdrawal @Ta5kBuilder constructor(val selection: StackSelection, automated: Automated) : Task(), Automated by automated { - override val name: String get() = "Withdrawing $selection from creative inventory" - - init { - listen { - selection.optimalStack?.let { optimalStack -> - if (player.mainHandStack.equal(optimalStack)) { - success() - return@listen - } - - if (!gamemode.isCreative) { - // ToDo: Maybe switch gamemode? - failure(NotInCreativeModeException()) - } - - inventoryRequest { - clickCreativeStack(optimalStack, 36 + player.inventory.selectedSlot) - action { player.inventory.selectedStack = optimalStack } - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - return@listen - } - - failure(IllegalStateException("Cannot move item: no optimal stack")) - } - } - } - - // Withdraws items from the creative menu to the player's main hand - context(automated: Automated) - override fun withdraw(selection: StackSelection) = CreativeWithdrawal(selection, automated) - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = safeContext.gamemode.isCreative - - class NotInCreativeModeException : IllegalStateException("Insufficient permission: not in creative mode") } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt index a290a050a..6b852f1af 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt @@ -19,59 +19,37 @@ package com.lambda.interaction.material.container.containers import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.Task -import com.lambda.util.Communication.info +import com.lambda.task.tasks.AcquireMaterialTask.Companion.acquire +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.task.tasks.PlaceContainerTask +import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText import com.lambda.util.text.literal +import net.minecraft.block.entity.EnderChestBlockEntity import net.minecraft.item.ItemStack -import net.minecraft.util.math.BlockPos +import net.minecraft.item.Items -object EnderChestContainer : MaterialContainer(Rank.EnderChest) { +object EnderChestContainer : MaterialContainer(Rank.EnderChest), ExternalContainer { + context(safeContext: SafeContext) + override val slots + get() = + if (ContainerManager.lastInteractedBlockEntity is EnderChestBlockEntity) + safeContext.player.currentScreenHandler.containerSlots + else emptyList() override var stacks = emptyList() - private var placePos: BlockPos? = null override val description = buildText { literal("Ender Chest") } -// override fun prepare(): Task<*> { -// TODO("Not yet implemented") -// } -// findBlock(Blocks.ENDER_CHEST).onSuccess { pos -> -// moveIntoEntityRange(pos) -// placePos = pos -// }.onFailure { -// acquireStack(Items.ENDER_CHEST.select()).onSuccess { _, stack -> -// placeContainer(stack).onSuccess { _, pos -> -// placePos = pos -// } -// } -// } - - class EnderchestWithdrawal @Ta5kBuilder constructor(selection: StackSelection) : Task() { - override val name = "Withdrawing $selection from ender chest" - - override fun SafeContext.onStart() { - info("Not yet implemented") - success() - } - } - context(automated: Automated) - override fun withdraw(selection: StackSelection) = EnderchestWithdrawal(selection) - - class EnderchestDeposit @Ta5kBuilder constructor(selection: StackSelection) : Task() { - override val name = "Depositing $selection into ender chest" - - override fun SafeContext.onStart() { - info("Not yet implemented") - success() + override fun access() = + automated.acquire { Items.ENDER_CHEST.select() }.thenOrNull { selection -> + val slot = selection.filterSlots(HotbarContainer.slots).firstOrNull() ?: return@thenOrNull FailureTask("Ender Chest not found in hotbar after transfer.") + PlaceContainerTask(slot, automated).finally { pos -> + OpenContainerTask(pos, automated) + } } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = EnderchestDeposit(selection) - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt index 3945ecdee..08248b916 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt @@ -18,41 +18,29 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.ContainerTask -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.deposit +import com.lambda.util.player.SlotUtils.hotbarSlots import com.lambda.util.player.SlotUtils.hotbarStacks import com.lambda.util.text.buildText import com.lambda.util.text.literal -import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot object HotbarContainer : MaterialContainer(Rank.Hotbar) { - override var stacks: List + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.hotbarSlots + override var stacks get() = mc.player?.hotbarStacks ?: emptyList() set(_) {} - override val description = buildText { literal("Hotbar") } - - class HotbarDeposit @Ta5kBuilder constructor( - val selection: StackSelection, - automated: Automated - ) : ContainerTask(), Automated by automated { - override val name: String get() = "Depositing $selection into hotbar" + override val swapMethodPriority = 9 - override fun SafeContext.onStart() { - val handler = player.currentScreenHandler - deposit(handler, selection).finally { - delayedFinish() - }.execute(this@HotbarDeposit) - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = HotbarDeposit(selection, automated) + override val description = buildText { literal("Hotbar") } context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true + override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { + swap(from.id, safeContext.player.hotbarSlots.indexOf(toHere)) + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt index 772548494..af1b0646c 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt @@ -20,18 +20,20 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.player.SlotUtils.allStacks +import com.lambda.util.player.SlotUtils.inventorySlots +import com.lambda.util.player.SlotUtils.inventoryStacks import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot object InventoryContainer : MaterialContainer(Rank.Inventory) { + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.inventorySlots override var stacks: List - get() = mc.player?.allStacks ?: emptyList() + get() = mc.player?.inventoryStacks ?: emptyList() set(_) {} override val description = buildText { literal("Inventory") } - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt index 64206d15e..4a146bf67 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt @@ -18,73 +18,29 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest -import com.lambda.interaction.material.ContainerTask -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.player.SlotUtils.mainHandSlots import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack -import net.minecraft.util.Hand +import net.minecraft.screen.slot.Slot object MainHandContainer : MaterialContainer(Rank.MainHand) { + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.mainHandSlots override var stacks: List get() = mc.player?.mainHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override val description = buildText { literal("MainHand") } - - class HandDeposit @Ta5kBuilder constructor( - val selection: StackSelection, - val hand: Hand, - automated: Automated - ) : ContainerTask(), Automated by automated { - override val name: String get() = "Depositing [$selection] to ${hand.name.lowercase().replace("_", " ")}" - - init { - listen { - val moveStack = InventoryContainer.matchingStacks(selection).firstOrNull() ?: run { - failure("No matching stacks found in inventory") - return@listen - } - - val handStack = player.getStackInHand(hand) - if (moveStack.equal(handStack)) { - success() - return@listen - } - - inventoryRequest { - val stackInOffHand = moveStack.equal(player.offHandStack) - val stackInMainHand = moveStack.equal(player.mainHandStack) - if ((hand == Hand.MAIN_HAND && stackInOffHand) || (hand == Hand.OFF_HAND && stackInMainHand)) { - swapHands() - return@inventoryRequest - } + override val swapMethodPriority = 10 - val slot = player.currentScreenHandler.slots.firstOrNull { it.stack == moveStack } - ?: run { - failure(IllegalStateException("Cannot find stack in inventory")) - return@inventoryRequest - } - swap(slot.id, player.inventory.selectedSlot) - - if (hand == Hand.OFF_HAND) swapHands() - - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - } - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = HandDeposit(selection, Hand.MAIN_HAND, automated) + override val description = buildText { literal("MainHand") } context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true + override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { + swap(from.id, safeContext.player.inventory.selectedSlot) + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt index 3820f499b..c9648d8e7 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt @@ -18,25 +18,29 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.util.player.SlotUtils.offHandSlots import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack -import net.minecraft.util.Hand +import net.minecraft.screen.slot.Slot object OffHandContainer : MaterialContainer(Rank.OffHand) { + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.offHandSlots override var stacks: List get() = mc.player?.offHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override val description = buildText { literal("OffHand") } + override val swapMethodPriority = 10 - context(automated: Automated) - override fun deposit(selection: StackSelection) = MainHandContainer.HandDeposit(selection, Hand.OFF_HAND, automated) + override val description = buildText { literal("OffHand") } - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true + context(_: SafeContext) + override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { + swap(from.id, 40) + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt index 7307bb73d..cd775656d 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt @@ -17,83 +17,39 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.deposit -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.withdraw -import com.lambda.task.Task -import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock -import com.lambda.task.tasks.OpenContainer -import com.lambda.task.tasks.PlaceContainer +import com.lambda.threading.runSafe +import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal +import net.minecraft.block.entity.ShulkerBoxBlockEntity import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot data class ShulkerBoxContainer( override var stacks: List, val containedIn: MaterialContainer, - val shulkerStack: ItemStack, + val shulkerSlot: Slot, ) : MaterialContainer(Rank.ShulkerBox) { + context(safeContext: SafeContext) + override val slots + get(): List = + if (ContainerManager.lastInteractedBlockEntity is ShulkerBoxBlockEntity) + safeContext.player.currentScreenHandler.containerSlots + else emptyList() + override val description = buildText { - highlighted(shulkerStack.name.string) + highlighted(shulkerSlot.stack.name.string) literal(" in ") highlighted(containedIn.name) literal(" in slot ") - highlighted("$slotInContainer") - } - - private val slotInContainer: Int get() = containedIn.stacks.indexOf(shulkerStack) - - class ShulkerWithdraw( - private val selection: StackSelection, - private val shulkerStack: ItemStack, - automated: Automated - ) : Task(), Automated by automated { - override val name = "Withdraw $selection from ${shulkerStack.name.string}" - - override fun SafeContext.onStart() { - PlaceContainer(shulkerStack, this@ShulkerWithdraw).then { placePos -> - OpenContainer(placePos, this@ShulkerWithdraw).then { screen -> - withdraw(screen, selection).then { - breakAndCollectBlock(placePos).finally { - success() - } - } - } - }.execute(this@ShulkerWithdraw) + highlighted("${runSafe { slotInContainer }}") } - } - context(automated: Automated) - override fun withdraw(selection: StackSelection) = ShulkerWithdraw(selection, shulkerStack, automated) - - class ShulkerDeposit( - private val selection: StackSelection, - private val shulkerStack: ItemStack, - automated: Automated - ) : Task(), Automated by automated { - override val name = "Deposit $selection into ${shulkerStack.name.string}" - - override fun SafeContext.onStart() { - PlaceContainer(shulkerStack, this@ShulkerDeposit).then { placePos -> - OpenContainer(placePos, this@ShulkerDeposit).then { screen -> - deposit(screen, selection).then { - breakAndCollectBlock(placePos).finally { - success() - } - } - } - }.execute(this@ShulkerDeposit) - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = ShulkerDeposit(selection, shulkerStack, automated) - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false + context(_: SafeContext) + private val slotInContainer: Int get() = containedIn.slots.indexOf(shulkerSlot) } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt index bd317df73..282fcd7da 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt @@ -25,12 +25,16 @@ import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.util.math.Box data class StashContainer( val chests: Set, val pos: Box, ) : MaterialContainer(Rank.Stash) { + context(_: SafeContext) + override val slots: List + get() = chests.flatMap { it.slots } override var stacks: List get() = chests.flatMap { it.stacks } set(_) {} @@ -40,11 +44,9 @@ data class StashContainer( highlighted(pos.center.roundedBlockPos.toShortString()) } + context(_: SafeContext) override fun materialAvailable(selection: StackSelection): Int = chests.sumOf { it.materialAvailable(selection) } - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false } diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt deleted file mode 100644 index 9845dc3a7..000000000 --- a/src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt +++ /dev/null @@ -1,99 +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.material.transfer - -import com.lambda.interaction.material.StackSelection -import com.lambda.util.item.ItemStackUtils.equal -import net.minecraft.item.ItemStack -import net.minecraft.screen.slot.Slot - -/** - * A class representing changes in an inventory's state over time. It acts as a tracker for - * detecting, storing, and merging differences between the original and updated states of - * inventory slots. This class extends a map-like structure, where the key is the slot index - * and the value is a list of pairs representing the original and updated states of an inventory slot. - * - * Example: - * ``` - * #0: 64 obsidian -> 0 air, 0 air -> 64 obsidian - * #36: 12 observer -> 11 observer - * ``` - * - Where `#0` is the slot id, first it was emptied and then got `64 obsidian` again - * - Where `#36` is the slot id, that was reduced by `1 observer` - * - * @property slots A list of `Slot` objects representing the current inventory slots being tracked. - * Defaults to an empty list. - */ -class InventoryChanges( - private var slots: List, -) : MutableMap>> by HashMap() { - private val originalStacks = slots.map { it.stack.copy() } - - /** - * Detect and store changes directly in the map. - */ - fun detectChanges() { - require(slots.isNotEmpty()) { "Cannot detect changes on an empty slots list" } - slots.forEachIndexed { index, slot -> - val originalStack = originalStacks[index] - val updatedStack = slot.stack - if (!originalStack.equal(updatedStack)) { - getOrPut(index) { mutableListOf() }.add(originalStack to updatedStack.copy()) - } - } - } - - /** - * Create a new `InventoryChanges` object that merges this changes object with another one. - * - * @param other Another `InventoryChanges` instance to merge with. - * @return A new `InventoryChanges` instance containing merged entries. - */ - infix fun merge(other: InventoryChanges) { - require(slots.isNotEmpty()) { "Cannot merge changes to an empty slots list" } - other.forEach { (key, value) -> - getOrPut(key) { mutableListOf() }.addAll(value) - } - } - - /** - * Evaluates if the current inventory changes fulfill the given selection requirement. - * - * @param to A list of `Slot` objects to evaluate for the selection criteria. - * @param selection A `StackSelection` object that specifies the selection criteria, including the - * target items and their required count. - * @return `true` if the total count of matching items across the filtered slots meets or exceeds - * the required count specified in the `StackSelection`; `false` otherwise. - */ - fun fulfillsSelection(to: List, selection: StackSelection): Boolean { - require(slots.isNotEmpty()) { "Cannot evaluate selection on an empty slots list" } - val targetSlots = selection.filterSlots(to).map { it.id } - return filter { it.key in targetSlots }.entries.sumOf { (_, changes) -> - changes.lastOrNull()?.second?.count ?: 0 - } >= selection.count - } - - override fun toString() = - if (entries.isEmpty()) { - "No changes detected" - } else { - entries.joinToString("\n") { key -> - "#${key.key}: ${key.value.joinToString { "${it.first} -> ${it.second}" }}" - } - } -} diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt deleted file mode 100644 index 70788f938..000000000 --- a/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt +++ /dev/null @@ -1,101 +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.material.transfer - -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest -import com.lambda.task.Task -import com.lambda.util.extension.containerSlots -import com.lambda.util.extension.inventorySlots -import com.lambda.util.item.ItemUtils.block -import net.minecraft.screen.ScreenHandler -import net.minecraft.screen.slot.Slot - -class SlotTransfer @Ta5kBuilder constructor( - val screen: ScreenHandler, - private val selection: StackSelection, - val from: List, - val to: List, - private val closeScreen: Boolean = true, - automated: Automated -) : Task(), Automated by automated { - private var selectedFrom = listOf() - private var selectedTo = listOf() - override val name: String - get() = "Moving $selection from slots [${selectedFrom.joinToString { "${it.id}" }}] to slots [${selectedTo.joinToString { "${it.id}" }}] in ${screen::class.simpleName}" - - private var delay = 0 - private lateinit var changes: InventoryChanges - - override fun SafeContext.onStart() { - changes = InventoryChanges(player.currentScreenHandler.slots) - } - - init { - listen { - if (changes.fulfillsSelection(to, selection)) { - if (closeScreen) { player.closeHandledScreen() } - success() - return@listen - } - - val current = player.currentScreenHandler - if (current != screen) { - failure("Screen has changed. Expected ${screen::class.simpleName} (revision ${screen.revision}) but got ${current::class.simpleName} (revision ${current.revision})") - return@listen - } - - selectedFrom = selection.filterSlots(from) - selectedTo = to.filter { it.stack.isEmpty } + to.filter { it.stack.item.block in inventoryConfig.disposables } - - val nextFrom = selectedFrom.firstOrNull() ?: return@listen - val nextTo = selectedTo.firstOrNull() ?: return@listen - - inventoryRequest { - swap(nextTo.id, 1) - swap(nextFrom.id, 1) - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - } - } - - companion object { - @Ta5kBuilder - fun Automated.moveItems( - screen: ScreenHandler, - selection: StackSelection, - from: List, - to: List, - closeScreen: Boolean = true, - ) = SlotTransfer(screen, selection, from, to, closeScreen, this) - - @Ta5kBuilder - context(automated: Automated) - fun withdraw(screen: ScreenHandler, selection: StackSelection, closeScreen: Boolean = true) = - automated.moveItems(screen, selection, screen.containerSlots, screen.inventorySlots, closeScreen) - - @Ta5kBuilder - context(automated: Automated) - fun deposit(screen: ScreenHandler, selection: StackSelection, closeScreen: Boolean = true) = - automated.moveItems(screen, selection, screen.inventorySlots, screen.containerSlots, closeScreen) - } -} diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt deleted file mode 100644 index 0bc68f3fc..000000000 --- a/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ /dev/null @@ -1,75 +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.material.transfer - -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.Task - -abstract class TransferResult : Task() { - data class ContainerTransfer( - val selection: StackSelection, - val from: MaterialContainer, - val to: MaterialContainer, - val automated: Automated - ) : TransferResult(), Automated by automated { - override val name = "Container Transfer of [$selection] from [${from.name}] to [${to.name}]" - - override fun SafeContext.onStart() { - val withdrawal = from.withdraw(selection) - val deposit = to.deposit(selection) - - val task = when { - withdrawal != null && deposit != null -> { - withdrawal.then { - deposit.finally { success() } - } - } - withdrawal != null -> { - withdrawal.finally { success() } - } - deposit != null -> { - deposit.finally { success() } - } - else -> null - } - - task?.execute(this@ContainerTransfer) - } - } - - data object NoSpace : TransferResult() { - override val name = "No space left in the target container" - - // ToDo: Needs inventory space resolver. compressing or disposing - override fun SafeContext.onStart() { - failure("No space left in the target container") - } - } - - data class MissingItems(val missing: Int) : TransferResult() { - override val name = "Missing $missing items" - - // ToDo: Find other satisfying permutations - override fun SafeContext.onStart() { - failure("Missing $missing items") - } - } -} diff --git a/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index f553623be..170556d4c 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -23,18 +23,19 @@ import com.lambda.context.SafeContext import com.lambda.event.events.EntityEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.hotbar.HotbarRequest import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest import com.lambda.interaction.managers.rotating.Rotation.Companion.rotationTo import com.lambda.interaction.managers.rotating.RotationManager import com.lambda.interaction.managers.rotating.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.transfer -import com.lambda.interaction.material.container.containers.MainHandContainer +import com.lambda.interaction.material.container.containers.HotbarContainer import com.lambda.interaction.material.container.containers.OffHandContainer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run import com.lambda.threading.runSafe +import com.lambda.threading.runSafeAutomated import com.lambda.threading.runSafeGameScheduled import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.info @@ -51,6 +52,7 @@ import com.lambda.util.math.flooredBlockPos import com.lambda.util.math.getHitVec import com.lambda.util.math.minus import com.lambda.util.math.plus +import com.lambda.util.player.SlotUtils.hotbarStacks import com.lambda.util.world.fastEntitySearch import net.minecraft.block.Blocks import net.minecraft.entity.Entity @@ -470,8 +472,14 @@ object CrystalAura : Module( if (swap && (swapHand == Hand.MAIN_HAND && player.mainHandStack.item != selection.item) || (swapHand == Hand.OFF_HAND && player.offHandStack.item != selection.item) - ) selection.transfer(when (swapHand) { Hand.MAIN_HAND -> MainHandContainer; Hand.OFF_HAND -> OffHandContainer }) - ?.run() + ) runSafeAutomated { + val crystalSlot = player.hotbarStacks.indexOfFirst { selection.filterStack(it) } + if (crystalSlot != -1) { + if (!HotbarRequest(crystalSlot, this).submit().done) return@runSafe + } + val swapTo = when (swapHand) { Hand.MAIN_HAND -> HotbarContainer; Hand.OFF_HAND -> OffHandContainer } + if (!selection.transfer(swapTo)) return@runSafe + } placeTimer.runSafeIfPassed(placeDelay.milliseconds) { placeInternal(this@Opportunity, swapHand) diff --git a/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt b/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt index b17420d68..e3ecd9642 100644 --- a/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt +++ b/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt @@ -23,7 +23,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run -import com.lambda.task.tasks.AcquireMaterial.Companion.acquire +import com.lambda.task.tasks.AcquireMaterialTask.Companion.acquire import net.minecraft.item.Items object ContainerTest : Module( diff --git a/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt b/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt index 107a925ad..f0c3868da 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt @@ -27,8 +27,8 @@ import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock -import com.lambda.task.tasks.OpenContainer -import com.lambda.task.tasks.PlaceContainer +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.task.tasks.PlaceContainerTask import com.lambda.util.item.ItemUtils.shulkerBoxes import net.minecraft.item.Items import net.minecraft.screen.ScreenHandler @@ -55,13 +55,13 @@ object InventoryTweaks : Module( listen { if (it.action != SlotActionType.PICKUP || it.button != 1) return@listen - val stack = it.screenHandler.getSlot(it.slot).stack - if (!(instantShulker && stack.item in shulkerBoxes) && !(instantEChest && stack.item == Items.ENDER_CHEST)) return@listen + val slot = it.screenHandler.getSlot(it.slot) + if (!(instantShulker && slot.stack.item in shulkerBoxes) && !(instantEChest && slot.stack.item == Items.ENDER_CHEST)) return@listen it.cancel() lastOpenScreen = null - placeAndOpen = PlaceContainer(stack, this@InventoryTweaks).then { placePos -> + placeAndOpen = PlaceContainerTask(slot, this@InventoryTweaks).then { placePos -> placedPos = placePos - OpenContainer(placePos, this@InventoryTweaks).finally { screenHandler -> + OpenContainerTask(placePos, this@InventoryTweaks).finally { screenHandler -> lastOpenScreen = screenHandler } }.run() diff --git a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 25ba05ffb..3ad052cd5 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -47,11 +47,7 @@ object Nuker : Module( private var task: Task<*>? = null init { - setDefaultAutomationConfig { - applyEdits { - inventoryConfig::immediateAccessOnly.edit { defaultValue(true) } - } - } + setDefaultAutomationConfig() onEnable { task = tickingBlueprint { diff --git a/src/main/kotlin/com/lambda/module/modules/player/Printer.kt b/src/main/kotlin/com/lambda/module/modules/player/Printer.kt index fe3e57480..587af9f41 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Printer.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Printer.kt @@ -54,7 +54,6 @@ object Printer : Module( editTyped(buildConfig::pathing, buildConfig::stayInRange) { defaultValue(false) } editTyped(breakConfig::efficientOnly, breakConfig::suitableToolsOnly) { defaultValue(false) } interactConfig::airPlace.edit { defaultValue(InteractConfig.AirPlaceMode.Grim) } - inventoryConfig::immediateAccessOnly.edit { defaultValue(true) } } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index c5c1c4ea7..0209808a6 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -70,7 +70,6 @@ object Scaffold : Module( ::swapWithDisposables, ::providerPriority, ::storePriority, - ::immediateAccessOnly, ::accessShulkerBoxes, ::accessEnderChest, ::accessChests, diff --git a/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt b/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt index 585bfc669..393ee2558 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt @@ -44,7 +44,6 @@ object StackReplenish : Module( applyEdits { hideAllGroupsExcept(inventoryConfig) inventoryConfig.apply { - ::immediateAccessOnly.edit { defaultValue(true) } hide(::disposables, ::swapWithDisposables, ::providerPriority, ::storePriority) } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt b/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt index c1cc5e0f0..4480fb2fe 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt @@ -28,7 +28,6 @@ import com.lambda.util.EnchantmentUtils.forEachEnchantment import com.lambda.util.EnchantmentUtils.getEnchantment import com.lambda.util.player.SlotUtils.hotbarSlots import com.lambda.util.player.SlotUtils.inventorySlots -import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot @@ -67,7 +66,8 @@ object ToolSaver : Module( }.thenByDescending { it.stack.isEmpty }.thenByDescending { - (it.stack.item as? BlockItem)?.block in inventoryConfig.disposables + it.stack.item in inventoryConfig.disposables + }.thenByDescending { it.stack.isStackable } val swapWith = inventorySlots @@ -78,6 +78,8 @@ object ToolSaver : Module( endangered to swapWith } + if (swaps.isEmpty()) return@listen + inventoryRequest { swaps.forEach { pickup(it.first.id) diff --git a/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/src/main/kotlin/com/lambda/task/tasks/AcquireMaterialTask.kt similarity index 69% rename from src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt rename to src/main/kotlin/com/lambda/task/tasks/AcquireMaterialTask.kt index 60d70f2b4..95472a839 100644 --- a/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/src/main/kotlin/com/lambda/task/tasks/AcquireMaterialTask.kt @@ -22,9 +22,11 @@ import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial +import com.lambda.interaction.material.container.containers.HotbarContainer import com.lambda.task.Task +import com.lambda.threading.runSafeAutomated -class AcquireMaterial @Ta5kBuilder constructor( +class AcquireMaterialTask @Ta5kBuilder constructor( val selection: StackSelection, automated: Automated ) : Task(), Automated by automated { @@ -32,17 +34,19 @@ class AcquireMaterial @Ta5kBuilder constructor( get() = "Acquiring $selection" override fun SafeContext.onStart() { - selection.findContainerWithMaterial() - ?.withdraw(selection) - ?.finally { - success(selection) - }?.execute(this@AcquireMaterial) - ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path + runSafeAutomated { + selection.findContainerWithMaterial() + ?.transferByTask(selection, HotbarContainer) + ?.finally { + success(selection) + }?.execute(this@AcquireMaterialTask) + ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path + } } companion object { @Ta5kBuilder fun Automated.acquire(selection: () -> StackSelection) = - AcquireMaterial(selection(), this) + AcquireMaterialTask(selection(), this) } } diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2051800ec..ff0c1675e 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -22,6 +22,7 @@ import com.lambda.Lambda.LOG import com.lambda.config.AutomationConfig.Companion.DEFAULT import com.lambda.config.groups.EatConfig.Companion.reasonEating import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -54,8 +55,7 @@ import com.lambda.task.tasks.EatTask.Companion.eat import com.lambda.threading.runSafeAutomated import com.lambda.util.Formatting.format import com.lambda.util.extension.Structure -import com.lambda.util.extension.inventorySlots -import com.lambda.util.item.ItemUtils.block +import com.lambda.util.extension.playerSlots import com.lambda.util.player.SlotUtils.hotbarAndInventoryStacks import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos @@ -107,7 +107,7 @@ class BuildTask private constructor( if (collectDrops()) return@listen - simulateAndProcess() + runSafeAutomated { simulateAndProcess() } } listen { @@ -118,7 +118,7 @@ class BuildTask private constructor( } } - private fun SafeContext.simulateAndProcess() { + private fun AutomatedSafeContext.simulateAndProcess() { val results = runSafeAutomated { blueprint.structure.simulate() } DEFAULT.drawables = results @@ -141,7 +141,7 @@ class BuildTask private constructor( handleResult(bestResult, viableResults) } - private fun SafeContext.handleResult(result: BuildResult, allResults: List) { + private fun AutomatedSafeContext.handleResult(result: BuildResult, allResults: List) { if (result !is Dependent && result !is Contextual && pendingInteractions.isNotEmpty()) return when (result) { @@ -193,7 +193,7 @@ class BuildTask private constructor( is Resolvable -> { LOG.info("Resolving: ${result.name}") - result.resolve()?.execute(this@BuildTask) + result.resolve() } } } @@ -211,8 +211,8 @@ class BuildTask private constructor( } if (player.hotbarAndInventoryStacks.none { it.isEmpty }) { - val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { - it.stack.item.block in inventoryConfig.disposables + val stackToThrow = player.currentScreenHandler.playerSlots.firstOrNull { + it.stack.item in inventoryConfig.disposables } ?: run { failure("No item in inventory to throw but inventory is full and cant pick up item drop") return@let true diff --git a/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt new file mode 100644 index 000000000..43b63479a --- /dev/null +++ b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2026 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.task.tasks + +import com.lambda.context.Automated +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest +import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ExternalContainer +import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.material.container.containers.InventoryContainer +import com.lambda.task.Task +import com.lambda.threading.runSafeAutomated + +class ContainerTransferTask( + private val fromContainer: MaterialContainer, + private val destination: MaterialContainer, + private val stackSelection: StackSelection, + automated: Automated, + private val failIfNoMaterial: Boolean = false +) : Task(), Automated by automated { + override val name = "Transferring $stackSelection from $fromContainer to $destination" + + init { + listen { + runSafeAutomated { + val slots = fromContainer.slots + val toSlots = destination.slots + if (fromContainer is ExternalContainer && slots.isEmpty()) { + if (destination is ExternalContainer && toSlots.isEmpty()) { + fromContainer.transferByTask(stackSelection, InventoryContainer).then { + InventoryContainer.transferByTask(stackSelection, destination).finally { success() } + } + } + fromContainer.access().execute(this@ContainerTransferTask).then { + fromContainer.transferByTask(stackSelection, destination).finally { success() } + } + return@listen + } else if (destination is ExternalContainer && toSlots.isEmpty()) { + destination.access().execute(this@ContainerTransferTask).then { + fromContainer.transferByTask(stackSelection, destination).finally { success() } + } + return@listen + } + + stackSelection.filterSlots(slots).firstOrNull()?.let { fromSlot -> + destination.getReplaceSlot()?.let { toSlot -> + inventoryRequest { + with(fromContainer) { transfer(fromSlot, toSlot) } + onComplete { success() } + }.submit() + return@listen + } + } + + if (failIfNoMaterial) { + failure("$stackSelection not found.") + return@listen + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt index b9e6c4f37..31c2deaf9 100644 --- a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt @@ -69,8 +69,9 @@ class EatTask @Ta5kBuilder constructor( mc.options.useKey.isPressed = false holdingUse = false } - foodFinder.transfer(MainHandContainer) - ?.execute(this@EatTask) ?: failure("No food found") + runSafeAutomated { + foodFinder.transfer(MainHandContainer) + } return@listen } eatStack = player.mainHandStack diff --git a/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt similarity index 98% rename from src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt rename to src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt index 3a8ea0d4a..1554b89cf 100644 --- a/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt @@ -31,7 +31,7 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -class OpenContainer @Ta5kBuilder constructor( +class OpenContainerTask @Ta5kBuilder constructor( private val blockPos: BlockPos, private val automated: Automated, private val waitForSlotLoad: Boolean = true, diff --git a/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt similarity index 94% rename from src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt rename to src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt index 2274eec30..22e52c979 100644 --- a/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt @@ -36,14 +36,15 @@ import net.minecraft.block.ChestBlock import net.minecraft.entity.mob.ShulkerEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items +import net.minecraft.screen.slot.Slot import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -class PlaceContainer @Ta5kBuilder constructor( - val stack: ItemStack, +class PlaceContainerTask @Ta5kBuilder constructor( + val slot: Slot, automated: Automated ) : Task(), Automated by automated { - private val startStack: ItemStack = stack.copy() + private val startStack: ItemStack = slot.stack.copy() override val name: String get() = "Placing container ${startStack.name.string}" override fun SafeContext.onStart() { @@ -75,7 +76,7 @@ class PlaceContainer @Ta5kBuilder constructor( .toBlueprint() .build(finishOnDone = true, collectDrops = false) .finally { success(containerPosition) } - .execute(this@PlaceContainer) + .execute(this@PlaceContainerTask) } private fun SafeContext.canBeOpened( diff --git a/src/main/kotlin/com/lambda/util/extension/Screen.kt b/src/main/kotlin/com/lambda/util/extension/Screen.kt index 8fda215ea..1b6546832 100644 --- a/src/main/kotlin/com/lambda/util/extension/Screen.kt +++ b/src/main/kotlin/com/lambda/util/extension/Screen.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 Lambda + * Copyright 2026 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 @@ -25,8 +25,8 @@ import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot val ScreenHandler.containerSlots: List get() = slots.filter { it.inventory is SimpleInventory } -val ScreenHandler.inventorySlots: List get() = slots.filter { it.inventory is PlayerInventory } +val ScreenHandler.playerSlots: List get() = slots.filter { it.inventory is PlayerInventory } val ScreenHandler.craftingSlots: List get() = slots.filter { it.inventory is CraftingInventory } val ScreenHandler.containerStacks: List get() = containerSlots.map { it.stack } -val ScreenHandler.inventoryStacks: List get() = inventorySlots.map { it.stack } +val ScreenHandler.playerStacks: List get() = playerSlots.map { it.stack } diff --git a/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt index ff59a6a59..df5745f2b 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt @@ -28,6 +28,7 @@ import net.minecraft.entity.attribute.EntityAttributeModifier import net.minecraft.entity.attribute.EntityAttributes import net.minecraft.item.ItemStack import net.minecraft.registry.entry.RegistryEntry +import net.minecraft.screen.slot.Slot object ItemStackUtils { // FixMe: Change this fucking retarded stuff when mojang wake up from their coma and realize they fucked this shit up @@ -74,8 +75,11 @@ object ItemStackUtils { val ItemStack.spaceLeft get() = maxCount - count val ItemStack.hasSpace get() = spaceLeft > 0 val List.spaceLeft get() = sumOf { it.spaceLeft } + val List.spaceLeft @JvmName("slotSpaceLeft") get() = sumOf { it.stack.spaceLeft } val List.empty: Int get() = count { it.isEmpty } + val List.empty: Int @JvmName("slotEmpty") get() = count { it.stack.isEmpty } val List.count: Int get() = if (isEmpty()) -1 else sumOf { it.count } + val List.count: Int @JvmName("slotCount") get() = if (isEmpty()) -1 else sumOf { it.stack.count } val List.copy: List get() = map { it.copy() } context(safeContext: SafeContext) diff --git a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index c91b3776a..b1d4db4ba 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -18,7 +18,6 @@ package com.lambda.util.item import net.minecraft.block.Block -import net.minecraft.block.Blocks import net.minecraft.component.DataComponentTypes import net.minecraft.item.BlockItem import net.minecraft.item.Item @@ -106,20 +105,20 @@ object ItemUtils { ) val defaultDisposables = setOf( - Blocks.DIRT, - Blocks.GRASS_BLOCK, - Blocks.COBBLESTONE, - Blocks.GRANITE, - Blocks.DIORITE, - Blocks.ANDESITE, - Blocks.SANDSTONE, - Blocks.RED_SANDSTONE, - Blocks.NETHERRACK, - Blocks.END_STONE, - Blocks.STONE, - Blocks.BASALT, - Blocks.BLACKSTONE, - Blocks.COBBLED_DEEPSLATE + Items.DIRT, + Items.GRASS_BLOCK, + Items.COBBLESTONE, + Items.GRANITE, + Items.DIORITE, + Items.ANDESITE, + Items.SANDSTONE, + Items.RED_SANDSTONE, + Items.NETHERRACK, + Items.END_STONE, + Items.STONE, + Items.BASALT, + Items.BLACKSTONE, + Items.COBBLED_DEEPSLATE ) val Item.block: Block get() = Block.getBlockFromItem(this) diff --git a/src/main/kotlin/com/lambda/util/player/SlotUtils.kt b/src/main/kotlin/com/lambda/util/player/SlotUtils.kt index 2532b3a21..71815f3d5 100644 --- a/src/main/kotlin/com/lambda/util/player/SlotUtils.kt +++ b/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -29,21 +29,20 @@ import net.minecraft.screen.slot.SlotActionType object SlotUtils { val ClientPlayerEntity.allSlots get() = currentScreenHandler.slots.filter { it.inventory is PlayerInventory } val ClientPlayerEntity.armorSlots get() = allSlots.filterIsInstance() - val ClientPlayerEntity.inventorySlots: List get() { - return if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) emptyList() + val ClientPlayerEntity.inventorySlots: List get() = + if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) emptyList() else if (currentScreenHandler === playerScreenHandler) allSlots.subList(4, 31) else allSlots.subList(0, 27) - } - val ClientPlayerEntity.hotbarSlots: List get() { - return if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots - else if (currentScreenHandler === playerScreenHandler) allSlots.subList(30, 39) - else allSlots.subList(26, 35) - } - val ClientPlayerEntity.hotbarAndInventorySlots: List get() { - return if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots - else if (currentScreenHandler === playerScreenHandler) allSlots.subList(4, 39) - else allSlots.subList(0, 35) - } + val ClientPlayerEntity.hotbarSlots: List get() = + if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots + else if (currentScreenHandler === playerScreenHandler) allSlots.subList(31, 40) + else allSlots.subList(27, 36) + val ClientPlayerEntity.hotbarAndInventorySlots: List get() = + if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots + else if (currentScreenHandler === playerScreenHandler) allSlots.subList(4, 40) + else allSlots.subList(0, 36) + val ClientPlayerEntity.mainHandSlots: List get() = listOf(hotbarSlots[inventory.selectedSlot]) + val ClientPlayerEntity.offHandSlots: List get() = if (currentScreenHandler === playerScreenHandler) listOf(allSlots.last()) else emptyList() val ClientPlayerEntity.allStacks: List get() = hotbarAndInventoryStacks + equipmentStacks val ClientPlayerEntity.equipmentStacks: List get() = inventory.equipment.map.map { it.value } From 06d629658ee70a7435d5a9de3854432a0ef86867 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:17:32 +0000 Subject: [PATCH 2/4] creative stack handling --- .../simulation/result/results/BreakResult.kt | 2 +- .../material/container/ContainerManager.kt | 6 ++--- .../material/container/MaterialContainer.kt | 16 +++++++++-- .../container/containers/CreativeContainer.kt | 27 +++++++++++++++---- .../container/containers/HotbarContainer.kt | 4 +-- .../container/containers/MainHandContainer.kt | 4 +-- .../container/containers/OffHandContainer.kt | 4 +-- .../task/tasks/ContainerTransferTask.kt | 6 +++-- 8 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt index 30741633e..ed85fd9e8 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt @@ -101,7 +101,7 @@ sealed class BreakResult : BuildResult() { override fun resolve() { selectStack { isItem(badItem).not() - }.transferByTask(HotbarContainer) + }.transferByTask(HotbarContainer)?.execute(task) } override fun compareResult(other: ComparableResult) = diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index c61e016f9..05a408244 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -98,9 +98,7 @@ object ContainerManager : Loadable { context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.transferByTask(destination: MaterialContainer) = with(automatedSafeContext) { - findContainerWithMaterial( - inventoryConfig.containerSelection - )?.transferByTask(this@transferByTask, destination) + findContainerWithMaterial()?.transferByTask(this@transferByTask, destination) } context(_: SafeContext) @@ -122,8 +120,8 @@ object ContainerManager : Loadable { containerSelection: ContainerSelection = automated.inventoryConfig.containerSelection, ): List = containers() - .filter { it.materialAvailable(this) >= count } .filter { containerSelection.matches(it) } + .filter { it.materialAvailable(this) >= count } .sortedWith(automated.inventoryConfig.providerPriority.materialComparator(this)) context(automatedSafeContext: AutomatedSafeContext) diff --git a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 48613d4b5..1176e24ea 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -39,6 +39,7 @@ import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal import com.lambda.util.text.text +import net.minecraft.component.DataComponentTypes import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot import net.minecraft.text.Text @@ -58,6 +59,12 @@ abstract class MaterialContainer( it.stack.isEmpty }.thenByDescending { it.stack.item in automated.inventoryConfig.disposables + }.thenByDescending { + it.stack.item.components.contains(DataComponentTypes.TOOL) + }.thenByDescending { + it.stack.item.components.contains(DataComponentTypes.FOOD) + }.thenByDescending { + it.stack.isStackable } @TextDsl @@ -122,10 +129,11 @@ abstract class MaterialContainer( context(automatedSafeContext: AutomatedSafeContext) fun transfer(stackSelection: StackSelection, destination: MaterialContainer): Boolean = with(automatedSafeContext) { - val fromSlot = stackSelection.filterSlots(slots).firstOrNull() ?: return false + val fromSlot = getSlot(stackSelection) ?: return false val toSlot = destination.getReplaceSlot() ?: return false return inventoryRequest { - transfer(fromSlot, toSlot) + if (swapMethodPriority > destination.swapMethodPriority) transfer(fromSlot, toSlot) + else with(destination) { transfer(toSlot, fromSlot) } }.submit().done } @@ -157,6 +165,10 @@ abstract class MaterialContainer( context(_: AutomatedSafeContext) open fun getReplaceSlot() = slots.sortedWith(replaceSorter).firstOrNull() + context(_: SafeContext) + open fun getSlot(stackSelection: StackSelection): Slot? = + stackSelection.filterSlots(slots).firstOrNull() + enum class Rank { MainHand, OffHand, diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt index 60a1207b2..f93a6b672 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt @@ -24,6 +24,8 @@ import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer import com.lambda.util.text.buildText import com.lambda.util.text.literal +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.inventory.SingleStackInventory import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot @@ -37,14 +39,29 @@ data object CreativeContainer : MaterialContainer(Rank.Creative) { override val description = buildText { literal("Creative") } - context(_: SafeContext) - override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { - clickCreativeStack(toHere.stack, from.id) + context(safeContext : SafeContext) + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + clickCreativeStack(fromHere.stack, toSlot.id) + safeContext.player.currentScreenHandler.slots[toSlot.id].stack = fromHere.stack } - context(_: SafeContext) + context(safeContext: SafeContext) + override fun getSlot(stackSelection: StackSelection) = + stackSelection.optimalStack?.let { stack -> + Slot( + object : SingleStackInventory { + override fun getStack() = stack + override fun setStack(stack: ItemStack?) {} + override fun markDirty() {} + override fun canPlayerUse(player: PlayerEntity?) = false + }, + 0, 0, 0 + ) + } + + context(safeContext: SafeContext) override fun materialAvailable(selection: StackSelection): Int = - if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 + if (safeContext.player.isCreative && selection.optimalStack != null) Int.MAX_VALUE else 0 context(_: SafeContext) override fun spaceAvailable(selection: StackSelection): Int = diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt index 08248b916..d0134181e 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt @@ -40,7 +40,7 @@ object HotbarContainer : MaterialContainer(Rank.Hotbar) { override val description = buildText { literal("Hotbar") } context(safeContext: SafeContext) - override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { - swap(from.id, safeContext.player.hotbarSlots.indexOf(toHere)) + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + swap(toSlot.id, safeContext.player.hotbarSlots.indexOf(toSlot)) } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt index 4a146bf67..2b12e47de 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt @@ -40,7 +40,7 @@ object MainHandContainer : MaterialContainer(Rank.MainHand) { override val description = buildText { literal("MainHand") } context(safeContext: SafeContext) - override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { - swap(from.id, safeContext.player.inventory.selectedSlot) + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + swap(toSlot.id, safeContext.player.inventory.selectedSlot) } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt index c9648d8e7..235350717 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt @@ -40,7 +40,7 @@ object OffHandContainer : MaterialContainer(Rank.OffHand) { override val description = buildText { literal("OffHand") } context(_: SafeContext) - override fun InventoryRequest.InvRequestBuilder.transfer(from: Slot, toHere: Slot) { - swap(from.id, 40) + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + swap(toSlot.id, 40) } } diff --git a/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt index 43b63479a..6c52b7fd1 100644 --- a/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt @@ -59,10 +59,12 @@ class ContainerTransferTask( return@listen } - stackSelection.filterSlots(slots).firstOrNull()?.let { fromSlot -> + fromContainer.getSlot(stackSelection)?.let { fromSlot -> destination.getReplaceSlot()?.let { toSlot -> inventoryRequest { - with(fromContainer) { transfer(fromSlot, toSlot) } + if (fromContainer.swapMethodPriority > destination.swapMethodPriority) + with(fromContainer) { transfer(fromSlot, toSlot) } + else with(destination) { transfer(toSlot, toSlot) } onComplete { success() } }.submit() return@listen From b3c567d38cae80b510fcd2fecf2b8531536eaf4f Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:58:22 +0000 Subject: [PATCH 3/4] improve / fix transferring from containers that require external actions like placing and opening a shulker, to then break and pick it up again afterward, etc. --- .../command/commands/TransferCommand.kt | 2 +- .../com/lambda/config/groups/BuildConfig.kt | 2 +- .../com/lambda/config/groups/BuildSettings.kt | 2 +- .../com/lambda/interaction/BaritoneManager.kt | 8 ++- .../simulation/checks/InteractSim.kt | 4 +- .../construction/verify/TargetState.kt | 5 +- .../managers/interacting/InteractManager.kt | 2 +- .../material/container/ContainerManager.kt | 27 +++++--- .../material/container/ExternalContainer.kt | 7 ++- .../material/container/MaterialContainer.kt | 8 +-- .../container/containers/ChestContainer.kt | 13 +++- .../container/containers/CreativeContainer.kt | 13 ++-- .../containers/EnderChestContainer.kt | 31 +++++++--- .../container/containers/HotbarContainer.kt | 2 +- .../containers/ShulkerBoxContainer.kt | 25 +++++++- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 3 +- .../task/tasks/ContainerTransferTask.kt | 62 ++++++++++++------- .../lambda/task/tasks/OpenContainerTask.kt | 16 +++-- .../lambda/task/tasks/PlaceContainerTask.kt | 11 ++-- .../kotlin/com/lambda/util/item/ItemUtils.kt | 2 +- 20 files changed, 165 insertions(+), 80 deletions(-) diff --git a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index cc61edea1..3df05168a 100644 --- a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -67,7 +67,7 @@ object TransferCommand : LambdaCommand( isItem(stack(ctx).value().item) } AutomationConfig.Companion.DEFAULT.runSafeAutomated { - findContainersWithSpace(selection).forEachIndexed { i, container -> + selection.findContainersWithSpace().forEachIndexed { i, container -> builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) } } diff --git a/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index e4b2fab9d..9c4cad6ca 100644 --- a/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -34,8 +34,8 @@ interface BuildConfig : ISettingGroup { val actionTimeout: Int val maxBuildDependencies: Int - val entityReach: Double val blockReach: Double + val entityReach: Double val scanReach: Double val checkSideVisibility: Boolean diff --git a/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 0aa955dbb..623d06a3e 100644 --- a/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -44,8 +44,8 @@ class BuildSettings( override val actionTimeout by c.setting("Action Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks").group(baseGroup, Group.General).index() override val maxBuildDependencies by c.setting("Max Sim Dependencies", 3, 0..10, 1, "Maximum dependency build results").group(baseGroup, Group.General).index() - override var entityReach by c.setting("Attack Reach", 3.0, 1.0..7.0, 0.01, "Maximum entity interaction distance").group(baseGroup, Group.Reach).index() override var blockReach by c.setting("Interact Reach", 4.5, 1.0..7.0, 0.01, "Maximum block interaction distance").group(baseGroup, Group.Reach).index() + override var entityReach by c.setting("Attack Reach", 3.0, 1.0..7.0, 0.01, "Maximum entity interaction distance").group(baseGroup, Group.Reach).index() override val scanReach: Double get() = max(entityReach, blockReach) override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible").group(baseGroup, Group.Scan).index() diff --git a/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt b/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt index 97a448780..0efcda8b6 100644 --- a/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt +++ b/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt @@ -21,11 +21,11 @@ import baritone.api.BaritoneAPI import baritone.api.IBaritone import baritone.api.Settings import baritone.api.pathing.goals.Goal +import com.lambda.config.AutomationConfig import com.lambda.config.Configurable import com.lambda.config.configurations.LambdaConfig import com.lambda.config.groups.RotationSettings import com.lambda.context.Automated -import com.lambda.config.AutomationConfig import com.lambda.util.BlockUtils.blockPos import com.lambda.util.NamedEnum import net.fabricmc.loader.api.FabricLoader @@ -352,8 +352,10 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf * Whether Baritone is active (pathing, calculating goal, etc.) */ val isActive: Boolean - get() = isBaritoneLoaded && (primary?.customGoalProcess?.isActive == true || primary?.pathingBehavior?.isPathing == true || primary?.pathingControlManager?.mostRecentInControl() - ?.orElse(null)?.isActive == true) + get() = isBaritoneLoaded && + (primary?.customGoalProcess?.isActive == true || + primary?.pathingBehavior?.isPathing == true || + primary?.pathingControlManager?.mostRecentInControl()?.orElse(null)?.isActive == true) /** * Sets the current Baritone goal and starts pathing diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt index 234a80c45..97d99604e 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt @@ -273,7 +273,7 @@ class InteractSim private constructor(simInfo: InteractSimInfo) } private suspend fun AutomatedSafeContext.testPlaceState(context: ItemPlacementContext): BlockState? { - val resultState = context.stack.blockItem.getPlacementState(context) + val resultState = (context.stack.blockItem ?: return null).getPlacementState(context) ?: run { handleEntityBlockage(context) return null @@ -287,7 +287,7 @@ class InteractSim private constructor(simInfo: InteractSimInfo) private suspend fun AutomatedSafeContext.handleEntityBlockage(context: ItemPlacementContext): List { val pos = context.blockPos - val theoreticalState = context.stack.blockItem.block.getPlacementState(context) + val theoreticalState = (context.stack.blockItem ?: return emptyList()).block.getPlacementState(context) ?: return emptyList() val collisionShape = theoreticalState.getCollisionShape( diff --git a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index 19c5d0e4e..3bbfcee78 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -34,7 +34,7 @@ import net.minecraft.state.property.Property import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -sealed class TargetState() : StateMatcher { +sealed class TargetState : StateMatcher { data object Empty : TargetState() { override fun toString() = "Empty" @@ -165,8 +165,7 @@ sealed class TargetState() : StateMatcher { } data class Stack(val itemStack: ItemStack) : TargetState() { - private val startStack: ItemStack = itemStack.copy() - override fun toString() = "Stack of ${startStack.item.name.string.capitalize()}" + override fun toString() = "Stack of ${itemStack.item.name.string.capitalize()}" private val block = itemStack.item.block diff --git a/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt b/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt index e9b7b02df..60bcc6386 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt @@ -256,7 +256,7 @@ object InteractManager : Manager( ) { ActionResult.PASS } else { - val item = itemStack.blockItem + val item = itemStack.blockItem ?: return ActionResult.FAIL place(interactContext, request, hand, hitResult, item, ItemPlacementContext(context)) } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 05a408244..295accd74 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -37,6 +37,7 @@ import net.minecraft.block.entity.ChestBlockEntity import net.minecraft.block.entity.EnderChestBlockEntity import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.ScreenHandlerType +import net.minecraft.screen.slot.Slot // ToDo: Make this a Configurable to save container caches. Should use a cached region based storage system. object ContainerManager : Loadable { @@ -111,9 +112,11 @@ object ContainerManager : Loadable { containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection ): MaterialContainer? = findContainersWithMaterial(containerSelection).firstOrNull() - context(_: AutomatedSafeContext) - fun findContainerWithSpace(selection: StackSelection): MaterialContainer? = - findContainersWithSpace(selection).firstOrNull() + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findContainerWithSpace( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ): MaterialContainer? = + findContainersWithSpace(containerSelection).firstOrNull() context(automated: Automated, safeContext: SafeContext) fun StackSelection.findContainersWithMaterial( @@ -125,13 +128,21 @@ object ContainerManager : Loadable { .sortedWith(automated.inventoryConfig.providerPriority.materialComparator(this)) context(automatedSafeContext: AutomatedSafeContext) - fun findContainersWithSpace( - selection: StackSelection, + fun StackSelection.findContainersWithSpace( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection ): List = containers() - .filter { it.spaceAvailable(selection) >= selection.count } - .filter { automatedSafeContext.inventoryConfig.containerSelection.matches(it) } - .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.spaceComparator(selection)) + .filter { containerSelection.matches(it) } + .filter { it.spaceAvailable(this) >= count } + .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.spaceComparator(this)) + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findSlotsWithMaterial( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ): List = + findContainersWithMaterial(containerSelection) + .flatMap { filterSlots(it.slots) } + context(automatedSafeContext: AutomatedSafeContext) fun findDisposable() = containers().find { container -> diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt index e7fa989db..291cbbaa2 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt @@ -17,10 +17,11 @@ package com.lambda.interaction.material.container -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.task.Task +import com.lambda.task.TaskGenerator interface ExternalContainer { - context(_: Automated) - fun access(): Task<*> + context(_: AutomatedSafeContext) + fun accessThen(exitAfter: Boolean = true, taskGenerator: TaskGenerator): Task<*>? } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 1176e24ea..dfdedf31e 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -60,9 +60,9 @@ abstract class MaterialContainer( }.thenByDescending { it.stack.item in automated.inventoryConfig.disposables }.thenByDescending { - it.stack.item.components.contains(DataComponentTypes.TOOL) + !it.stack.item.components.contains(DataComponentTypes.TOOL) }.thenByDescending { - it.stack.item.components.contains(DataComponentTypes.FOOD) + !it.stack.item.components.contains(DataComponentTypes.FOOD) }.thenByDescending { it.stack.isStackable } @@ -130,7 +130,7 @@ abstract class MaterialContainer( fun transfer(stackSelection: StackSelection, destination: MaterialContainer): Boolean = with(automatedSafeContext) { val fromSlot = getSlot(stackSelection) ?: return false - val toSlot = destination.getReplaceSlot() ?: return false + val toSlot = destination.getReplaceableSlot() ?: return false return inventoryRequest { if (swapMethodPriority > destination.swapMethodPriority) transfer(fromSlot, toSlot) else with(destination) { transfer(toSlot, fromSlot) } @@ -163,7 +163,7 @@ abstract class MaterialContainer( matchingStacks(selection).spaceLeft + stacks.empty * selection.stackSize context(_: AutomatedSafeContext) - open fun getReplaceSlot() = slots.sortedWith(replaceSorter).firstOrNull() + open fun getReplaceableSlot() = slots.sortedWith(replaceSorter).firstOrNull() context(_: SafeContext) open fun getSlot(stackSelection: StackSelection): Slot? = diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt index 424549adb..2bc6f264d 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt @@ -17,11 +17,13 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.task.Task +import com.lambda.task.TaskGenerator import com.lambda.task.tasks.OpenContainerTask import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText @@ -55,6 +57,11 @@ data class ChestContainer( } } - context(automated: Automated) - override fun access() = OpenContainerTask(blockPos, automated) + context(automatedSafeContext: AutomatedSafeContext) + override fun accessThen(exitAfter: Boolean, taskGenerator: TaskGenerator): Task<*> = + OpenContainerTask(blockPos, automatedSafeContext).then { + taskGenerator.invoke(automatedSafeContext, Unit).finally { + if (exitAfter) automatedSafeContext.player.closeScreen() + } + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt index f93a6b672..116156449 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt @@ -17,16 +17,17 @@ package com.lambda.interaction.material.container.containers -import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer import com.lambda.util.text.buildText import com.lambda.util.text.literal +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.SingleStackInventory import net.minecraft.item.ItemStack +import net.minecraft.screen.PlayerScreenHandler import net.minecraft.screen.slot.Slot data object CreativeContainer : MaterialContainer(Rank.Creative) { @@ -61,9 +62,13 @@ data object CreativeContainer : MaterialContainer(Rank.Creative) { context(safeContext: SafeContext) override fun materialAvailable(selection: StackSelection): Int = - if (safeContext.player.isCreative && selection.optimalStack != null) Int.MAX_VALUE else 0 + if (safeContext.player.isCreative && correctScreenHandler && selection.optimalStack != null) Int.MAX_VALUE else 0 - context(_: SafeContext) + context(safeContext: SafeContext) override fun spaceAvailable(selection: StackSelection): Int = - if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 + if (safeContext.player.isCreative && correctScreenHandler && selection.optimalStack != null) Int.MAX_VALUE else 0 + + context(safeContext: SafeContext) + private val correctScreenHandler + get() = safeContext.player.currentScreenHandler is PlayerScreenHandler || safeContext.player.currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt index 6b852f1af..a0bd695eb 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt @@ -17,13 +17,15 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ContainerManager.findSlotsWithMaterial import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.tasks.AcquireMaterialTask.Companion.acquire +import com.lambda.task.TaskGenerator +import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock import com.lambda.task.tasks.OpenContainerTask import com.lambda.task.tasks.PlaceContainerTask import com.lambda.util.extension.containerSlots @@ -32,6 +34,7 @@ import com.lambda.util.text.literal import net.minecraft.block.entity.EnderChestBlockEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items +import net.minecraft.util.math.BlockPos object EnderChestContainer : MaterialContainer(Rank.EnderChest), ExternalContainer { context(safeContext: SafeContext) @@ -44,12 +47,22 @@ object EnderChestContainer : MaterialContainer(Rank.EnderChest), ExternalContain override val description = buildText { literal("Ender Chest") } - context(automated: Automated) - override fun access() = - automated.acquire { Items.ENDER_CHEST.select() }.thenOrNull { selection -> - val slot = selection.filterSlots(HotbarContainer.slots).firstOrNull() ?: return@thenOrNull FailureTask("Ender Chest not found in hotbar after transfer.") - PlaceContainerTask(slot, automated).finally { pos -> - OpenContainerTask(pos, automated) + private var placePos = BlockPos.ORIGIN + + context(automatedSafeContext: AutomatedSafeContext) + override fun accessThen(exitAfter: Boolean, taskGenerator: TaskGenerator) = + Items.ENDER_CHEST + .select() + .findSlotsWithMaterial() + .firstOrNull()?.let { slot -> + PlaceContainerTask(slot, automatedSafeContext).then { pos -> + placePos = pos + OpenContainerTask(pos, automatedSafeContext).then { + taskGenerator.invoke(automatedSafeContext, Unit).thenOrNull { + if (exitAfter) automatedSafeContext.breakAndCollectBlock(placePos, lifeMaintenance = false) + else null + } + } + } } - } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt index d0134181e..12b57dfa9 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt @@ -41,6 +41,6 @@ object HotbarContainer : MaterialContainer(Rank.Hotbar) { context(safeContext: SafeContext) override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { - swap(toSlot.id, safeContext.player.hotbarSlots.indexOf(toSlot)) + swap(toSlot.id, safeContext.player.hotbarSlots.indexOf(fromHere)) } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt index cd775656d..bacee449b 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt @@ -17,9 +17,15 @@ package com.lambda.interaction.material.container.containers +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.task.TaskGenerator +import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.task.tasks.PlaceContainerTask import com.lambda.threading.runSafe import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText @@ -28,12 +34,13 @@ import com.lambda.util.text.literal import net.minecraft.block.entity.ShulkerBoxBlockEntity import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot +import net.minecraft.util.math.BlockPos data class ShulkerBoxContainer( override var stacks: List, val containedIn: MaterialContainer, val shulkerSlot: Slot, -) : MaterialContainer(Rank.ShulkerBox) { +) : MaterialContainer(Rank.ShulkerBox), ExternalContainer { context(safeContext: SafeContext) override val slots get(): List = @@ -52,4 +59,18 @@ data class ShulkerBoxContainer( context(_: SafeContext) private val slotInContainer: Int get() = containedIn.slots.indexOf(shulkerSlot) -} + + private var placePos = BlockPos.ORIGIN + + context(automatedSafeContext: AutomatedSafeContext) + override fun accessThen(exitAfter: Boolean, taskGenerator: TaskGenerator) = + PlaceContainerTask(shulkerSlot, automatedSafeContext).then { pos -> + placePos = pos + OpenContainerTask(pos, automatedSafeContext).then { + taskGenerator.invoke(automatedSafeContext, Unit).thenOrNull { + if (exitAfter) automatedSafeContext.breakAndCollectBlock(placePos) + else null + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index ff0c1675e..70ed2ec50 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -262,11 +262,10 @@ class BuildTask private constructor( fun Automated.breakAndCollectBlock( blockPos: BlockPos, finishOnDone: Boolean = true, - collectDrops: Boolean = true, lifeMaintenance: Boolean = false ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, lifeMaintenance, this + finishOnDone, true, lifeMaintenance, this ) } } diff --git a/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt index 6c52b7fd1..8121f76bb 100644 --- a/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt @@ -29,53 +29,73 @@ import com.lambda.task.Task import com.lambda.threading.runSafeAutomated class ContainerTransferTask( - private val fromContainer: MaterialContainer, - private val destination: MaterialContainer, + private var fromContainer: MaterialContainer, + private val toContainer: MaterialContainer, private val stackSelection: StackSelection, automated: Automated, private val failIfNoMaterial: Boolean = false ) : Task(), Automated by automated { - override val name = "Transferring $stackSelection from $fromContainer to $destination" + override val name = "Transferring $stackSelection from $fromContainer to $toContainer" + + private var delegateTask: Task<*>? = null init { listen { + if (delegateTask?.isCompleted == true) { + success() + return@listen + } runSafeAutomated { + val fromExternal = fromContainer as? ExternalContainer val slots = fromContainer.slots - val toSlots = destination.slots - if (fromContainer is ExternalContainer && slots.isEmpty()) { - if (destination is ExternalContainer && toSlots.isEmpty()) { - fromContainer.transferByTask(stackSelection, InventoryContainer).then { - InventoryContainer.transferByTask(stackSelection, destination).finally { success() } - } + val toSlots = toContainer.slots + fromExternal.takeIf { slots.isEmpty() }?.let { fromExternal -> + if (toContainer is ExternalContainer && toSlots.isEmpty()) { + fromContainer.transferByTask(stackSelection, InventoryContainer).finally { + fromContainer = InventoryContainer + }.execute(this@ContainerTransferTask) + return@listen } - fromContainer.access().execute(this@ContainerTransferTask).then { - fromContainer.transferByTask(stackSelection, destination).finally { success() } + delegateTask = fromExternal.accessThen { + fromContainer.transferByTask(stackSelection, toContainer) + }?.execute(this@ContainerTransferTask) ?: run { + checkFail() + return@listen } return@listen - } else if (destination is ExternalContainer && toSlots.isEmpty()) { - destination.access().execute(this@ContainerTransferTask).then { - fromContainer.transferByTask(stackSelection, destination).finally { success() } + } + if (toContainer is ExternalContainer && toSlots.isEmpty()) { + delegateTask = toContainer.accessThen { + fromContainer.transferByTask(stackSelection, toContainer) + }?.execute(this@ContainerTransferTask) ?: run { + checkFail() + return@listen } return@listen } fromContainer.getSlot(stackSelection)?.let { fromSlot -> - destination.getReplaceSlot()?.let { toSlot -> + toContainer.getReplaceableSlot()?.let { toSlot -> inventoryRequest { - if (fromContainer.swapMethodPriority > destination.swapMethodPriority) + if (fromContainer.swapMethodPriority > toContainer.swapMethodPriority) with(fromContainer) { transfer(fromSlot, toSlot) } - else with(destination) { transfer(toSlot, toSlot) } + else with(toContainer) { transfer(toSlot, fromSlot) } onComplete { success() } }.submit() return@listen } } - if (failIfNoMaterial) { - failure("$stackSelection not found.") - return@listen - } + checkFail() } } } + + + private fun checkFail(): Boolean = + failIfNoMaterial.also { + failure(NoMaterialAccessException(stackSelection)) + } + + private class NoMaterialAccessException(stackSelection: StackSelection) : IllegalStateException("Unable to access $stackSelection.") } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt b/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt index 1554b89cf..734ae9d05 100644 --- a/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt @@ -17,10 +17,12 @@ package com.lambda.task.tasks +import baritone.api.pathing.goals.GoalNear import com.lambda.context.Automated import com.lambda.event.events.InventoryEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.BaritoneManager import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest import com.lambda.interaction.managers.rotating.visibilty.lookAtBlock import com.lambda.task.Task @@ -44,9 +46,10 @@ class OpenContainerTask @Ta5kBuilder constructor( private var inScope = 0 enum class State { - Scoping, Opening, SlotLoading; + Pathing, Scoping, Opening, SlotLoading; fun description(inScope: Int) = when (this) { + Pathing -> "Pathing closer" Scoping -> "Waiting for scope ($inScope)" Opening -> "Opening container" SlotLoading -> "Waiting for slots to load" @@ -79,9 +82,14 @@ class OpenContainerTask @Ta5kBuilder constructor( } listen { - if (containerState != State.Scoping) return@listen - - val checkedHit = runSafeAutomated { lookAtBlock(blockPos, sides) } ?: return@listen + if (containerState != State.Scoping && containerState != State.Pathing) return@listen + + val checkedHit = runSafeAutomated { lookAtBlock(blockPos, sides) } + ?: run { + containerState = State.Pathing + if (!BaritoneManager.isActive) BaritoneManager.setGoalAndPath(GoalNear(blockPos, 3)) + return@listen + } if (interactConfig.rotate && !rotationRequest { rotation(checkedHit.rotation) }.submit().done) return@listen interaction.interactBlock(player, Hand.MAIN_HAND, checkedHit.hit.blockResult ?: return@listen) diff --git a/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt b/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt index 22e52c979..5cef9c8fe 100644 --- a/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt @@ -44,8 +44,7 @@ class PlaceContainerTask @Ta5kBuilder constructor( val slot: Slot, automated: Automated ) : Task(), Automated by automated { - private val startStack: ItemStack = slot.stack.copy() - override val name: String get() = "Placing container ${startStack.name.string}" + override val name: String get() = "Placing container ${slot.stack.name.string}" override fun SafeContext.onStart() { val results = runSafeAutomated { @@ -54,25 +53,25 @@ class PlaceContainerTask @Ta5kBuilder constructor( .asSequence() .filter { !ManagerUtils.isPosBlocked(it) } .flatMap { - it.toStructure(TargetState.Stack(startStack)) + it.toStructure(TargetState.Stack(slot.stack)) .simulate() } } val options = results.filterIsInstance().filter { - canBeOpened(startStack, it.pos, it.context.hitResult.side) + canBeOpened(slot.stack, it.pos, it.context.hitResult.side) } + results.filterIsInstance() val containerPosition = options.filter { // ToDo: Check based on if we can move the player close enough rather than y level once the custom pathfinder is merged it.pos.y == player.blockPos.y }.minByOrNull { it.pos distSq player.pos }?.pos ?: run { - failure("Couldn't find a valid container placement position for ${startStack.name.string}") + failure("Couldn't find a valid container placement position for ${slot.stack.name.string}") return@onStart } containerPosition - .toStructure(TargetState.Stack(startStack)) + .toStructure(TargetState.Stack(slot.stack)) .toBlueprint() .build(finishOnDone = true, collectDrops = false) .finally { success(containerPosition) } diff --git a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index b1d4db4ba..1d60c10ec 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -122,7 +122,7 @@ object ItemUtils { ) val Item.block: Block get() = Block.getBlockFromItem(this) - val ItemStack.blockItem: BlockItem get() = (item as? BlockItem ?: Items.AIR) as BlockItem + val ItemStack.blockItem get() = item as? BlockItem val Item.nutrition: Int get() = components.get(DataComponentTypes.FOOD)?.nutrition ?: 0 From 29242725ed61c83c9f900e18853302a2cec5b294 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Thu, 8 Jan 2026 13:26:35 +0000 Subject: [PATCH 4/4] small cleanup --- .../material/container/ContainerManager.kt | 21 +++++++++++-------- .../material/container/MaterialContainer.kt | 2 ++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 295accd74..594ac5efe 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -17,7 +17,6 @@ package com.lambda.interaction.material.container -import com.lambda.context.Automated import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.core.Loadable @@ -113,19 +112,18 @@ object ContainerManager : Loadable { ): MaterialContainer? = findContainersWithMaterial(containerSelection).firstOrNull() context(automatedSafeContext: AutomatedSafeContext) - fun StackSelection.findContainerWithSpace( - containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection - ): MaterialContainer? = - findContainersWithSpace(containerSelection).firstOrNull() - - context(automated: Automated, safeContext: SafeContext) fun StackSelection.findContainersWithMaterial( - containerSelection: ContainerSelection = automated.inventoryConfig.containerSelection, + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection, ): List = containers() .filter { containerSelection.matches(it) } .filter { it.materialAvailable(this) >= count } - .sortedWith(automated.inventoryConfig.providerPriority.materialComparator(this)) + .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.materialComparator(this)) + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findContainerWithSpace( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ): MaterialContainer? = findContainersWithSpace(containerSelection).firstOrNull() context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.findContainersWithSpace( @@ -136,6 +134,11 @@ object ContainerManager : Loadable { .filter { it.spaceAvailable(this) >= count } .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.spaceComparator(this)) + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findSlotWithMaterial( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ) = findSlotsWithMaterial(containerSelection).firstOrNull() + context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.findSlotsWithMaterial( containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection diff --git a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index dfdedf31e..dd4ce6cda 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -63,6 +63,8 @@ abstract class MaterialContainer( !it.stack.item.components.contains(DataComponentTypes.TOOL) }.thenByDescending { !it.stack.item.components.contains(DataComponentTypes.FOOD) + }.thenByDescending { + !it.stack.item.components.contains(DataComponentTypes.CONSUMABLE) }.thenByDescending { it.stack.isStackable }