From 23a2a911c1d460d8ef56f636ad5fd2c41162fee2 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 19 Sep 2025 19:13:29 +0200 Subject: [PATCH 1/7] AutoEat prototype --- .../com/lambda/config/groups/BreakSettings.kt | 8 +- .../com/lambda/config/groups/EatConfig.kt | 38 ++++++++ .../com/lambda/config/groups/EatSettings.kt | 42 +++++++++ .../construction/result/BuildResult.kt | 16 ++-- .../construction/simulation/BuildSimulator.kt | 43 ++++++--- .../construction/verify/TargetState.kt | 6 +- .../interaction/material/StackSelection.kt | 48 +++++----- .../material/container/ContainerManager.kt | 4 +- .../request/breaking/BreakConfig.kt | 7 ++ .../module/modules/client/TaskFlowModule.kt | 3 + .../lambda/module/modules/movement/Speed.kt | 2 +- .../lambda/module/modules/player/AutoEat.kt | 75 ++++++++++++++++ .../module/modules/player/HighwayTools.kt | 8 ++ .../lambda/module/modules/render/BlockESP.kt | 5 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 42 +++++++-- .../kotlin/com/lambda/task/tasks/EatTask.kt | 89 +++++++++++++++++++ src/main/kotlin/com/lambda/util/BlockUtils.kt | 16 ++-- 17 files changed, 383 insertions(+), 69 deletions(-) create mode 100644 src/main/kotlin/com/lambda/config/groups/EatConfig.kt create mode 100644 src/main/kotlin/com/lambda/config/groups/EatSettings.kt create mode 100644 src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt create mode 100644 src/main/kotlin/com/lambda/task/tasks/EatTask.kt diff --git a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt index 03fc24a1e..c5edd44de 100644 --- a/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/BreakSettings.kt @@ -80,10 +80,16 @@ class BreakSettings( override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, description = "Blocks that wont be broken", visibility = vis).group(groupPath, Group.General) // Tool - override val suitableToolsOnly by c.setting("Suitable Tools Only", false, "Places a restriction to only use tools suitable for the given block", visibility = vis).group(groupPath, Group.General) + override val suitableToolsOnly by c.setting("Suitable Tools Only", true, "Only use tools suitable for the given block (will get the item drop)", visibility = vis).group(groupPath, Group.General) override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks", visibility = vis).group(groupPath, Group.General) override val forceFortunePickaxe by c.setting("Force Fortune Pickaxe", false, "Force fortune pickaxe when breaking blocks", visibility = vis).group(groupPath, Group.General) override val minFortuneLevel by c.setting("Min Fortune Level", 1, 1..3, 1, "The minimum fortune level to use") { vis() && forceFortunePickaxe }.group(groupPath, Group.General) + override val useWoodenTools by c.setting("Use Wooden Tools", true, "Use wooden tools when breaking blocks", visibility = vis).group(groupPath, Group.General) + override val useStoneTools by c.setting("Use Stone Tools", true, "Use stone tools when breaking blocks", visibility = vis).group(groupPath, Group.General) + override val useIronTools by c.setting("Use Iron Tools", true, "Use iron tools when breaking blocks", visibility = vis).group(groupPath, Group.General) + override val useDiamondTools by c.setting("Use Diamond Tools", true, "Use diamond tools when breaking blocks", visibility = vis).group(groupPath, Group.General) + override val useGoldTools by c.setting("Use Gold Tools", true, "Use gold tools when breaking blocks", visibility = vis).group(groupPath, Group.General) + override val useNetheriteTools by c.setting("Use Netherite Tools", true, "Use netherite tools when breaking blocks", visibility = vis).group(groupPath, Group.General) // Cosmetics override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds", visibility = vis).group(groupPath, Group.Cosmetic) diff --git a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt new file mode 100644 index 000000000..7b4d7871f --- /dev/null +++ b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt @@ -0,0 +1,38 @@ +/* + * 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.config.groups + +import com.lambda.util.NamedEnum +import net.minecraft.item.Item + +interface EatConfig { + val eatFood: Boolean + val minFoodLevel: Int + val eatUntilFull: Boolean + val eatOnFire: Boolean + val eatHeal: Boolean + + val selectionMode: SelectionMode + val whitelist: List + val blacklist: List + + enum class SelectionMode(override val displayName: String): NamedEnum { + Whitelist("Whitelist"), + Blacklist("Blacklist") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/groups/EatSettings.kt b/src/main/kotlin/com/lambda/config/groups/EatSettings.kt new file mode 100644 index 000000000..0168d7f0e --- /dev/null +++ b/src/main/kotlin/com/lambda/config/groups/EatSettings.kt @@ -0,0 +1,42 @@ +/* + * 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.config.groups + +import com.lambda.config.Configurable +import com.lambda.event.events.TickEvent +import com.lambda.interaction.request.hotbar.HotbarConfig +import com.lambda.util.NamedEnum +import net.minecraft.item.Item +import net.minecraft.item.Items + +class EatSettings( + c: Configurable, + baseGroup: NamedEnum, + vis: () -> Boolean = { true } +) : EatConfig { + val defaultWhitelist = listOf(Items.GOLDEN_CARROT) + + override val eatFood by c.setting("Eat Food", true, "Whether food should be eaten", vis).group(baseGroup) + override val eatUntilFull by c.setting("Eat Until Full", false, "Eat until the food level is full") { vis() && eatFood }.group(baseGroup) + override val minFoodLevel by c.setting("Minimum Food Level", 6, 0..20, 1, "The minimum food level to eat food", " food level") { vis() && eatFood }.group(baseGroup) + override val eatOnFire by c.setting("Eat On Fire", false, "Eat when you are on fire") { vis() && eatFood }.group(baseGroup) + override val eatHeal by c.setting("Eat Heal", false, "Eat healing food when you are below the minimum health level") { vis() && eatFood }.group(baseGroup) + override val selectionMode by c.setting("Selection Mode", EatConfig.SelectionMode.Whitelist, "The selection mode for eating") { vis() && eatFood }.group(baseGroup) + override val whitelist by c.setting("Whitelist", defaultWhitelist, defaultWhitelist, "The whitelist of items to eat") { vis() && eatFood }.group(baseGroup) + override val blacklist by c.setting("Blacklist", listOf(), listOf(), "The blacklist of items to eat") { vis() && eatFood }.group(baseGroup) +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 1712db18c..bd00f9e5c 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -92,7 +92,7 @@ abstract class BuildResult : ComparableResult, Nameable { } /** - * The player has no permission to interact with the block. (E.g.: Spectator mode) + * The player has no permission to interact with the block. (E.g.: Adventure mode) * @param blockPos The position of the block that is restricted. */ data class Restricted( @@ -204,14 +204,12 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = - neededSelection.let { selection -> - selection.transfer(MainHandContainer, inventory) - ?: MaterialContainer.AwaitItemTask( - "Couldn't find $neededSelection anywhere.", - selection, - inventory - ) - } + neededSelection.transfer(MainHandContainer, inventory) + ?: MaterialContainer.AwaitItemTask( + "Couldn't find $neededSelection anywhere.", + neededSelection, + inventory + ) override fun ShapeBuilder.buildRenderer() { box(blockPos, color, color) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 2784526bc..d2110728d 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -61,6 +61,7 @@ import com.lambda.util.BlockUtils.hasFluid import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.isEmpty import com.lambda.util.BlockUtils.isNotEmpty +import com.lambda.util.Communication.info import com.lambda.util.Communication.warn import com.lambda.util.math.distSq import com.lambda.util.math.vec3d @@ -85,6 +86,13 @@ import net.minecraft.item.Item import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext +import net.minecraft.item.Items +import net.minecraft.registry.tag.ItemTags.DIAMOND_TOOL_MATERIALS +import net.minecraft.registry.tag.ItemTags.GOLD_TOOL_MATERIALS +import net.minecraft.registry.tag.ItemTags.IRON_TOOL_MATERIALS +import net.minecraft.registry.tag.ItemTags.NETHERITE_TOOL_MATERIALS +import net.minecraft.registry.tag.ItemTags.STONE_TOOL_MATERIALS +import net.minecraft.registry.tag.ItemTags.WOODEN_TOOL_MATERIALS import net.minecraft.state.property.Properties import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult @@ -396,9 +404,8 @@ object BuildSimulator { if (!currentState.isReplaceable && !statePromoting) return acc preProcessing.sides.forEach { neighbor -> - val hitPos = if (!place.airPlace.isEnabled && (currentState.isEmpty || statePromoting)) - pos.offset(neighbor) - else pos + val hitPos = if (!place.airPlace.isEnabled && (currentState.isAir || statePromoting)) + pos.offset(neighbor) else pos val hitSide = neighbor.opposite val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> @@ -644,7 +651,7 @@ object BuildSimulator { val state = blockState(pos) /* is a block that will be destroyed by breaking adjacent blocks */ - if (!breaking.breakWeakBlocks && state.block.hardness == 0f && state.isNotEmpty) { + if (!breaking.breakWeakBlocks && state.block.hardness == 0f && !state.isAir && state.isNotEmpty) { acc.add(BuildResult.Ignored(pos)) return acc } @@ -831,13 +838,24 @@ object BuildSimulator { state.calcItemBlockBreakingDelta(player, world, pos, it) } ) { - run { - if (breaking.suitableToolsOnly) isSuitableForBreaking(state) - else StackSelection.EVERYTHING - } and if (breaking.forceSilkTouch) { - hasEnchantment(Enchantments.AQUA_AFFINITY) - } else if (breaking.forceFortunePickaxe) { - hasEnchantment(Enchantments.FORTUNE, breaking.minFortuneLevel) + isTool() and if (breaking.suitableToolsOnly) { + isSuitableForBreaking(state) + } else StackSelection.EVERYTHING and if (breaking.forceSilkTouch) { + hasEnchantment(Enchantments.SILK_TOUCH) + } else StackSelection.EVERYTHING and if (breaking.forceFortunePickaxe) { + hasEnchantment(Enchantments.FORTUNE) + } else StackSelection.EVERYTHING and if (!breaking.useWoodenTools) { + hasTag(WOODEN_TOOL_MATERIALS).not() + } else StackSelection.EVERYTHING and if (!breaking.useStoneTools) { + hasTag(STONE_TOOL_MATERIALS).not() + } else StackSelection.EVERYTHING and if (!breaking.useIronTools) { + hasTag(IRON_TOOL_MATERIALS).not() + } else StackSelection.EVERYTHING and if (!breaking.useDiamondTools) { + hasTag(DIAMOND_TOOL_MATERIALS).not() + } else StackSelection.EVERYTHING and if (!breaking.useGoldTools) { + hasTag(GOLD_TOOL_MATERIALS).not() + } else StackSelection.EVERYTHING and if (!breaking.useNetheriteTools) { + hasTag(NETHERITE_TOOL_MATERIALS).not() } else StackSelection.EVERYTHING } @@ -851,7 +869,8 @@ object BuildSimulator { return acc } - val swapStack = swapCandidates.map { it.matchingStacks(stackSelection) } + val swapStack = swapCandidates + .map { it.matchingStacks(stackSelection) } .asSequence() .flatten() .let { containerStacks -> 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 cbc8b294a..8d1fe9a53 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -46,8 +46,7 @@ sealed class TargetState(val type: Type) : StateMatcher { pos: BlockPos, world: ClientWorld, ignoredProperties: Collection> - ) = - state.isEmpty + ) = state.isEmpty override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack = ItemStack.EMPTY @@ -63,8 +62,7 @@ sealed class TargetState(val type: Type) : StateMatcher { pos: BlockPos, world: ClientWorld, ignoredProperties: Collection> - ) = - state.isAir + ) = state.isAir override fun getStack(world: ClientWorld, pos: BlockPos, inventory: InventoryConfig): ItemStack = ItemStack.EMPTY diff --git a/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 9d6de926e..188537a98 100644 --- a/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -17,13 +17,18 @@ package com.lambda.interaction.material +import com.lambda.interaction.material.StackSelection.Companion.StackSelectionDsl import com.lambda.util.EnchantmentUtils.getEnchantment import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.component.ComponentType +import net.minecraft.component.DataComponentTypes import net.minecraft.enchantment.Enchantment import net.minecraft.item.Item import net.minecraft.item.ItemStack +import net.minecraft.item.ToolMaterial +import net.minecraft.item.consume.UseAction import net.minecraft.registry.RegistryKey import net.minecraft.registry.tag.TagKey import net.minecraft.screen.slot.Slot @@ -32,6 +37,7 @@ import kotlin.reflect.KClass /** * [StackSelection] is a class that holds a predicate for matching [ItemStack]s. */ +@StackSelectionDsl class StackSelection { var selector: (ItemStack) -> Boolean = EVERYTHING var comparator: Comparator = NO_COMPARE @@ -53,6 +59,8 @@ class StackSelection { */ fun bestItemMatch(stacks: List): ItemStack? = stacks.minWithOrNull(comparator) + fun matches(stack: ItemStack): Boolean = filterStack(stack) + fun filterStack(stack: ItemStack) = if (inShulkerBox) stack.shulkerBoxContents.any { selector(it) } else selector(stack) @@ -65,34 +73,28 @@ class StackSelection { fun filterSlots(slots: List): List = slots.filter(::filterSlot).sortedWith { slot, slot2 -> comparator.compare(slot.stack, slot2.stack) } - @StackSelectionDsl fun > sortBy(selector: (ItemStack) -> R?): StackSelection = apply { comparator = compareBy(selector) } - @StackSelectionDsl fun > sortByDescending(selector: (ItemStack) -> R?): StackSelection = apply { comparator = compareByDescending(selector) } - @StackSelectionDsl fun > thenBy(selector: (ItemStack) -> R?): StackSelection = apply { check(comparator != NO_COMPARE) { "No comparator specified" } comparator = comparator.thenBy(selector) } - @StackSelectionDsl fun > thenByDescending(selector: (ItemStack) -> R?): StackSelection = apply { check(comparator != NO_COMPARE) { "No comparator specified" } comparator = comparator.thenByDescending(selector) } - @StackSelectionDsl fun sortWith(custom: Comparator): StackSelection = apply { comparator = custom } - @StackSelectionDsl fun reversed(): StackSelection = apply { comparator = comparator.reversed() } @@ -131,7 +133,6 @@ class StackSelection { * @param item The [Item] to be matched. * @return A predicate that matches the [Item]. */ - @StackSelectionDsl fun isItem(item: Item): (ItemStack) -> Boolean { this.item = item return { it.item == item } @@ -143,32 +144,35 @@ class StackSelection { * @param items The collection of `Item` instances to match against. * @return A predicate that checks if the `ItemStack`'s item is contained in the provided collection. */ - @StackSelectionDsl fun isOneOfItems(items: Collection): (ItemStack) -> Boolean = { it.item in items } + fun isNoneOfItems(items: Collection): (ItemStack) -> Boolean = { it.item !in items } + /** * Returns a predicate that checks if a given `ItemStack` exists within the provided collection of `ItemStack`s. * * @param stacks A collection of `ItemStack` instances to be checked against. * @return A predicate that evaluates to `true` if the given `ItemStack` is within the specified collection, otherwise `false`. */ - @StackSelectionDsl fun isOneOfStacks(stacks: Collection): (ItemStack) -> Boolean = { it in stacks } - @StackSelectionDsl fun isSuitableForBreaking(blockState: BlockState): (ItemStack) -> Boolean = { it.isSuitableFor(blockState) } - @StackSelectionDsl - fun isTag(tag: TagKey): (ItemStack) -> Boolean { - return { it.isIn(tag) } - } + fun hasTag(tag: TagKey): (ItemStack) -> Boolean = { it.isIn(tag) } + + fun hasUseAction(action: UseAction): (ItemStack) -> Boolean = { it.useAction == action } + + fun isTool(): (ItemStack) -> Boolean = hasComponent(DataComponentTypes.TOOL) + + fun isFood(): (ItemStack) -> Boolean = hasComponent(DataComponentTypes.FOOD) + + fun hasComponent(type: ComponentType<*>): (ItemStack) -> Boolean = { it.components.contains(type) } /** * [isItem] returns a predicate that matches a specific [Item] instance. * @param T The instance of [Item] to be matched. * @return A predicate that matches the [Item]. */ - @StackSelectionDsl inline fun isItem(): (ItemStack) -> Boolean { itemClass = T::class return { it.item is T } @@ -179,7 +183,6 @@ class StackSelection { * @param block The [Block] to be matched. * @return A predicate that matches the [Block]. */ - @StackSelectionDsl fun isBlock(block: Block): (ItemStack) -> Boolean { item = block.asItem() return { it.item == block.asItem() } @@ -190,7 +193,6 @@ class StackSelection { * @param stack The [ItemStack] to be matched. * @return A predicate that matches the [ItemStack]. */ - @StackSelectionDsl fun isItemStack(stack: ItemStack): (ItemStack) -> Boolean { this.itemStack = stack return { ItemStack.areEqual(it, stack) } @@ -201,7 +203,6 @@ class StackSelection { * @param damage The damage value to be matched. * @return A predicate that matches the damage value. */ - @StackSelectionDsl fun hasDamage(damage: Int): (ItemStack) -> Boolean { this.damage = damage return { it.damage == damage } @@ -213,7 +214,6 @@ class StackSelection { * @param level The level to be matched (if -1 will look for any level above 0). * @return A predicate that matches the [Enchantment] and `level`. */ - @StackSelectionDsl fun hasEnchantment(enchantment: RegistryKey, level: Int = -1): (ItemStack) -> Boolean = { if (level < 0) { it.getEnchantment(enchantment) > 0 @@ -226,7 +226,6 @@ class StackSelection { * Returns the negation of the original predicate. * @return A new predicate that matches if the original predicate does not match. */ - @StackSelectionDsl fun ((ItemStack) -> Boolean).not(): (ItemStack) -> Boolean { return { !this(it) } } @@ -236,7 +235,6 @@ class StackSelection { * @param otherPredicate The second predicate. * @return A new predicate that matches if both inputs predicate match. */ - @StackSelectionDsl infix fun ((ItemStack) -> Boolean).and(otherPredicate: (ItemStack) -> Boolean): (ItemStack) -> Boolean { return { this(it) && otherPredicate(it) } } @@ -246,7 +244,6 @@ class StackSelection { * @param otherPredicate The second predicate. * @return A new predicate that matches if either input predicate matches. */ - @StackSelectionDsl infix fun ((ItemStack) -> Boolean).or(otherPredicate: (ItemStack) -> Boolean): (ItemStack) -> Boolean { return { this(it) || otherPredicate(it) } } @@ -257,6 +254,13 @@ class StackSelection { itemClass?.let { append(it.simpleName) } itemStack?.let { append(it.name.string) } damage?.let { append(" with damage $it") } + when (selector) { + EVERYTHING -> append(" everything") + NOTHING -> append(" nothing") + else -> append(" custom predicate") + } + if (inShulkerBox) append(" in shulker box") + if (comparator != NO_COMPARE) append(" sorted by custom comparator") } companion object { 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 85dfce04a..c022bc7da 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -110,18 +110,18 @@ object ContainerManager : Loadable { containerSelection: ContainerSelection = inventory.containerSelection, ): List = container() - .sortedWith(inventory.providerPriority.materialComparator(this)) .filter { it.materialAvailable(this) >= count } .filter { containerSelection.matches(it) } + .sortedWith(inventory.providerPriority.materialComparator(this)) fun containerWithSpace( selection: StackSelection, inventory: InventoryConfig = TaskFlowModule.inventory, ): List = container() - .sortedWith(inventory.providerPriority.spaceComparator(selection)) .filter { it.spaceAvailable(selection) >= selection.count } .filter { inventory.containerSelection.matches(it) } + .sortedWith(inventory.providerPriority.spaceComparator(selection)) fun findDisposable(inventory: InventoryConfig = TaskFlowModule.inventory) = container().find { container -> inventory.disposables.any { container.materialAvailable(it.asItem().select()) > 0 } diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt index 275944380..798d1357d 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakConfig.kt @@ -63,6 +63,13 @@ interface BreakConfig : RequestConfig { val forceFortunePickaxe: Boolean val minFortuneLevel: Int + val useWoodenTools: Boolean + val useStoneTools: Boolean + val useIronTools: Boolean + val useDiamondTools: Boolean + val useGoldTools: Boolean + val useNetheriteTools: Boolean + val sounds: Boolean val particles: Boolean val breakingTexture: Boolean diff --git a/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt b/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt index 944b343fe..80c7e92e2 100644 --- a/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt +++ b/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt @@ -18,6 +18,7 @@ package com.lambda.module.modules.client import com.lambda.config.groups.BuildSettings +import com.lambda.config.groups.EatSettings import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings @@ -41,6 +42,7 @@ object TaskFlowModule : Module( Interaction("Interaction"), Inventory("Inventory"), Hotbar("Hotbar"), + Eat("Eat"), Debug("Debug") } @@ -49,6 +51,7 @@ object TaskFlowModule : Module( val interaction = InteractionSettings(this, Group.Interaction, InteractionMask.Both) val inventory = InventorySettings(this, Group.Inventory) val hotbar = HotbarSettings(this, Group.Hotbar) + val eat = EatSettings(this, Group.Eat) val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree").group(Group.Debug) val shrinkFactor by setting("Shrink Factor", 0.001, 0.0..1.0, 0.001).group(Group.Debug) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index f0ae41c75..69bb6b243 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -190,7 +190,7 @@ object Speed : Module( || player.isElytraFlying || player.isTouchingWater || player.isInLava - || player.hasVehicle()) return false + || player.isRiding) return false return when (mode) { Mode.GRIM_STRAFE -> !player.input.handledByBaritone diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt new file mode 100644 index 000000000..6df756ac7 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt @@ -0,0 +1,75 @@ +/* + * 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.module.modules.player + +import com.lambda.config.groups.EatSettings +import com.lambda.config.groups.RotationSettings +import com.lambda.context.SafeContext +import com.lambda.event.events.TickEvent +import com.lambda.event.events.UpdateManagerEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.Request.Companion.submit +import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotating.Rotation.Companion.wrap +import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.interaction.request.rotating.visibilty.lookAt +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.task.RootTask.run +import com.lambda.task.tasks.EatTask +import com.lambda.task.tasks.EatTask.Companion.eat +import com.lambda.task.tasks.EatTask.Companion.shouldEat +import com.lambda.util.NamedEnum +import com.lambda.util.math.distSq +import net.minecraft.entity.Entity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.util.math.MathHelper.wrapDegrees +import kotlin.random.Random + +object AutoEat : Module( + name = "AutoEat", + description = "Eats food when you are hungry", + tag = ModuleTag.PLAYER, +) { + private enum class Group(override val displayName: String) : NamedEnum { + FOOD("Food"), + FIRE("Fire"), + HEAL("Heal"), + } + + private val eat = EatSettings(this, Group.FOOD) + + private var eatTask: EatTask? = null + + init { + listen { + if (eatTask != null || !shouldEat(eat)) return@listen + + val task = eat(eat) + task.finally { eatTask = null } + task.run() + eatTask = task + } + + onDisable { + eatTask?.cancel() + eatTask = null + } + } +} diff --git a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 67bc81251..79ad099ce 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -18,6 +18,8 @@ package com.lambda.module.modules.player import com.lambda.config.groups.BuildSettings +import com.lambda.config.groups.EatSettings +import com.lambda.config.groups.HotbarSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings @@ -70,6 +72,8 @@ object HighwayTools : Module( private val rotation = RotationSettings(this, Group.Rotation) private val interact = InteractionSettings(this, Group.Interaction, InteractionMask.Block) private val inventory = InventorySettings(this, Group.Inventory) + private val hotbar = HotbarSettings(this, Group.Hotbar) + private val eat = EatSettings(this, Group.Eat) private var octant = EightWayDirection.NORTH private var distanceMoved = 0 @@ -100,6 +104,8 @@ object HighwayTools : Module( Rotation("Rotation"), Interaction("Interaction"), Inventory("Inventory"), + Hotbar("Hotbar"), + Eat("Eat") } init { @@ -140,6 +146,8 @@ object HighwayTools : Module( rotation = rotation, interact = interact, inventory = inventory, + hotbar = hotbar, + eat = eat, ).run() } diff --git a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index c979b9263..1b45d0030 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -82,11 +82,10 @@ object BlockESP : Module( pos: BlockPos, sides: Int, ) = runSafe { - val shape = outlineShape(state, pos) val blockColor = blockColor(state, pos) - if (drawFaces) filled(shape, if (useBlockColor) blockColor else faceColor, sides) - if (drawOutlines) outline(shape, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) + if (drawFaces) filled(pos, state, if (useBlockColor) blockColor else faceColor, sides) + if (drawOutlines) outline(pos, state, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) } private fun rebuildMesh(ctx: SafeContext, from: Any, to: Any): Unit = esp.rebuild() diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 95d315179..04700964b 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -20,6 +20,7 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.GoalBlock import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig +import com.lambda.config.groups.EatConfig import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent @@ -51,6 +52,10 @@ import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task +import com.lambda.task.tasks.EatTask.Companion.eat +import com.lambda.task.tasks.EatTask.Companion.hasFood +import com.lambda.task.tasks.EatTask.Companion.shouldEat +import com.lambda.util.Communication.info import com.lambda.util.Formatting.string import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots @@ -69,6 +74,7 @@ class BuildTask @Ta5kBuilder constructor( private val interactionConfig: InteractionConfig = TaskFlowModule.interaction, private val inventory: InventoryConfig = TaskFlowModule.inventory, private val hotbar: HotbarConfig = TaskFlowModule.hotbar, + private val eat: EatConfig = TaskFlowModule.eat, ) : Task() { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" @@ -81,6 +87,7 @@ class BuildTask @Ta5kBuilder constructor( private var placements = 0 private var breaks = 0 private val dropsToCollect = mutableSetOf() + var eatTask: EatTask? = null private val onItemDrop: ((item: ItemEntity) -> Unit)? get() = if (collectDrops) { item -> @@ -93,6 +100,18 @@ class BuildTask @Ta5kBuilder constructor( init { listen { + val parentEating = (parent as? BuildTask)?.eatTask != null + when { + !parentEating && eatTask == null && shouldEat(eat) && hasFood(eat, inventory) -> { + eatTask = eat(eat) + eatTask?.finally { + eatTask = null + }?.execute(this@BuildTask) + return@listen + } + eatTask != null -> return@listen + } + if (blueprint is TickingBlueprint) { blueprint.tick() ?: failure("Failed to tick the ticking blueprint") } @@ -204,8 +223,7 @@ class BuildTask @Ta5kBuilder constructor( return@let true } - val noInventorySpace = player.hotbarAndStorage.none { it.isEmpty } - if (noInventorySpace) { + if (player.hotbarAndStorage.none { it.isEmpty }) { val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { it.stack.item.block in TaskFlowModule.inventory.disposables } ?: run { @@ -237,8 +255,10 @@ class BuildTask @Ta5kBuilder constructor( rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interaction, inventory: InventoryConfig = TaskFlowModule.inventory, + hotbar: HotbarConfig = TaskFlowModule.hotbar, + eat: EatConfig = TaskFlowModule.eat, blueprint: () -> Blueprint, - ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) + ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat) @Ta5kBuilder fun Structure.build( @@ -248,7 +268,9 @@ class BuildTask @Ta5kBuilder constructor( rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interaction, inventory: InventoryConfig = TaskFlowModule.inventory, - ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) + hotbar: HotbarConfig = TaskFlowModule.hotbar, + eat: EatConfig = TaskFlowModule.eat, + ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat) @Ta5kBuilder fun Blueprint.build( @@ -258,7 +280,9 @@ class BuildTask @Ta5kBuilder constructor( rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interaction, inventory: InventoryConfig = TaskFlowModule.inventory, - ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory) + hotbar: HotbarConfig = TaskFlowModule.hotbar, + eat: EatConfig = TaskFlowModule.eat, + ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat) @Ta5kBuilder fun breakAndCollectBlock( @@ -269,9 +293,11 @@ class BuildTask @Ta5kBuilder constructor( rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interaction, inventory: InventoryConfig = TaskFlowModule.inventory, + hotbar: HotbarConfig = TaskFlowModule.hotbar, + eat: EatConfig = TaskFlowModule.eat, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact, inventory + finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat ) @Ta5kBuilder @@ -283,9 +309,11 @@ class BuildTask @Ta5kBuilder constructor( rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interaction, inventory: InventoryConfig = TaskFlowModule.inventory, + hotbar: HotbarConfig = TaskFlowModule.hotbar, + eat: EatConfig = TaskFlowModule.eat, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact, inventory + finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat ) } } diff --git a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt new file mode 100644 index 000000000..c801c2acc --- /dev/null +++ b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt @@ -0,0 +1,89 @@ +/* + * 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.task.tasks + +import com.lambda.config.groups.EatConfig +import com.lambda.context.SafeContext +import com.lambda.event.Event +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +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.request.inventory.InventoryConfig +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.task.Task +import net.minecraft.component.DataComponentTypes +import net.minecraft.component.type.FoodComponent +import net.minecraft.item.ItemStack +import net.minecraft.util.ActionResult +import net.minecraft.util.Hand +import java.awt.event.InputEvent + +class EatTask @Ta5kBuilder constructor( + val config: EatConfig = TaskFlowModule.eat, + val inventory: InventoryConfig = TaskFlowModule.inventory, +) : Task() { + override val name: String + get() = "Eating ${eatStack ?: "nothing"}" + + private var eatStack: ItemStack? = null + private val selection get() = foodSelector(config) + + init { + listen { + val nutrition = eatStack?.item?.components?.get(DataComponentTypes.FOOD)?.nutrition ?: 0 + val wouldBeFull = nutrition + player.hungerManager.foodLevel > 20 + if (!config.eatUntilFull && !shouldEat(config) || wouldBeFull) { + interaction.stopUsingItem(player) + success() + return@listen + } + + if (!selection.matches(player.mainHandStack)) { + selection.transfer(MainHandContainer, inventory) + ?.execute(this@EatTask) ?: failure("No food found") + return@listen + } + eatStack = player.mainHandStack + + (interaction.interactItem(player, Hand.MAIN_HAND) as? ActionResult.Success)?.let { + if (it.swingSource == ActionResult.SwingSource.CLIENT) player.swingHand(Hand.MAIN_HAND) + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(Hand.MAIN_HAND) + } + } + } + + companion object { + fun foodSelector(config: EatConfig) = selectStack { + when { + config.selectionMode == EatConfig.SelectionMode.Whitelist -> isOneOfItems(config.whitelist) + else -> isNoneOfItems(config.blacklist) + } and isFood() + } + + @Ta5kBuilder + fun eat(config: EatConfig) = EatTask(config) + + fun SafeContext.shouldEat(config: EatConfig) = + player.hungerManager.foodLevel <= config.minFoodLevel + + fun hasFood(config: EatConfig, inventory: InventoryConfig) = + foodSelector(config).transfer(MainHandContainer, inventory) != null + } +} diff --git a/src/main/kotlin/com/lambda/util/BlockUtils.kt b/src/main/kotlin/com/lambda/util/BlockUtils.kt index 8f936ab13..0125fef78 100644 --- a/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -256,23 +256,23 @@ object BlockUtils { player: PlayerEntity, world: BlockView, blockPos: BlockPos, - item: ItemStack + stack: ItemStack ): Float { val hardness = getHardness(world, blockPos) return if (hardness == -1.0f) 0.0f else { - val harvestMultiplier = if (item.canHarvest(this)) 30 else 100 - player.getItemBlockBreakingSpeed(this, item) / hardness / harvestMultiplier + val harvestMultiplier = if (stack.canHarvest(this)) 30 else 100 + player.getItemBlockBreakingSpeed(this, stack) / hardness / harvestMultiplier } } - fun ItemStack.canHarvest(blockState: BlockState) = - !blockState.isToolRequired || isSuitableFor(blockState) + fun ItemStack.canHarvest(state: BlockState) = + !state.isToolRequired || isSuitableFor(state) fun PlayerEntity.getItemBlockBreakingSpeed( - blockState: BlockState, + state: BlockState, item: ItemStack ): Float { - var speedMultiplier = item.getMiningSpeedMultiplier(blockState) + var speedMultiplier = item.getMiningSpeedMultiplier(state) if (speedMultiplier > 1.0f) { speedMultiplier += item.getEnchantment(Enchantments.EFFICIENCY).let { if (it > 0) (it * it) + 1 @@ -299,7 +299,7 @@ object BlockUtils { speedMultiplier *= getAttributeValue(EntityAttributes.BLOCK_BREAK_SPEED).toFloat() if (isSubmergedIn(FluidTags.WATER)) { getAttributeInstance(EntityAttributes.SUBMERGED_MINING_SPEED)?.let { speed -> - speedMultiplier *= speed.getValue().toFloat() + speedMultiplier *= speed.value.toFloat() } } From 6ebfb1044d7f12a234c1de9a00069fffdd46dd6b Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Sep 2025 03:45:00 +0200 Subject: [PATCH 2/7] Improve prototype --- .../com/lambda/config/groups/EatConfig.kt | 88 +++++++++++++++++-- .../com/lambda/config/groups/EatSettings.kt | 27 +++--- .../construction/simulation/BuildSimulator.kt | 18 ++-- .../interaction/material/StackSelection.kt | 7 +- .../lambda/module/modules/player/AutoEat.kt | 22 +---- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 8 +- .../kotlin/com/lambda/task/tasks/EatTask.kt | 62 +++++++------ .../kotlin/com/lambda/util/item/ItemUtils.kt | 3 + 8 files changed, 153 insertions(+), 82 deletions(-) diff --git a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt index 7b4d7871f..590fed795 100644 --- a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt +++ b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt @@ -17,22 +17,92 @@ package com.lambda.config.groups +import com.lambda.context.SafeContext +import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.StackSelection.Companion.selectStack +import com.lambda.threading.runSafe +import com.lambda.util.Describable import com.lambda.util.NamedEnum +import com.lambda.util.item.ItemUtils.nutrition +import net.minecraft.entity.effect.StatusEffects import net.minecraft.item.Item +import net.minecraft.item.ItemStack interface EatConfig { - val eatFood: Boolean + val eatOnHunger: Boolean val minFoodLevel: Int - val eatUntilFull: Boolean + val nutritiousFood: List + val selectionPriority: SelectionPriority + val saturated: Saturation val eatOnFire: Boolean - val eatHeal: Boolean + val resistanceFood: List + val eatOnDamage: Boolean + val minDamage: Int + val regenerationFood: List + val ignoreBadFood: Boolean + val badFood: List - val selectionMode: SelectionMode - val whitelist: List - val blacklist: List + enum class Saturation( + override val displayName: String, + override val description: String + ): NamedEnum, Describable { + EatSmart("Eat Smart", "Eats until the next food would exceed the hunger limit."), + EatUntilFull("Eat Until Full", "Eats food until the hunger bar is completely full. May waste some food."), + } + + enum class SelectionPriority( + val comparator: Comparator, + override val displayName: String, + override val description: String): NamedEnum, Describable { + LeastNutritious( + compareBy { it.item.nutrition }, + "Least Nutritious", + "Eats food items with the least nutritional value." + ), + MostNutritious( + compareByDescending { it.item.nutrition }, + "Most Nutritious", + "Eats food items with the most nutritional value." + ) + } + + enum class Reason(val message: (ItemStack) -> String) { + None({ "Waiting for food to eat..." }), + Hunger({ "Eating ${it.item.name.string} due to Hunger" }), + Damage({ "Eating ${it.item.name.string} due to Damage" }), + Fire({ "Eating ${it.item.name.string} due to Fire" }); + + fun shouldEat() = this != None + + fun shouldKeepEating(config: EatConfig, stack: ItemStack?) = runSafe { + if (stack == null || stack.isEmpty) return@runSafe false + when(this@Reason) { + Hunger -> when(config.saturated) { + Saturation.EatSmart -> stack.item.nutrition + player.hungerManager.foodLevel <= 20 + Saturation.EatUntilFull -> player.hungerManager.isNotFull + } + Damage -> !player.hasStatusEffect(StatusEffects.REGENERATION) + Fire -> !player.hasStatusEffect(StatusEffects.FIRE_RESISTANCE) + None -> false + } + } ?: false + + fun selector(config: EatConfig) = selectStack(sorter = config.selectionPriority.comparator) { + when(this@Reason) { + None -> any() + Hunger -> isOneOfItems(config.nutritiousFood) + Damage -> isOneOfItems(config.regenerationFood) + Fire -> isOneOfItems(config.resistanceFood) + } and if (config.ignoreBadFood) isNoneOfItems(config.badFood) else any() + } + } - enum class SelectionMode(override val displayName: String): NamedEnum { - Whitelist("Whitelist"), - Blacklist("Blacklist") + companion object { + fun SafeContext.reasonEating(config: EatConfig) = when { + config.eatOnHunger && player.hungerManager.foodLevel <= config.minFoodLevel -> Reason.Hunger + config.eatOnDamage && player.health <= config.minDamage && !player.hasStatusEffect(StatusEffects.REGENERATION) -> Reason.Damage + config.eatOnFire && player.isOnFire && !player.hasStatusEffect(StatusEffects.FIRE_RESISTANCE) -> Reason.Fire + else -> Reason.None + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/groups/EatSettings.kt b/src/main/kotlin/com/lambda/config/groups/EatSettings.kt index 0168d7f0e..f4981a86a 100644 --- a/src/main/kotlin/com/lambda/config/groups/EatSettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/EatSettings.kt @@ -18,8 +18,6 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.event.events.TickEvent -import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.util.NamedEnum import net.minecraft.item.Item import net.minecraft.item.Items @@ -29,14 +27,21 @@ class EatSettings( baseGroup: NamedEnum, vis: () -> Boolean = { true } ) : EatConfig { - val defaultWhitelist = listOf(Items.GOLDEN_CARROT) + val nutritiousFoodDefaults = listOf(Items.APPLE, Items.BAKED_POTATO, Items.BEEF, Items.BEETROOT, Items.BEETROOT_SOUP, Items.BREAD, Items.CARROT, Items.CHICKEN, Items.CHORUS_FRUIT, Items.COD, Items.COOKED_BEEF, Items.COOKED_CHICKEN, Items.COOKED_COD, Items.COOKED_MUTTON, Items.COOKED_PORKCHOP, Items.COOKED_RABBIT, Items.COOKED_SALMON, Items.COOKIE, Items.DRIED_KELP, Items.ENCHANTED_GOLDEN_APPLE, Items.GOLDEN_APPLE, Items.GOLDEN_CARROT, Items.HONEY_BOTTLE, Items.MELON_SLICE, Items.MUSHROOM_STEW, Items.MUTTON, Items.POISONOUS_POTATO, Items.PORKCHOP, Items.POTATO, Items.PUFFERFISH, Items.PUMPKIN_PIE, Items.RABBIT, Items.RABBIT_STEW, Items.ROTTEN_FLESH, Items.SALMON, Items.SPIDER_EYE, Items.SUSPICIOUS_STEW, Items.SWEET_BERRIES, Items.GLOW_BERRIES, Items.TROPICAL_FISH) + val resistanceFoodDefaults = listOf(Items.ENCHANTED_GOLDEN_APPLE) + val regenerationFoodDefaults = listOf(Items.ENCHANTED_GOLDEN_APPLE, Items.GOLDEN_APPLE) + val negativeFoodDefaults = listOf(Items.CHICKEN, Items.POISONOUS_POTATO, Items.PUFFERFISH, Items.ROTTEN_FLESH, Items.SPIDER_EYE) - override val eatFood by c.setting("Eat Food", true, "Whether food should be eaten", vis).group(baseGroup) - override val eatUntilFull by c.setting("Eat Until Full", false, "Eat until the food level is full") { vis() && eatFood }.group(baseGroup) - override val minFoodLevel by c.setting("Minimum Food Level", 6, 0..20, 1, "The minimum food level to eat food", " food level") { vis() && eatFood }.group(baseGroup) - override val eatOnFire by c.setting("Eat On Fire", false, "Eat when you are on fire") { vis() && eatFood }.group(baseGroup) - override val eatHeal by c.setting("Eat Heal", false, "Eat healing food when you are below the minimum health level") { vis() && eatFood }.group(baseGroup) - override val selectionMode by c.setting("Selection Mode", EatConfig.SelectionMode.Whitelist, "The selection mode for eating") { vis() && eatFood }.group(baseGroup) - override val whitelist by c.setting("Whitelist", defaultWhitelist, defaultWhitelist, "The whitelist of items to eat") { vis() && eatFood }.group(baseGroup) - override val blacklist by c.setting("Blacklist", listOf(), listOf(), "The blacklist of items to eat") { vis() && eatFood }.group(baseGroup) + override val eatOnHunger by c.setting("Eat On Hunger", true, "Whether to eat when hungry", vis).group(baseGroup) + override val minFoodLevel by c.setting("Minimum Food Level", 6, 0..20, 1, "The minimum food level to eat food", " food level") { vis() && eatOnHunger }.group(baseGroup) + override val saturated by c.setting("Saturated", EatConfig.Saturation.EatSmart, "When to stop eating") { vis() && eatOnHunger }.group(baseGroup) + override val nutritiousFood by c.setting("Nutritious Food", nutritiousFoodDefaults, nutritiousFoodDefaults, "Items that are be considered nutritious") { vis() && eatOnHunger }.group(baseGroup) + override val selectionPriority by c.setting("Selection Priority", EatConfig.SelectionPriority.MostNutritious, "The priority for selecting food items") { vis() && eatOnHunger }.group(baseGroup) + override val eatOnFire by c.setting("Eat On Fire", true, "Whether to eat when on fire", vis).group(baseGroup) + override val resistanceFood by c.setting("Resistance Food", resistanceFoodDefaults, resistanceFoodDefaults, "Items that give Fire Resistance") { vis() && eatOnFire}.group(baseGroup) + override val eatOnDamage by c.setting("Eat On Damage", true, "Whether to eat when damaged", vis).group(baseGroup) + override val minDamage by c.setting("Minimum Damage", 10, 0..20, 1, "The minimum damage threshold to trigger eating") { vis() && eatOnDamage }.group(baseGroup) + override val regenerationFood by c.setting("Regeneration Food", regenerationFoodDefaults, regenerationFoodDefaults, "Items that give Regeneration") { vis() && eatOnDamage }.group(baseGroup) + override val ignoreBadFood by c.setting("Ignore Bad Food", true, "Whether to eat when the food is bad", vis).group(baseGroup) + override val badFood by c.setting("Bad Food", negativeFoodDefaults, negativeFoodDefaults, "Items that are considered bad food") { vis() && ignoreBadFood }.group(baseGroup) } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index d2110728d..eb9a0f9aa 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -840,23 +840,23 @@ object BuildSimulator { ) { isTool() and if (breaking.suitableToolsOnly) { isSuitableForBreaking(state) - } else StackSelection.EVERYTHING and if (breaking.forceSilkTouch) { + } else any() and if (breaking.forceSilkTouch) { hasEnchantment(Enchantments.SILK_TOUCH) - } else StackSelection.EVERYTHING and if (breaking.forceFortunePickaxe) { + } else any() and if (breaking.forceFortunePickaxe) { hasEnchantment(Enchantments.FORTUNE) - } else StackSelection.EVERYTHING and if (!breaking.useWoodenTools) { + } else any() and if (!breaking.useWoodenTools) { hasTag(WOODEN_TOOL_MATERIALS).not() - } else StackSelection.EVERYTHING and if (!breaking.useStoneTools) { + } else any() and if (!breaking.useStoneTools) { hasTag(STONE_TOOL_MATERIALS).not() - } else StackSelection.EVERYTHING and if (!breaking.useIronTools) { + } else any() and if (!breaking.useIronTools) { hasTag(IRON_TOOL_MATERIALS).not() - } else StackSelection.EVERYTHING and if (!breaking.useDiamondTools) { + } else any() and if (!breaking.useDiamondTools) { hasTag(DIAMOND_TOOL_MATERIALS).not() - } else StackSelection.EVERYTHING and if (!breaking.useGoldTools) { + } else any() and if (!breaking.useGoldTools) { hasTag(GOLD_TOOL_MATERIALS).not() - } else StackSelection.EVERYTHING and if (!breaking.useNetheriteTools) { + } else any() and if (!breaking.useNetheriteTools) { hasTag(NETHERITE_TOOL_MATERIALS).not() - } else StackSelection.EVERYTHING + } else any() } val silentSwapSelection = selectContainer { diff --git a/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 188537a98..d8a3d957a 100644 --- a/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -128,6 +128,9 @@ class StackSelection { } } + fun any() = EVERYTHING + fun none() = NOTHING + /** * [isItem] returns a predicate that matches a specific [Item]. * @param item The [Item] to be matched. @@ -146,7 +149,7 @@ class StackSelection { */ fun isOneOfItems(items: Collection): (ItemStack) -> Boolean = { it.item in items } - fun isNoneOfItems(items: Collection): (ItemStack) -> Boolean = { it.item !in items } + fun isNoneOfItems(items: Collection): (ItemStack) -> Boolean = isOneOfItems(items).not() /** * Returns a predicate that checks if a given `ItemStack` exists within the provided collection of `ItemStack`s. @@ -154,7 +157,7 @@ class StackSelection { * @param stacks A collection of `ItemStack` instances to be checked against. * @return A predicate that evaluates to `true` if the given `ItemStack` is within the specified collection, otherwise `false`. */ - fun isOneOfStacks(stacks: Collection): (ItemStack) -> Boolean = { it in stacks } + fun isOneOfStacks(stacks: Collection): (ItemStack) -> Boolean = stacks::contains fun isSuitableForBreaking(blockState: BlockState): (ItemStack) -> Boolean = { it.isSuitableFor(blockState) } diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt index 6df756ac7..496b69f4f 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt @@ -17,30 +17,16 @@ package com.lambda.module.modules.player +import com.lambda.config.groups.EatConfig.Companion.reasonEating import com.lambda.config.groups.EatSettings -import com.lambda.config.groups.RotationSettings -import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent -import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.request.Request.Companion.submit -import com.lambda.interaction.request.rotating.Rotation -import com.lambda.interaction.request.rotating.Rotation.Companion.rotationTo -import com.lambda.interaction.request.rotating.Rotation.Companion.wrap -import com.lambda.interaction.request.rotating.RotationRequest -import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run import com.lambda.task.tasks.EatTask import com.lambda.task.tasks.EatTask.Companion.eat -import com.lambda.task.tasks.EatTask.Companion.shouldEat import com.lambda.util.NamedEnum -import com.lambda.util.math.distSq -import net.minecraft.entity.Entity -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.util.math.MathHelper.wrapDegrees -import kotlin.random.Random object AutoEat : Module( name = "AutoEat", @@ -49,17 +35,15 @@ object AutoEat : Module( ) { private enum class Group(override val displayName: String) : NamedEnum { FOOD("Food"), - FIRE("Fire"), - HEAL("Heal"), } private val eat = EatSettings(this, Group.FOOD) - private var eatTask: EatTask? = null init { listen { - if (eatTask != null || !shouldEat(eat)) return@listen + val reason = reasonEating(eat) + if (eatTask != null || !reason.shouldEat()) return@listen val task = eat(eat) task.finally { eatTask = null } diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 04700964b..32aa3cb63 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -21,6 +21,7 @@ import baritone.api.pathing.goals.GoalBlock import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.EatConfig +import com.lambda.config.groups.EatConfig.Companion.reasonEating import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent @@ -53,9 +54,6 @@ import com.lambda.interaction.request.rotating.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.task.tasks.EatTask.Companion.eat -import com.lambda.task.tasks.EatTask.Companion.hasFood -import com.lambda.task.tasks.EatTask.Companion.shouldEat -import com.lambda.util.Communication.info import com.lambda.util.Formatting.string import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots @@ -100,9 +98,9 @@ class BuildTask @Ta5kBuilder constructor( init { listen { - val parentEating = (parent as? BuildTask)?.eatTask != null +// val parentEating = (parent as? BuildTask)?.eatTask != null when { - !parentEating && eatTask == null && shouldEat(eat) && hasFood(eat, inventory) -> { + eatTask == null && reasonEating(eat).shouldEat() -> { eatTask = eat(eat) eatTask?.finally { eatTask = null diff --git a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt index c801c2acc..51678c7f2 100644 --- a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt @@ -18,8 +18,8 @@ package com.lambda.task.tasks import com.lambda.config.groups.EatConfig +import com.lambda.config.groups.EatConfig.Companion.reasonEating import com.lambda.context.SafeContext -import com.lambda.event.Event import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.material.StackSelection.Companion.selectStack @@ -28,35 +28,51 @@ import com.lambda.interaction.material.container.containers.MainHandContainer import com.lambda.interaction.request.inventory.InventoryConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task -import net.minecraft.component.DataComponentTypes -import net.minecraft.component.type.FoodComponent +import com.lambda.util.item.ItemUtils.nutrition import net.minecraft.item.ItemStack import net.minecraft.util.ActionResult import net.minecraft.util.Hand -import java.awt.event.InputEvent class EatTask @Ta5kBuilder constructor( - val config: EatConfig = TaskFlowModule.eat, - val inventory: InventoryConfig = TaskFlowModule.inventory, + val config: EatConfig, + val inventory: InventoryConfig ) : Task() { override val name: String - get() = "Eating ${eatStack ?: "nothing"}" + get() = reason.message(eatStack ?: ItemStack.EMPTY) private var eatStack: ItemStack? = null - private val selection get() = foodSelector(config) + private var reason = EatConfig.Reason.None + private var holdingUse = false + + override fun SafeContext.onStart() { + reason = reasonEating(config) + } init { listen { - val nutrition = eatStack?.item?.components?.get(DataComponentTypes.FOOD)?.nutrition ?: 0 - val wouldBeFull = nutrition + player.hungerManager.foodLevel > 20 - if (!config.eatUntilFull && !shouldEat(config) || wouldBeFull) { + if (holdingUse && !reason.shouldKeepEating(config, eatStack)) { + mc.options.useKey.isPressed = false + holdingUse = false interaction.stopUsingItem(player) success() return@listen } - if (!selection.matches(player.mainHandStack)) { - selection.transfer(MainHandContainer, inventory) + if (player.isUsingItem) { + if (!holdingUse) { + mc.options.useKey.isPressed = true + holdingUse = true + } + return@listen + } + + val foodFinder = reason.selector(config) + if (!foodFinder.matches(player.mainHandStack)) { + if (holdingUse) { + mc.options.useKey.isPressed = false + holdingUse = false + } + foodFinder.transfer(MainHandContainer, inventory) ?.execute(this@EatTask) ?: failure("No food found") return@listen } @@ -65,25 +81,17 @@ class EatTask @Ta5kBuilder constructor( (interaction.interactItem(player, Hand.MAIN_HAND) as? ActionResult.Success)?.let { if (it.swingSource == ActionResult.SwingSource.CLIENT) player.swingHand(Hand.MAIN_HAND) mc.gameRenderer.firstPersonRenderer.resetEquipProgress(Hand.MAIN_HAND) + mc.options.useKey.isPressed = true + holdingUse = true } } } companion object { - fun foodSelector(config: EatConfig) = selectStack { - when { - config.selectionMode == EatConfig.SelectionMode.Whitelist -> isOneOfItems(config.whitelist) - else -> isNoneOfItems(config.blacklist) - } and isFood() - } - @Ta5kBuilder - fun eat(config: EatConfig) = EatTask(config) - - fun SafeContext.shouldEat(config: EatConfig) = - player.hungerManager.foodLevel <= config.minFoodLevel - - fun hasFood(config: EatConfig, inventory: InventoryConfig) = - foodSelector(config).transfer(MainHandContainer, inventory) != null + fun eat( + config: EatConfig = TaskFlowModule.eat, + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = EatTask(config, inventory) } } diff --git a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index da21632f5..2364191f4 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -19,6 +19,7 @@ package com.lambda.util.item import net.minecraft.block.Block import net.minecraft.block.Blocks +import net.minecraft.component.DataComponentTypes import net.minecraft.item.Item import net.minecraft.item.Items @@ -121,6 +122,8 @@ object ItemUtils { val Item.block: Block get() = Block.getBlockFromItem(this) + val Item.nutrition: Int get() = components.get(DataComponentTypes.FOOD)?.nutrition ?: 0 + fun Int.toItemCount(): String { if (this < 0) { return "Invalid input" From 39167e948048a23c38969f9fed2a7392b2a3a25b Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Sep 2025 04:05:32 +0200 Subject: [PATCH 3/7] Life maintenance mode for building --- .../lambda/module/modules/player/AutoEat.kt | 9 +++-- .../module/modules/player/HighwayTools.kt | 1 + .../kotlin/com/lambda/task/tasks/BuildTask.kt | 37 +++++++++++-------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt index 496b69f4f..722947623 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoEat.kt @@ -19,6 +19,7 @@ package com.lambda.module.modules.player import com.lambda.config.groups.EatConfig.Companion.reasonEating import com.lambda.config.groups.EatSettings +import com.lambda.config.groups.InventorySettings import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.module.Module @@ -34,10 +35,12 @@ object AutoEat : Module( tag = ModuleTag.PLAYER, ) { private enum class Group(override val displayName: String) : NamedEnum { - FOOD("Food"), + Eating("Eating"), + Inventory("Inventory") } - private val eat = EatSettings(this, Group.FOOD) + private val eat = EatSettings(this, Group.Eating) + private val inventory = InventorySettings(this, Group.Inventory) private var eatTask: EatTask? = null init { @@ -45,7 +48,7 @@ object AutoEat : Module( val reason = reasonEating(eat) if (eatTask != null || !reason.shouldEat()) return@listen - val task = eat(eat) + val task = eat(eat, inventory) task.finally { eatTask = null } task.run() eatTask = task diff --git a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 79ad099ce..792a89785 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -148,6 +148,7 @@ object HighwayTools : Module( inventory = inventory, hotbar = hotbar, eat = eat, + lifeMaintenance = true, ).run() } diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 32aa3cb63..b33d738ea 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -63,16 +63,17 @@ import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos import java.util.concurrent.ConcurrentLinkedQueue -class BuildTask @Ta5kBuilder constructor( +class BuildTask private constructor( private val blueprint: Blueprint, - private val finishOnDone: Boolean = true, - private val collectDrops: Boolean = TaskFlowModule.build.collectDrops, - private val build: BuildConfig = TaskFlowModule.build, - private val rotation: RotationConfig = TaskFlowModule.rotation, - private val interactionConfig: InteractionConfig = TaskFlowModule.interaction, - private val inventory: InventoryConfig = TaskFlowModule.inventory, - private val hotbar: HotbarConfig = TaskFlowModule.hotbar, - private val eat: EatConfig = TaskFlowModule.eat, + private val finishOnDone: Boolean, + private val collectDrops: Boolean, + private val build: BuildConfig, + private val rotation: RotationConfig, + private val interactionConfig: InteractionConfig, + private val inventory: InventoryConfig, + private val hotbar: HotbarConfig, + private val eat: EatConfig, + private val lifeMaintenance: Boolean, ) : Task() { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" @@ -98,9 +99,8 @@ class BuildTask @Ta5kBuilder constructor( init { listen { -// val parentEating = (parent as? BuildTask)?.eatTask != null when { - eatTask == null && reasonEating(eat).shouldEat() -> { + lifeMaintenance && eatTask == null && reasonEating(eat).shouldEat() -> { eatTask = eat(eat) eatTask?.finally { eatTask = null @@ -255,8 +255,9 @@ class BuildTask @Ta5kBuilder constructor( inventory: InventoryConfig = TaskFlowModule.inventory, hotbar: HotbarConfig = TaskFlowModule.hotbar, eat: EatConfig = TaskFlowModule.eat, + lifeMaintenance: Boolean = false, blueprint: () -> Blueprint, - ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat) + ) = BuildTask(blueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat, lifeMaintenance) @Ta5kBuilder fun Structure.build( @@ -268,7 +269,8 @@ class BuildTask @Ta5kBuilder constructor( inventory: InventoryConfig = TaskFlowModule.inventory, hotbar: HotbarConfig = TaskFlowModule.hotbar, eat: EatConfig = TaskFlowModule.eat, - ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat) + lifeMaintenance: Boolean = false, + ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat, lifeMaintenance) @Ta5kBuilder fun Blueprint.build( @@ -280,7 +282,8 @@ class BuildTask @Ta5kBuilder constructor( inventory: InventoryConfig = TaskFlowModule.inventory, hotbar: HotbarConfig = TaskFlowModule.hotbar, eat: EatConfig = TaskFlowModule.eat, - ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat) + lifeMaintenance: Boolean = false, + ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat, lifeMaintenance) @Ta5kBuilder fun breakAndCollectBlock( @@ -293,9 +296,10 @@ class BuildTask @Ta5kBuilder constructor( inventory: InventoryConfig = TaskFlowModule.inventory, hotbar: HotbarConfig = TaskFlowModule.hotbar, eat: EatConfig = TaskFlowModule.eat, + lifeMaintenance: Boolean = false, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat + finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat, lifeMaintenance ) @Ta5kBuilder @@ -309,9 +313,10 @@ class BuildTask @Ta5kBuilder constructor( inventory: InventoryConfig = TaskFlowModule.inventory, hotbar: HotbarConfig = TaskFlowModule.hotbar, eat: EatConfig = TaskFlowModule.eat, + lifeMaintenance: Boolean = false, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat + finishOnDone, collectDrops, build, rotation, interact, inventory, hotbar, eat, lifeMaintenance ) } } From a28ea6d8683a2c3f1437819ea55b1afe06d7d0ea Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Sep 2025 04:28:02 +0200 Subject: [PATCH 4/7] Fix interaction with world border --- .../lambda/interaction/construction/simulation/BuildSimulator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index eb9a0f9aa..89aeec7c4 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -407,6 +407,7 @@ object BuildSimulator { val hitPos = if (!place.airPlace.isEnabled && (currentState.isAir || statePromoting)) pos.offset(neighbor) else pos val hitSide = neighbor.opposite + if (!world.worldBorder.contains(hitPos)) return@forEach val voxelShape = blockState(hitPos).getOutlineShape(world, hitPos).let { outlineShape -> if (!outlineShape.isEmpty || !place.airPlace.isEnabled) outlineShape From ea613edce0bee626ce5f99e33ad97908adbb3173 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Sep 2025 14:53:04 +0200 Subject: [PATCH 5/7] Better shulker placements and solve TaskFlow memory leak --- src/main/java/com/lambda/mixin/render/DebugHudMixin.java | 6 ------ src/main/kotlin/com/lambda/config/groups/EatConfig.kt | 8 +++++--- src/main/kotlin/com/lambda/task/Task.kt | 2 ++ src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt | 3 ++- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/lambda/mixin/render/DebugHudMixin.java b/src/main/java/com/lambda/mixin/render/DebugHudMixin.java index 46825717e..4b29a9e66 100644 --- a/src/main/java/com/lambda/mixin/render/DebugHudMixin.java +++ b/src/main/java/com/lambda/mixin/render/DebugHudMixin.java @@ -17,7 +17,6 @@ package com.lambda.mixin.render; -import com.lambda.task.RootTask; import com.lambda.util.DebugInfoHud; import net.minecraft.client.gui.hud.DebugHud; import org.spongepowered.asm.mixin.Mixin; @@ -33,9 +32,4 @@ public class DebugHudMixin { private void onGetRightText(CallbackInfoReturnable> cir) { DebugInfoHud.addDebugInfo(cir.getReturnValue()); } - - @Inject(method = "getLeftText", at = @At("TAIL")) - private void onGetLeftText(CallbackInfoReturnable> cir) { - cir.getReturnValue().addAll(List.of(RootTask.INSTANCE.toString().split("\n"))); - } } diff --git a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt index 590fed795..72e0fd336 100644 --- a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt +++ b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt @@ -18,7 +18,6 @@ package com.lambda.config.groups import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.threading.runSafe import com.lambda.util.Describable @@ -32,13 +31,16 @@ interface EatConfig { val eatOnHunger: Boolean val minFoodLevel: Int val nutritiousFood: List - val selectionPriority: SelectionPriority val saturated: Saturation + val eatOnFire: Boolean val resistanceFood: List + val eatOnDamage: Boolean val minDamage: Int val regenerationFood: List + + val selectionPriority: SelectionPriority val ignoreBadFood: Boolean val badFood: List @@ -67,7 +69,7 @@ interface EatConfig { } enum class Reason(val message: (ItemStack) -> String) { - None({ "Waiting for food to eat..." }), + None({ "Waiting for reason to eat..." }), Hunger({ "Eating ${it.item.name.string} due to Hunger" }), Damage({ "Eating ${it.item.name.string} due to Damage" }), Fire({ "Eating ${it.item.name.string} due to Fire" }); diff --git a/src/main/kotlin/com/lambda/task/Task.kt b/src/main/kotlin/com/lambda/task/Task.kt index b86240929..dc2497a3f 100644 --- a/src/main/kotlin/com/lambda/task/Task.kt +++ b/src/main/kotlin/com/lambda/task/Task.kt @@ -23,6 +23,7 @@ import com.lambda.event.EventFlow.unsubscribe import com.lambda.event.Muteable import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.Communication.logError import com.lambda.util.Nameable @@ -126,6 +127,7 @@ abstract class Task : Nameable, Muteable { fun success(result: Result) { unsubscribe() state = State.COMPLETED + if (!TaskFlowModule.showAllEntries) parent?.subTasks?.remove(this) runSafe { executeNextTask(result) } diff --git a/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index 25e5969f0..9d06ba957 100644 --- a/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -36,6 +36,7 @@ import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState import com.lambda.util.item.ItemUtils.shulkerBoxes +import com.lambda.util.math.distSq import net.minecraft.block.ChestBlock import net.minecraft.entity.mob.ShulkerEntity import net.minecraft.item.ItemStack @@ -72,7 +73,7 @@ class PlaceContainer @Ta5kBuilder constructor( 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.blockPos.y == player.blockPos.y - }.minOrNull()?.blockPos ?: run { + }.minByOrNull { it.blockPos distSq player.pos }?.blockPos ?: run { failure("Couldn't find a valid container placement position for ${startStack.name.string}") return@onStart } From 787fe986361f6863f4499ac7a9bb0aa131bb1701 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 21 Sep 2025 07:16:12 +0200 Subject: [PATCH 6/7] Split wall filling into both sides --- .../com/lambda/config/groups/EatConfig.kt | 3 ++- .../module/modules/player/HighwayTools.kt | 22 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt index 72e0fd336..6088fda4d 100644 --- a/src/main/kotlin/com/lambda/config/groups/EatConfig.kt +++ b/src/main/kotlin/com/lambda/config/groups/EatConfig.kt @@ -55,7 +55,8 @@ interface EatConfig { enum class SelectionPriority( val comparator: Comparator, override val displayName: String, - override val description: String): NamedEnum, Describable { + override val description: String + ): NamedEnum, Describable { LeastNutritious( compareBy { it.item.nutrition }, "Least Nutritious", diff --git a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 792a89785..920a1192c 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -61,8 +61,9 @@ object HighwayTools : Module( private val pavementMaterial by setting("Pavement Material", Blocks.OBSIDIAN, "Material to build the highway with") { pavement == Material.Block }.group(Group.Structure) private val floor by setting("Floor", Material.None, "Material for the floor").group(Group.Structure) private val floorMaterial by setting("Floor Material", Blocks.NETHERRACK, "Material to build the floor with") { floor == Material.Block }.group(Group.Structure) - private val walls by setting("Walls", Material.None, "Material for the walls").group(Group.Structure) - private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, "Material to build the walls with") { walls == Material.Block }.group(Group.Structure) + private val rightWall by setting("Right Wall", Material.None, "Build the right wall").group(Group.Structure) + private val leftWall by setting("Left Wall", Material.None, "Build the left wall").group(Group.Structure) + private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, "Material to build the walls with") { rightWall == Material.Block }.group(Group.Structure) private val ceiling by setting("Ceiling", Material.None, "Material for the ceiling").group(Group.Structure) private val ceilingMaterial by setting("Ceiling Material", Blocks.OBSIDIAN, "Material to build the ceiling with") { ceiling == Material.Block }.group(Group.Structure) private val distance by setting("Distance", -1, -1..1000000, 1, "Distance to build the highway/tunnel (negative for infinite)").group(Group.Structure) @@ -224,26 +225,25 @@ object HighwayTools : Module( ).associateWith { target(ceiling, ceilingMaterial) } } - if (walls != Material.None) { - val wallElevation = rimHeight + if (pavement != Material.None) 1 else 0 - - // Left wall + val wallElevation = if (pavement != Material.None) rimHeight else 0 + if (pavement != Material.None) 1 else 0 + if (rightWall != Material.None) { structure += generateDirectionalTube( orthogonal, 1, height - wallElevation, - -center + width, + -center - 1, wallElevation, - ).associateWith { target(walls, wallMaterial) } + ).associateWith { target(rightWall, wallMaterial) } + } - // Right wall + if (leftWall != Material.None) { structure += generateDirectionalTube( orthogonal, 1, height - wallElevation, - -center - 1, + -center + width, wallElevation, - ).associateWith { target(walls, wallMaterial) } + ).associateWith { target(rightWall, wallMaterial) } } if (floor != Material.None) { From 39c04ec5dc28b83184f048de0054d65c8960d6c2 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 21 Sep 2025 08:28:28 +0200 Subject: [PATCH 7/7] Fix left and right issue --- .../com/lambda/module/modules/player/HighwayTools.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 920a1192c..38f3cf27c 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -63,7 +63,7 @@ object HighwayTools : Module( private val floorMaterial by setting("Floor Material", Blocks.NETHERRACK, "Material to build the floor with") { floor == Material.Block }.group(Group.Structure) private val rightWall by setting("Right Wall", Material.None, "Build the right wall").group(Group.Structure) private val leftWall by setting("Left Wall", Material.None, "Build the left wall").group(Group.Structure) - private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, "Material to build the walls with") { rightWall == Material.Block }.group(Group.Structure) + private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, "Material to build the walls with") { rightWall == Material.Block || leftWall == Material.Block }.group(Group.Structure) private val ceiling by setting("Ceiling", Material.None, "Material for the ceiling").group(Group.Structure) private val ceilingMaterial by setting("Ceiling Material", Blocks.OBSIDIAN, "Material to build the ceiling with") { ceiling == Material.Block }.group(Group.Structure) private val distance by setting("Distance", -1, -1..1000000, 1, "Distance to build the highway/tunnel (negative for infinite)").group(Group.Structure) @@ -231,7 +231,7 @@ object HighwayTools : Module( orthogonal, 1, height - wallElevation, - -center - 1, + -center + width, wallElevation, ).associateWith { target(rightWall, wallMaterial) } } @@ -241,9 +241,9 @@ object HighwayTools : Module( orthogonal, 1, height - wallElevation, - -center + width, + -center - 1, wallElevation, - ).associateWith { target(rightWall, wallMaterial) } + ).associateWith { target(leftWall, wallMaterial) } } if (floor != Material.None) {